CSAPP3e-学习笔记-第八章
判断题:异常分为四种类别:中断(interrupt)、陷阱(trap)、故障(fault)、终止 (abort),四类异常都是同步异常。(错)
CSAPP3e 8.1.2 异常的类别
异常可以分为四类:中断 (interrupt) 、陷阱 (trap) 、故障 (fault) 和终止 (abort) 。下面的表对这些类别的属性做了小结。
判断题:进程是一个执行中程序的实例,每个进程都运行在某个进程的上下文(context) 中。(对)
CSAPP3e 8.2 进程
定义:一个执行中程序的实例。
进程提供给应用程序两个关键抽象:
逻辑控制流
- PC(程序计数器)值的序列叫做逻辑控制流。
- 每个程序似乎独占地使用 CPU
- 通过 OS 内核的上下文切换机制提供
私有地址空间
- 每个程序似乎独占地使用内存系统
- OS 内核的虚拟内存机制提供
判断题:上下文(context)是由程序正确运行所需要的状态组成,包括:内存中的程序的代码和数据、栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。(对)
CSAPP3e 8.2.5 上下文切换
进程由常驻内存的操作系统代码块(称为内核)管理,内核不是一个单独的进程,而是作为现有进程的一部分运行。
操作系统内核使用上下文切换实现多任务。上下文包含进程的状态:通用目的寄存器、浮点寄存器、程序计数器、用户栈、内核栈、各种内核数据结构。
调度、调度器、上下文切换过程:
判断题:父进程和新创建的子进程具有相同的 PID 和 PGID。(错)
判断题:fork 函数调用一次返回两次,一次返回到父进程,一次返回到新创建的子进程。(对)
CSAPP3e 8.4.2 创建和终止进程
从程序员的角度,我们可以认为进程总是处于下面三种状态之一
- 运行(Running)
进程要么在 CPU 上执行,要么在等待被执行且最终会被操作系统内核调度。 - 停止/暂停/挂起(Stopped/Paused/Hanged)
进程的执行被挂起且不会被调度,直到收到新的信号。 - 终止(Terminated)
进程永远地停止了,但仍占资源。
创建进程
父进程通过调用 fork
函数创建一个新的运行的子进程。
1 |
|
- 被调用一次,却返回两次
- 子进程返回 0,父进程返回子进程的 PID
- 新创建的子进程几乎但不完全与父进程相同
- 子进程得到与父进程虚拟地址空间相同的(但是独立的)一份副本(代码、数据段、堆、共享库以及用户栈)
- 子进程获得与父进程任何打开文件描述符相同的副本
- stdout 文件在父、子进程是相同的
- 子进程有不同于父进程的 PID
- 并发执行
- 不能预测父进程与子进程 的执行顺序
CSAPP3e 8.5.2 发送信号: 进程组
每个进程只属于一个进程组,进程组是由一个正整数进程组 ID 来标识的(PGID)。
多选题:每个信号类型都有一个预定义的默认行为,分别是(A B C D)
A. 进程终止
B. 进程终止并转储内存
C. 进程停止(挂起)直到被SIGCONT
信号重启
D. 进程忽略该信号
CSAPP3e 8.5 Linux 信号
signal 就是一条小消息,它通知进程系统中发生了一个某种类型的事件。
- 类似于异常和中断
- 从内核发送到(有时是在另一个进程的请求下)一个进程
- 信号类型是用小整数 ID 来标识的(1-30)
- 信号中唯一的信息是它的 ID 和它的到达
CSAPP3e 8.5.3 接收信号
每个信号类型都有一个预定义默认行为, 是下面中的一种:
- 进程终止
- 进程终止并转储内存
- 进程停止(挂起)直到被
SIGCONT
信号重启 - 进程忽略该信号
进程可以通过 signal
函数修改和信号相关联的默认行为,其中 SIGSTOP
和 SIGKILL
的默认行为不能修改。
多选题:进程调用
int kill(pid_t pid, int sig)
函数发送信号给其他进程,以下说法正确的是(A B C)
A. $pid>0$:kill
函数发送信号号码 sig 给进程 pid。
B. $pid=0$:kill
函数发送信号 sig 给调用进程所在进程组中的每个进程。
C. $pid<0$:kill
函数发送信号 sig 给进程组 $| pid |$(pid 的绝对值)中的每个进程。
D. 以上说法都不正确
CSAPP3e 8.5.2 发送信号: 进程组
进程可以通过调用 kill
函数发送信号给其他进程(包括自己)。
1 |
|
pid 取值有三种情况:
pid | 结果 |
---|---|
$pid>0$ | kill 函数发送信号号码 sig 给进程 pid。 |
$pid=0$ | kill 函数发送信号 sig 给调用进程所在进程组中的每个进程。 |
$pid<0$ | kill 函数发送信号 sig 给进程组$ |
多选题:发送信号的原因:(A B)
A. 内核检测到一个系统事件,比如除零错误或子进程终止。
B. 一个进程调用了kill
函数,显式地要求内核发送一个信号给目的进程。
C. 进程创建子进程。
CSAPP3e 8.5.1 信号术语:发送信号
内核通过更新目的进程上下文中的某个状态,发送 (递送)一个信号给目的进程。
发送信号可以是如下原因之一:
- 内核检测到一个系统事件如除零错误(
SIGFPE
)或者子进程 终止(SIGCHLD
)。 - 一个进程调用了
kill
系统调用,显式地请求内核发送一 个信号到目的进程。- 一个进程可以发送信号给它自己。
多选题:signal 函数接受两个参数:信号值和函数指针,可以通过下列哪些方法(A B C)来改变和信号 signum 相关联的行为。
A. 如果 handler 是 SIG_IGN,那么忽略类型为 signum 的信号。
B. 如果 handler 是 SIG_DFL,那么类型为 signum 的信号行为恢复为默认行为。
C. 如果 hanlder 是用户定义的函数的地址,这个函数就被称为信号处理程序。
CSAPP3e 8.5.3 接收信号
可以使用 signal
函数修改和信号 signum 相关联的默认行为:
1 |
|
handler 的不同取值:
SIG_IGN
: 忽略类型为 signum 的信号SIG_DFL
: 类型为 signum 的信号行为恢复为默认行为- 否则,handler 就是用户定义的函数的地址,这个函数称为信号处理程序
- 只要进程接收到类型为 signum 的信号就会调用信号处理程序
- 将处理程序的地址传递到
signal
函数从而改变默认行为,这叫作设置信号处理程序 - 调用信号处理程序称为捕获信号
- 执行信号处理程序称为处理信号
- 当处理程序执行
return
时,控制会传递到控制流中被信号接收所中断的指令处
单选题:以下对Linux 系统提供的监控和操作进程的工具说法错误的是(C):
A. $TOP$:打印关于当前进程资源使用的信息。
B. $STRACE$:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹。
C. $PS$:列出当前系统中的进程,不包括僵死进程。
D. $/proc$:一个虚拟文件系统,以 ASCII 文本格式输出大量内核数据结构的内容,用户程序可以读取这些内容。
CSAPP3e 8.4.3 回收子进程
为什么回收?–与fork创建相反!
当进程终止时,它仍然消耗系统资源
- Examples: Exit status, various OS tables(占用内存)
称为僵死进程( “僵尸 zombie”进程)
僵死进程占用内存资源、打开的 IO 资源
回收 (Reaping)
- 父进程执行回收(using
wait
orwaitpid
) - 内核删掉僵死子进程、从系统中删除掉它的所有痕迹
父进程不回收子进程的后果:
- 如果父进程没有回收它的僵死子进程就终止了,内核安排 init-养父 进程去回收它们(init 进程 PID 为 1,系统启动时创建,不会终止,是所有进程的祖先)
- 长时间运行的进程应当主动回收它们的僵死子进程
ps
命令:列出当前系统中的进程
1 |
|
ps
命令显示的子进程标记为 “defunct”,即僵死进程。
CSAPP3e 练习题8.4 P520
考虑下面的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main() {
int status;
pid_t pid;
printf("Hello\n");
pid = Fork();
printf("%d\n", !pid);
if (pid != 0) {
if (waitpid(-1, &status, 0) > 0) {
if (WIFEXITED(status) != 0)
printf("%d\n", WEXITSTATUS(status));
}
}
printf("Bye\n");
exit(2);
}A. 这个程序会产生多少输出行?
B. 这些输出行的一种可能的顺序是什么?
A. 打印输出 6 行
B. 可能的输出:Hello, 1, 0, Bye, 2, Bye
CSAPP3e 8.4.3 回收子进程 >> 与子进程同步:wait
/waitpid
wait 函数
父进程通过 wait
/waitpid
函数回收子进程:
- 调用
wait(&status)
等价于调用waitpid(-1, &status, 0)
。
1 |
|
挂起当前进程的执行直到它的一个子进程终止。
返回已终止子进程(可能很多,是个集合)的 pid。
如
child_status != NULL
,则在该指针指向的整型量中写入关于终止原因和退出状态的信息。用
wait.h
头文件中定义的宏来检查(具体作用查看):WEXITSTATUS
是一个检验子进程退出的正常还是非正常和返回值的宏。WIFEXITED(status)
这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。WEXITSTATUS(status)
当WIFEXITED
返回非零值时,可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)
退出,WEXITSTATUS(status)
就会返回 5;如果子进程调用exit(7)
,WEXITSTATUS(status)
就会返回 7。请注意,如果进程不是正常退出的,也就是说,
WIFEXITED
返回 0,这个值就毫无意义。
子进程完成结束的顺序是任意的(没有固定的顺序)。
可用宏函数
WIFEXITED
和WEXITSTATUS
获取进程的退出状态信息。
waitpid 函数
1 |
|
- 默认情况下(当
options
= 0 时),waitpid
挂起调用进程的执行,直到它的等待集合(wait set) 中的一个子进程终止。 - 如果等待集合中的一个进程在刚调用的时刻就已经终止了,那么
waitpid
就立即返回。
在上面这两种情况中,waitpid
返回导致 waitpid
返回的已终止子进程的 PID。此时,已终止的子进程已经被回收,内核会从系统中删除掉它的所有痕迹。
参数说明:
判定等待集合的成员
pid 成员 $pid=-1$ 等待集合是由父进程所有的子进程组成的。 $pid>0$ 等待集合是一个单独的子进程,它的进程 ID 等于 pid。 修改默认行为
通过设置
Options
来修改默认行为(默认Options
= 0,等待子进程终止):options 行为 WNOHANG
如果等待集合的任何子进程没有终止,就立即返回 0。
父进程可继续其他工作。WUNTRACED
挂起当前进程,直到等待集合中的任一进程终止或停止,则返回其 pid。
用于检查。WCONTINUED
挂起当前进程,直到等待集合中的任一进程从运行到终止或一个停止的进程收到 SIGCONT
而恢复运行。`WNOHANG WUNTRACED`组合