CSAPP笔记(八) 异常

深入理解计算机系统

异常

与过程调用的差异

  • 返回地址为当前指令,或者下一条指令。
  • 从用户程序转移到内核时,状态信息是压入内核栈中。
  • 内核模式下有完全访问权限。

异常类别

异常
中断: 硬件
陷阱: 系统调用
故障: 缺页
终止: 硬件错误

进程

  • 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切换

上下文切换

每个进程都有一个上下文,其中包含寄存器、程序计数器等信息。
当调度进程时要(在内核模式)切换上下文:

  • 保存当前的
  • 恢复新来的
  • 控制转交给新的

高速缓存污染

切换了进程或者调用了中断程序后,原进程继续运行,但高速缓存的内容可能已经发生了变化。

进程控制

  • fork 父进程返回子程序ID,子进程返回0.
  • waitpid
  • execve argv最后一项要为空指针,执行成功不返回

信号

signal.7
信号

发送信号

接收信号

    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进程按序执行的假设下。
实际上顺序是不确定的,但总有一个进程先行一步。