https://time.geekbang.org/column/article/89251
进程管理
创建进程的系统调用叫fork。
在Linux中,要创建进程,需要一个老的进程调用fork来实现。其中,老的进程叫作父进程(Parent Process),新的进程叫作子进程(Child Process)。
等待子进程结束的系统调用叫waitpid,父进程可以调用它,将子进程的进程号作为参数传给它,这样父进程就知道子进程运行完了没有,成功与否。
创建进程的总结:
1、Linux中父进程调用fork创建子进程。
2、父进程调用fork时,子进程拷贝所有父进程的数据接口和代码过来。
3、当前进程是子进程,fork返回0;当前进程是父进程,fork返回子进程进程号
4、如果返回0,说明当前进程是子进程,子进程请求execve系统调用,执行另一个程序。
5、如果返回子进程号,说明当前进程是父进程,按照原父进程原计划执行。
6、父进程要对子进程负责,调用waitpid将子进程进程号作为参数,父进程就能知道子进程运行完了没有,成功与否。
7、操作系统启动的时候先创建了一个所有用户进程的“祖宗进程”
内存管理
代码段:放代码的空间
数据段:执行过程中产生的数据的空间
堆里面分配内存的系统调用:brk和mmap。brk:分配的内存数量比较小的时候使用,会和原来的堆的数据连在一起mmap:分配的内存数量比较大的时候使用,重新划分一块区域。
文件管理
对于文件操作,常见的六个系统调用:
open:打开文件close:关闭文件creat:创建文件lseek:跳到文件的某个位置read:读文件write:写文件
文件描述符:对于每个文件,Linux都会分配一个文件描述符(File Descriptor),本质上是一个整数。Linux一切皆文件,有了文件描述符就可以使用系统调用,查看或者干预进程运行的方方面面。
信号处理
经常遇到的信号有以下几种:
- 在执行一个程序的时候,在键盘输入
CTRL + C,这就是中断的信号,正在执行的命令就会中止退出; - 非法访问内存
- 硬件故障
- 用户进程通过
kill函数,将一个用户信号发送给另一个进程。
每种信号都定义了默认的动作,例如硬件故障,默认终止;
也可以提供信号处理函数,可以通过sigaction系统调用,注册一个信号处理函数。
进程间通信
进程间通信的方式有很多种。
首先就是发个消息,不需要一段很长的数据,这种方式称为消息队列(Message Queue)。这个消息队列是在内核里的,可以通过msgget创建一个新的队列,msgsnd将消息发送到消息队列,而消息接受方可以使用msgrcv从队列中取消息。
当需要交互的信息比较大时,可以使用共享内存的方式。通过shmget创建一个共享内存块,通过shmat将共享内存映射到自己的内存空间,然后就可以读写了。这个时候,会带来另外一个问题——资源竞争。比如说大家同时修改同一块数据怎么办?需要有一种方式,让不同的人能够排他地访问,这就是信号量的机制Semaphore。
一个简单的场景:对于只允许一个人访问的需求,我们可以将信号量设为 1。当一个人要访问的时候,先调用sem_wait。如果这时候没有人访问,则占用这个信号量,他就可以开始访问了。如果这个时候另一个人要访问,也会调用 sem_wait。由于前一个人已经在访问了,所以后面这个人就必须等待上一个人访问完之后才能访问。当上一个人访问完毕后,会调用sem_post将信号量释放,于是下一个人等待结束,可以访问这个资源了。
网络通信
不同机器要想通过网络相互通信,要遵循相同的网络协议,即TCP/IP网络协议栈。
网络协议栈以服务的形式提供给上层应用使用。
网络服务是通过套接字Socket来提供服务的。
平常开发时,并没有直接使用系统调用,而是使用中介Glibc。Glibc是Linux下使用的开源的标准C库,它是GNU发布的libc库。Glibc为程序员提供了丰富的API,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。比如说,系统提供打开文件系统调用sys_open对应的是Glibc中的open函数。
