Dal*_*sen 4 c operating-system libraries c-libraries raspberry-pi
我正在尝试为Raspberry Pi创建一个操作系统(没有什么大的,只是为了好玩),尽管我可以在Assembly中写出所有内容,但这比在C中编写它要困难得多.我想知道是否(以及为什么如果我不能,我可以将C库(文件)包含在操作系统中,这样我就不必重写它们了.它不会起作用,因为库本身是用C语言编写的吗?
不,您必须将C库移植到您的操作系统,因为该库具有挂钩到操作系统细节的"存根".C标准要求某些标题以独立模式存在,您始终可以使用它.但像printf这样的库函数必须自己实现或通过填充存根来移植.看看newlib,看看你要做的工作.它至少需要一个具有syscall接口的工作内核(用于读取,写入等).这些将取决于操作系统中可用的功能(例如,文件系统.)取自FAQ:
- 将newlib移植到新平台需要执行哪些步骤?
基本端口需要更改许多文件并添加一些目录.
将子目录添加到您的平台的newlib/libc/machine目录
在此目录中,您需要具有setjmp/longjmp实现.这是必需的,因为setjmp/longjmp通常是汇编程序.查看libc/machine/fr30目录并复制/修改其中的文件.
编辑newlib/libc/include/machine/ieeefp.h
这定义了平台的ieee字节顺序.编译器应该定义标识您的机器的东西.在某些情况下,字节序可能是编译器选项,因此除了您的平台标识符之外,您可能还必须检查另一个定义.请参阅文件中的示例.
编辑newlib/libc/include/machine/setjmp.h
您需要指定setjmp缓冲区特性以与setjmp/longjmp实现相匹配.这只是setjmp缓冲区的大小.有关示例,请参阅文件
编辑newlib/libc/include/sys/config.h
根据需要,这有各种定义.大多数情况下,它定义了一些最大值.有些默认值可能适用于您的平台,在这种情况下您无需执行任何操作.
编辑configure.host
您需要添加配置,以便newlib可以识别它.您应该通过machine_dir变量为您的平台指定新的计算机目录.如果需要,您可以添加特殊的newlib编译标志.sys_dir用于OS的东西,所以你不需要改变它.较旧的平台使用sys_dir来实现系统调用,但这不正确并且是历史上的麻烦.syscall_dir是一个选择,但我建议默认指定syscall_dir = syscalls.阅读newlib/libc/include/reent.h中的注释以获取选择的解释.
将平台子目录添加到libgloss
您需要为您的平台添加bsp.这是newlib和所需的任何链接描述文件所需的最小系统调用.这在板与板之间有所不同(它也可以是模拟器).有关示例,请参阅mn10300或fr30.您需要编辑configure.in并重新生成configure,以便构建新文件.默认情况下,您会获得libnosys,它会为您提供一组默认的系统调用存根.大多数存根只是返回失败.您仍然需要提供__exit例程.这可以像生成异常以停止程序一样简单.
可能会覆盖头文件
如果需要覆盖任何默认机器头文件,可以将机器目录添加到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
关闭文件.最小的实施:
Run Code Online (Sandbox Code Playgroud)int close(int file) { return -1; }
environ
指向环境变量及其值的列表的指针.对于最小的环境,这个空列表就足够了:
Run Code Online (Sandbox Code Playgroud)char *__env[1] = { 0 }; char **environ = __env;
execve
将控制权转移到新流程.最小的实现(对于没有进程的系统):
Run Code Online (Sandbox Code Playgroud)#include <errno.h> #undef errno extern int errno; int execve(char *name, char **argv, char **env) { errno = ENOMEM; return -1; }
fork
创建一个新流程.最小的实现(对于没有进程的系统):
Run Code Online (Sandbox Code Playgroud)#include <errno.h> #undef errno extern int errno; int fork(void) { errno = EAGAIN; return -1; }
fstat
打开文件的状态.为了与这些示例中的其他最小实现保持一致,所有文件都被视为字符特殊设备.所需的sys/stat.h头文件分发在此C库的include子目录中.
Run Code Online (Sandbox Code Playgroud)#include <sys/stat.h> int fstat(int file, struct stat *st) { st->st_mode = S_IFCHR; return 0; }
getpid
进程ID; 这有时用于生成不太可能与其他进程冲突的字符串.对于没有进程的系统,实现最少:
Run Code Online (Sandbox Code Playgroud)int getpid(void) { return 1; }
isatty
查询输出流是否为终端.为了与仅支持输出到stdout的其他最小实现保持一致,建议使用以下最小实现:
Run Code Online (Sandbox Code Playgroud)int isatty(int file) { return 1; }
kill
发送信号.最小的实施:
Run Code Online (Sandbox Code Playgroud)#include <errno.h> #undef errno extern int errno; int kill(int pid, int sig) { errno = EINVAL; return -1; }
link
为现有文件建立新名称.最小的实施:
Run Code Online (Sandbox Code Playgroud)#include <errno.h> #undef errno extern int errno; int link(char *old, char *new) { errno = EMLINK; return -1; }
lseek
在文件中设置位置.最小的实施:
Run Code Online (Sandbox Code Playgroud)int lseek(int file, int ptr, int dir) { return 0; }
open
打开一个文件.最小的实施:
Run Code Online (Sandbox Code Playgroud)int open(const char *name, int flags, int mode) { return -1; }
read
从文件中读取.最小的实施:
Run Code Online (Sandbox Code Playgroud)int read(int file, char *ptr, int len) { return 0; }
sbrk
增加程序数据空间.由于malloc和相关函数依赖于此,因此有一个有效的实现是很有用的.以下是独立系统的满足要求; 它利用了GNU链接器自动定义的符号_end.
Run Code Online (Sandbox Code Playgroud)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; }
stat
文件的状态(按名称).最小的实施:
Run Code Online (Sandbox Code Playgroud)int stat(char *file, struct stat *st) { st->st_mode = S_IFCHR; return 0; }
times
当前流程的时间信息.最小的实施:
Run Code Online (Sandbox Code Playgroud)int times(struct tms *buf) { return -1; }
unlink
删除文件的目录条目.最小的实施:
Run Code Online (Sandbox Code Playgroud)#include <errno.h> #undef errno extern int errno; int unlink(char *name) { errno = ENOENT; return -1; }
wait
等待子进程.最小的实施:
Run Code Online (Sandbox Code Playgroud)#include <errno.h> #undef errno extern int errno; int wait(int *status) { errno = ECHILD; return -1; }
write
写入文件.libc子例程将使用此系统例程输出到所有文件,包括stdout-so如果您需要生成任何输出,例如到串口进行调试,则应该使您的最小写入能够执行此操作.以下最小实现是一个不完整的例子; 它依赖于outbyte子例程(未显示;通常,您必须在硬件制造商提供的示例中以汇编程序编写)以实际执行输出.
Run Code Online (Sandbox Code Playgroud)int write(int file, char *ptr, int len) { int todo; for (todo = 0; todo < len; todo++) { outbyte (*ptr++); } return len; }
有关端口newlib所需步骤的更全面概述,请参阅osdev.org.虽然我建议首先阅读网站上与编写内核有关的其他教程,因为移植C库绝对不是编写内核时的第一步.
归档时间: |
|
查看次数: |
201 次 |
最近记录: |