我可以在编程操作系统时包含C库(因为它们是用C语言编写的)

Dal*_*sen 4 c operating-system libraries c-libraries raspberry-pi

我正在尝试为Raspberry Pi创建一个操作系统(没有什么大的,只是为了好玩),尽管我可以在Assembly中写出所有内容,但这比在C中编写它要困难得多.我想知道是否(以及为什么如果我不能,我可以将C库(文件)包含在操作系统中,这样我就不必重写它们了.它不会起作用,因为库本身是用C语言编写的吗?

use*_*466 7

不,您必须将C库移植到您的操作系统,因为该库具有挂钩到操作系统细节的"存根".C标准要求某些标题以独立模式存在,您始终可以使用它.但像printf这样的库函数必须自己实现或通过填充存根来移植.看看newlib,看看你要做的工作.它至少需要一个具有syscall接口的工作内核(用于读取,写入等).这些将取决于操作系统中可用的功能(例如,文件系统.)取自FAQ:

  1. 将newlib移植到新平台需要执行哪些步骤?

基本端口需要更改许多文件并添加一些目录.

  1. 将子目录添加到您的平台的newlib/libc/machine目录

    在此目录中,您需要具有setjmp/longjmp实现.这是必需的,因为setjmp/longjmp通常是汇编程序.查看libc/machine/fr30目录并复制/修改其中的文件.

  2. 编辑newlib/libc/include/machine/ieeefp.h

    这定义了平台的ieee字节顺序.编译器应该定义标识您的机器的东西.在某些情况下,字节序可能是编译器选项,因此除了您的平台标识符之外,您可能还必须检查另一个定义.请参阅文件中的示例.

  3. 编辑newlib/libc/include/machine/setjmp.h

    您需要指定setjmp缓冲区特性以与setjmp/longjmp实现相匹配.这只是setjmp缓冲区的大小.有关示例,请参阅文件

  4. 编辑newlib/libc/include/sys/config.h

    根据需要,这有各种定义.大多数情况下,它定义了一些最大值.有些默认值可能适用于您的平台,在这种情况下您无需执行任何操作.

  5. 编辑configure.host

    您需要添加配置,以便newlib可以识别它.您应该通过machine_dir变量为您的平台指定新的计算机目录.如果需要,您可以添加特殊的newlib编译标志.sys_dir用于OS的东西,所以你不需要改变它.较旧的平台使用sys_dir来实现系统调用,但这不正确并且是历史上的麻烦.syscall_dir是一个选择,但我建议默认指定syscall_dir = syscalls.阅读newlib/libc/include/reent.h中的注释以获取选择的解释.

  6. 将平台子目录添加到libgloss

    您需要为您的平台添加bsp.这是newlib和所需的任何链接描述文件所需的最小系统调用.这在板与板之间有所不同(它也可以是模拟器).有关示例,请参阅mn10300或fr30.您需要编辑configure.in并重新生成configure,以便构建新文件.默认情况下,您会获得libnosys,它会为您提供一组默认的系统调用存根.大多数存根只是返回失败.您仍然需要提供__exit例程.这可以像生成异常以停止程序一样简单.

  7. 可能会覆盖头文件

    如果需要覆盖任何默认机器头文件,可以将机器目录添加到newlib/libc/machine /该子目录中的Header文件将覆盖newlib/libc/include/machine中的缺省值.您可能不需要这样做.

这假设您已经处理过将新配置添加到顶级目录文件.

现在linux是一个不同的动物.它是一个具有广泛系统调用的操作系统.如果查看newlib/libc/sys/linux目录,可以在那里找到许多系统调用(例如,参见io.c).有一组为特定平台定义的基本系统调用宏.对于x86,您将在newlib/libc/sys/linux/machine/i386/syscall.h文件中找到这些宏.目前,linux支持仅适用于x86.要添加另一个平台,必须为新平台提供syscall.h文件,还需要移植一些其他特定于平台的文件.

对于newlib,请查看syscall文档页面,其中列出了您需要实现的内容以及最小实现所包含的内容.sbrk如果你还没有实现内存管理,你会很快意识到这些东西会变得毫无意义.在移植C库时,您可能最终编写了大部分内核.

_exit

退出程序而不清理文件.如果您的系统不提供此功能,最好避免与需要它的子程序(退出,系统)链接.

close

关闭文件.最小的实施:

          int close(int file) {
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

environ

指向环境变量及其值的列表的指针.对于最小的环境,这个空列表就足够了:

          char *__env[1] = { 0 };
          char **environ = __env;
Run Code Online (Sandbox Code Playgroud)

execve

将控制权转移到新流程.最小的实现(对于没有进程的系统):

          #include <errno.h>
          #undef errno
          extern int errno;
          int execve(char *name, char **argv, char **env) {
            errno = ENOMEM;
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

fork

创建一个新流程.最小的实现(对于没有进程的系统):

          #include <errno.h>
          #undef errno
          extern int errno;
          int fork(void) {
            errno = EAGAIN;
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

fstat

打开文件的状态.为了与这些示例中的其他最小实现保持一致,所有文件都被视为字符特殊设备.所需的sys/stat.h头文件分发在此C库的include子目录中.

          #include <sys/stat.h>
          int fstat(int file, struct stat *st) {
            st->st_mode = S_IFCHR;
            return 0;
          }
Run Code Online (Sandbox Code Playgroud)

getpid

进程ID; 这有时用于生成不太可能与其他进程冲突的字符串.对于没有进程的系统,实现最少:

          int getpid(void) {
            return 1;
          }
Run Code Online (Sandbox Code Playgroud)

isatty

查询输出流是否为终端.为了与仅支持输出到stdout的其他最小实现保持一致,建议使用以下最小实现:

          int isatty(int file) {
            return 1;
          }
Run Code Online (Sandbox Code Playgroud)

kill

发送信号.最小的实施:

          #include <errno.h>
          #undef errno
          extern int errno;
          int kill(int pid, int sig) {
            errno = EINVAL;
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

link

为现有文件建立新名称.最小的实施:

          #include <errno.h>
          #undef errno
          extern int errno;
          int link(char *old, char *new) {
            errno = EMLINK;
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

lseek

在文件中设置位置.最小的实施:

          int lseek(int file, int ptr, int dir) {
            return 0;
          }
Run Code Online (Sandbox Code Playgroud)

open

打开一个文件.最小的实施:

          int open(const char *name, int flags, int mode) {
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

read

从文件中读取.最小的实施:

          int read(int file, char *ptr, int len) {
            return 0;
          }
Run Code Online (Sandbox Code Playgroud)

sbrk

增加程序数据空间.由于malloc和相关函数依赖于此,因此有一个有效的实现是很有用的.以下是独立系统的满足要求; 它利用了GNU链接器自动定义的符号_end.

          caddr_t sbrk(int incr) {
            extern char _end;     /* Defined by the linker */
            static char *heap_end;
            char *prev_heap_end;

            if (heap_end == 0) {
              heap_end = &_end;
            }
            prev_heap_end = heap_end;
            if (heap_end + incr > stack_ptr) {
              write (1, "Heap and stack collision\n", 25);
              abort ();
            }

            heap_end += incr;
            return (caddr_t) prev_heap_end;
          }
Run Code Online (Sandbox Code Playgroud)

stat

文件的状态(按名称).最小的实施:

          int stat(char *file, struct stat *st) {
            st->st_mode = S_IFCHR;
            return 0;
          }
Run Code Online (Sandbox Code Playgroud)

times

当前流程的时间信息.最小的实施:

          int times(struct tms *buf) {
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

unlink

删除文件的目录条目.最小的实施:

          #include <errno.h>
          #undef errno
          extern int errno;
          int unlink(char *name) {
            errno = ENOENT;
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

wait

等待子进程.最小的实施:

          #include <errno.h>
          #undef errno
          extern int errno;
          int wait(int *status) {
            errno = ECHILD;
            return -1;
          }
Run Code Online (Sandbox Code Playgroud)

write

写入文件.libc子例程将使用此系统例程输出到所有文件,包括stdout-so如果您需要生成任何输出,例如到串口进行调试,则应该使您的最小写入能够执行此操作.以下最小实现是一个不完整的例子; 它依赖于outbyte子例程(未显示;通常,您必须在硬件制造商提供的示例中以汇编程序编写)以实际执行输出.

          int write(int file, char *ptr, int len) {
            int todo;

            for (todo = 0; todo < len; todo++) {
              outbyte (*ptr++);
            }
            return len;
          }
Run Code Online (Sandbox Code Playgroud)

有关端口newlib所需步骤的更全面概述,请参阅osdev.org.虽然我建议首先阅读网站上与编写内核有关的其他教程,因为移植C库绝对不是编写内核时的第一步.