异常
与过程调用的差异
- 返回地址为当前指令,或者下一条指令。
- 从用户程序转移到内核时,状态信息是压入内核栈中。
- 内核模式下有完全访问权限。
异常类别
中断: 硬件
陷阱: 系统调用
故障: 缺页
终止: 硬件错误
进程
- the program’s code and data stored in memory,
- its stack, the contents of its general-purpose registers,
- its program counter,
- environment variables,
- the set of open file descriptors.
逻辑控制流
并发流
上图的A和B
并行流
不在同一处理器中
地址空间
内核模式
位于地址空间顶部,通过CPU切换
上下文切换
每个进程都有一个上下文,其中包含寄存器、程序计数器等信息。
当调度进程时要(在内核模式)切换上下文:
- 保存当前的
- 恢复新来的
- 控制转交给新的
高速缓存污染
切换了进程或者调用了中断程序后,原进程继续运行,但高速缓存的内容可能已经发生了变化。
进程控制
信号
发送信号
接收信号
- signal
- sigaction
- sigprocmask
- sigemptyset
- sigfillset
- sigaddset
- sigdelset
- sigismember
内核从异常处理程序返回,将控制交给进程时,会检查待处理信号(pending&~blocked),并强制进程去接收
if k in pending&~blocked:
handler(k)
else:
nexti
void handler2(int sig)
{
pid_t pid;
printf("%d\n", sig);
while ((pid = waitpid(-1, NULL, 0)) > 0){
printf("Handler reaped child %d\n", (int)pid);
Sleep(2);
}
if (errno != ECHILD)
unix_error("waitpid error");
return;
}
int main() {
pid_t pid;
if (signal(SIGCHLD, handler2) == SIG_ERR)
unix_error("signal error");
for (int i = 0; i < 3; i++) {
pid = Fork();
if (pid == 0) {
printf("Hello from child %d\n", (int)getpid());
Sleep(1);
exit(0);
}
}
exit(0);
}
输出
Hello from child 8001
Hello from child 8002
Hello from child 8003
17 // 来自8001 handler
Handler reaped child 8001 // 来自8001 handler
Handler reaped child 8002 // 来自8001 handler
Handler reaped child 8003 // 来自8001 handler
17 // 来自8002 handler
.
8002 8003是由8001的handler的waitpid处理掉,并不依赖SIGCHLD。
所以有个SIGCHLD信号被父进程接收就可以,多余的信号随意。
当然,以上所述均是在1 2 3进程按序执行的假设下。
实际上顺序是不确定的,但总有一个进程先行一步。