Linux 内核系统结构
内核系统结构图
操作系统内核是一个庞大的系统工程,越大越复杂的系统就越需要架构设计,将复杂的大系统进行模块化分解,从而将大的复杂问题,变成若干相对小的简单问题。以子系统维度看,它的系统结构图,如下所示:
内核作用
内核把计算机资源管理起来,保护起来,然后通过系统调用这个服务窗口将这些资源分配给用户程序使用,对底层硬件和用户程序起到隔离和协调的作用,提升资源使用效率和程序开发者的开发效率。
系统调用
如上所述,要使用计算机资源,就必须知道怎么使用系统调用服务。下面是一些常见的系统调用。
进程管理
fork
创建进程使用fork。当父进程调用 fork 创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。
对于 fork 系统调用的返回值,如果当前进程是子进程,就返回 0;如果当前进程是父进程,就返回子进程的进程号。这样首先在返回值这里就有了一个区分,然后通过 if-else 语句判断,如果是父进程,还接着做原来应该做的事情;如果是子进程,需要请求另一个系统调用execve来执行另一个程序,这个时候,子进程和父进程就彻底分道扬镳了,也就产生了一个分支(fork)了。
execve
运行一个程序用execve。
waitpid
父进程可以调用它,将子进程的进程号作为参数传给它,这样父进程就知道子进程运行完了没有,成功与否。
内存管理
每个进程都有自己独立的内存空间,比如:代码段、数据段、堆、栈等。
brk/mmap
这里介绍两个在堆里面分配内存的系统调用,brk和mmap。当分配的内存数量比较小的时候。当分配的内存数量比较大的时候,使用mmap,会重新划分一块区域。
文件管理
Linux 里有一个特点,那就是一切皆文件,包括二进制文件、文本文件、标准输入输出文件、管道、socket、设备、文件夹等都是文件。每个文件,Linux 都会分配一个文件描述符(File Descriptor),这是一个整数。有了文件描述符,我们就可以使用系统调用,查看或者干预进程运行的方方面面。“一切皆文件”的优势:统一了操作的入口,提供了极大的便利。
对于文件的操作,下面这六个系统调用是最重要的:
open
打开文件
close
关闭文件
creat
创建文件
lseek
打开文件以后,可以使用lseek跳到文件的某个位置
read/write
文件读写
信号处理
kill/SIGKILL/SIGSTOP
发送信号,终止进程
sigaction
注册一个信号处理函数
sem_wait/sem_post
抢占/释放信号量
进程间通信
msgget/msgsnd/msgrcv
通过msgget创建一个新的消息队列(在系统内核中),msgsnd将消息发送到消息队列,而消息接收方可以使用msgrcv从队列中取消息。
shmget/shmat
通过shmget创建一个共享内存块,通过shmat将共享内存映射到自己的内存空间,然后就可以读写了。共享内存存在“竞争”问题,通过信号量的机制 Semaphore解决。
网络通信
socket/bind/connect/accept/listen
我们可以通过 Socket 系统调用建立一个 Socket。Socket 也是一个文件,也有一个文件描述符,也可以通过读写函数进行通信。
Glibc
Glibc 为程序员提供丰富的 API,除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。
每个特定的系统调用对应了至少一个 Glibc 封装的库函数,比如说,系统提供的打开文件系统调用 sys_open 对应的是 Glibc 中的 open 函数。
有时候,Glibc 一个单独的 API 可能调用多个系统调用,比如说,Glibc 提供的 printf 函数就会调用如 sys_open、sys_mmap、sys_write、sys_close 等等系统调用。
也有时候,多个 API 也可能只对应同一个系统调用,如 Glibc 下实现的 malloc、calloc、free 等函数用来分配和释放内存,都利用了内核的 sys_brk 的系统调用。
版权声明: 本文为 InfoQ 作者【林昱榕】的原创文章。
原文链接:【http://xie.infoq.cn/article/03fe5c54b38aab9b09eb6dc5b】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论