抢占式|深入理解计算机系统:进程( 二 )


多任务也叫做时间分片(time slicing) 。
如果两个流并发运行在不同的处理器或者计算机 , 称为并行流(parallel flow) 。

  • 私有地址空间(Private Address Space)
一般 , 进程间地址空间读写保护 。 进程地址空间32位进程 , 代码段从0x08048000开始 , 64位进程从0x00400000开始:
抢占式|深入理解计算机系统:进程文章插图
[ Process address space ]
  • 用户模式和内核模式(User and Kernel Modes)
  • 通过控制寄存器中的模式位(mode bit)描述进程当前享有的特权 。
  • 内核模式:(超级用户)可执行指令集中任何指令 , 并且可以访问系统中任何存储器位置 。
  • 用户模式:不允许执行特权指令 , 不允许直接引用地址空间中内核区内的代码和数据 , 任何尝试都会引发致命保护故障 。 可以通过系统调用接口间接访问内核代码和数据 。
  • 上下文切换(Context Switches)
  • 内核为每个进程维持一个上下文(context) , 是内核重新启动一个被抢占的进程所需的状态 。 包括:
  • 通用目的的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构(地址空间的页表、有关当前进程信息的进程表、进程已打开文件的信息的文件表)
  • 内核调度器(scheduler)负责调度进程 , 抢占当前进程 , 重新开始先前被抢占的进程 。
0x01 101 Inside ProcessProcess Control
如何控制进程?
?
PID
pid > 0
#include// for pid_t#include ?pid_t getpid(void); // 获取进程IDpid_t getppid(void); // 获取父进程IDCreating and Terminating Process
从程序角度来看 , 进程总处于以下三种状态:
  • Running——要么处于CPU执行中 , 要么处于等待被执行且最终会被内核调度 。
  • Stopped——进程被挂起(suspend) , 且不会被调度 。 当收到SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信号时 , 进程停止 , 直到收到SIGCONT信号 , 进程再次开始运行 。
  • Terminated——进程永远停止了 。 三种原因导致终止:
  1. 收到一个默认行为时终止进程的信号;
  2. 从主程序返回;
  3. 调用exit 。
#include #include /* 创建子进程* 返回:子进程=0 , 父进程=子进程PID , 出错=-1*/pid_t fork(void);?#include void exit(int status);父进程通过调用fork创建一个新的运行子进程 , 最大的区别在于不同的PID 。
  • fork():一次调用 , 返回两次 。
  1. 在调用进程中(父进程) , 返回子进程PID;
  2. 在新创建的子进程中 , 在子进程中返回0 。
  • 并发执行:父子进程是并发运行的独立进程 。
  • 相同但是独立的地址空间 。 子进程与父进程用户级虚拟地址空间相同的拷贝 , 相同的本地变量值、堆、全局变量、以及代码 。 如代码中print出来不一样的x 。
  • 共享文件:任何打开文件描述符相同的拷贝 , 如stdout 。
int main() { pid_t pid; int x = 1;? pid = fork(); // 在此处分裂出了两条时间线! if (pid == 0) {// 子进程 printf("child: x=%d\n", ++x); exit(0); } // 父进程 printf("parent: x=%d\n", --x); exit(0);? return 0;}out:
parent: x=0
child: x=2
child |————x=2————father ——————————x=0———— fork exitReap Child Process
进程终止时 , 保持位已终止状态 , 直到被父进程回收(reap) 。 当父进程回收已终止的子进程 , 内核将子进程的退出状态传递给父进程 , 然后抛弃已终止的进程 , 此刻进程不复存在 。
僵尸进程(zombie):一个终止了但还未被回收的进程 。 但是如果父进程没有回收就终止了 , 则内核安排init进程(PID=1)回收僵尸进程 。
#include #include ?/* 进程可以调用waitpid等待子进程终止或者结束 。 * 默认options=0 , 挂起调用进程 , 直到它等待集合中的一个子进程终止 。 如果等待集合中的一个进程在刚调用的时刻就已经终止了 , 那么waitpid立即返回 。 返回已终止的子进程PID , 并去除该子进程 。 ?*输入参数pid:pid>0 , 等待集合就是一个单独的子进程 , 进程ID等于pid 。 pid=-1 , 等待集合是由父进程所有的子进程组成 。 ?*输入参数options:WNOHANGE:等待集合中任何子进程都还没有终止 , 立即返回0;默认行为还是挂起调用进程直到子进程终止 。 WUNTRACED:挂起调用进程执行 , 直到集合中有一个进程终止或停止 。 返回该进程PID 。 WNOHANGE|WUNTRACED:立刻返回 , 0=如果没有终止或停止的子进程;PID=终止或停止的子进程PID 。 ?*输入参数status:WIFEXITED:True=子进程是通过return或者exit终止的;WEXITSTATUS:返回exit状态 , 只有WIFEXITED=True时被定义;WIFSIGNALED:True=子进程是因为一个未被捕获的信号终止的;WTERMSIG:返回导致子进程终止信号量 , 只有WIFSIGNALED=True被定义;WIFSTOPPED:True=返回的子进程是停止的;WSTOPSIG:返回引起子进程停止的信号的数量 , 只有WIFSTOPPED=True被定义;?返回:成功=子进程PID;if WNOHANG=0;其他错误=-1(errno=ECHILD , 没有子进程;errno=EINTR , 被一个信号中断)*/pid_t waitpid(pid_t pid, int *status, int options);pid_t wait(int *status); //等价于waitpid(-1,