About Blog Contact

为什么Linux使用系统调用还需要下载内核?

知乎问题:Linux内核为什么不在安装好的操作系统中,而需要安装好系统后再下载?

编译好的Linux内核就存在文件系统中。比如说我的Ubuntu上就存在/boot/vmlinuz-5.3.0-23-generic,这个文件在开机时就由GRUB给加载到内存中去执行(其实是个自解压文件,可以提升加载速度)。

不知道你说的“新的系统调用”是什么意思,是想改内核新加一个系统调用,还是使用一个系统调用。改内核的话肯定要下载了:编译好的内核是二进制文件,代码得额外去下载。使用的话则不一样。

回顾一下系统调用的执行过程:对x86_64,程序在用户态使用syscall指令,rax里面放系统调用号。这个过程一般被库给包装过,用户程序使用库函数即可。Linux上最常用的库是glibc,也称libc。这样,对用户而言,系统调用跟调用别的函数一般没有区别。

这样,使用系统调用的话,需要有系统的C库和有关头文件。比如这个使用write系统调用的程序write.c

#include <unistd.h>
char* buf = "Hello world!\n";
int main() {
    write(1, buf, 13); // 去/usr/include/x86_64-linux-gnu/asm/unistd_64.h里面找
    return 0;
}

编译后运行:

$ gcc write.c -o write
$ ./write
Hello world!

unistd.h/usr/include/unistd.h,是许多系统调用和glibc函数的头文件。编译时要和/lib/x86_64-linux-gnu/libc.so.6链接,write的实现就在里面。 libc.so库是几乎百分之百的程序都要用的,几乎所有的非嵌入式系统都有。不过这个/usr/include/unistd.h可不一定系统都有。看看是哪个包的:

$ dpkg -S /usr/include/unistd.h
libc6-dev:amd64: /usr/include/unistd.h

看看这个包的信息(删去了几行):

$ apt --info libc6-dev
Package: libc6-dev
Version: 2.30-0ubuntu2
Priority: optional
Build-Essential: yes
Provides: libc-dev
Depends: libc6 (= 2.30-0ubuntu2), libc-dev-bin (= 2.30-0ubuntu2), linux-libc-dev
APT-Manual-Installed: no
Description: GNU C Library: Development Libraries and Header Files
 Contains the symlinks, headers, and object files needed to compile
 and link programs which use the standard C library.

Priority: optional,不一定非要有哦。

总结一下,使用系统调用不光需要内核、glibc,还需要头文件。头文件可能系统没自带,需要下载。不过我还没遇到过需要额外下载的情况,不知道是不是Ubuntu默认装了。

PS:如果你善于汇编,那手动调用syscall当然可以。刚才的例子重写:

.section .data
    msg .ascii "Hello world!\n"
.section .text
.global _start
_start:
    movq $1, %rax   ; write 系统调用; 
    movq $1, %rdi   ; 1是stdout。
    movq $msg, %rsi ; 输出msg
    movq $13, %rdx  ; 13个字符
    syscall         
    movq $60, %rax  ; 使用 _exit syscall。
    movq $0, %rdi   ; 返回值 0
    syscall         ; 

汇编:

$ as -mmnemonic=intel hello.s -o hello.o
$ ld hello.o -o hello
$./hello
Hello world!

参考资料: