Ch 8 进程控制

进程ID虽然是唯一的,但是却是可复用的。 大多数系统都会实现延迟复用算法,使被赋予新建进程的ID不同于最近终止进程所使用的ID。防止新进程被误认成之前的进程

ID=0的进程是调度进程,常被称为交换进程 ID=1的进程是init进程,负责自举内核后启动一个 UNIX系统,是一个以超级用户特权运行的普通用户进程,也是所有孤儿进程的父进程

还有其他一些操作系统服务的内核进程

fork

fork 调用一次,返回两次,子进程返回 0 因为可以调用 getpid 得到父进程的ID,父进程返回新建进程的ID是因为没有获得所有子进程ID的接口

通常父子会共享正文段,其他的段看情况,如果采用写时复制的话在修改某一段之前都是共享的状态

父子进程的执行先后与内核的调度算法有关

sizeof 和 strlen 区别: strlen 的长度不记录 null 字节 sizeof 的长度记录 null 字节

如果在 fork 前调用了带缓冲的IO,那么缓冲区在 fork 的时候也会被拷贝 因为所有打开的文件描述符也会被复制到子进程中,因此如果我们重定向父进程的标准输出,子进程的标准输出也会被同样重定向。因为偏移量也是共享的,因此会进行追加而不是覆盖

共享文件描述符在读写的时候需要进行进程同步,或者由一方(比如父进程)主动关闭不需要使用的文件描述符或主动等待对方先完成

fork 失败?

  1. 进程过多
  2. 用户ID的进程总数超过系统限制 CHILD_MAX

用法

  1. 希望进行自复制并执行不同代码段 网络服务
  2. 一个进程要执行不同程序 shell

vfork 可以保证子进程先运行,在它调用 exec 或 exit 之后父进程才被调度执行,在此之前父进程会进入休眠

exit

内核负责存储退出的状态,父进程通过 wait 或 waitpid 取得终止状态。 如果父进程在子进程之前终止,子进程会自动成为 init 进程 1 的子进程。终止之前内核会挨个检查这个进程的父进程是否要终止

wait/waitpid

  • 如果所有子进程都还在运行,则阻塞
  • 如果一个子进程已终止,取得终止状态并立即返回
  • 没有任何子进程,立即出错返回

wait 会阻塞等待,waitpid 不会阻塞

如果一个进程 fork 一个子进程,但不要它等待子进程终止,也不希望僵死,那么可以选择 fork 两次,让第一个子进程退出,那么子进程的子进程就会被 init 收养

exec

只是用磁盘上的新程序替换了当前进程的正文段,数据段,堆段和栈段

进程会计

每个进程结束后内核就会写一个会计记录,包括命令名,CPU时间总量,用户ID和组ID,启动时间等。这些数据都可以在进程表中找到。

终止后才写会计记录,因此如果进程没有终止,我们无法得到它的会计记录。 会计文件中的记录的顺序是按照终止的顺序来的。

会计记录对应的是进程而不是程序,新 fork 一个进程会初始化一个记录,一个进程如果执行了多个程序,记录中的命令名为最后一个执行的程序,CPU时间为所有执行的程序之和

进程调度

见 内核笔记