2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结 2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结

教材内容学习总结

进程控制

获取进程

每个进程都有唯一一个正数进程ID。函数getpid返回目标进程的ID,函数getppid返回目标进程的父进程ID。

创建进程和终止进程

从程序员角度,进程被认为总是处在以下三种状态之一:

  • 运行 进程要么正在CPU上执行,要么是在等待被执行借最终会被内核调度。
  • 停止 进程的执行被挂起,且不会被调度。当进程收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号时,进程就停止,并保持停止直到收到SIGCONT信号,在这个时刻,进程再次开始运行。
  • 终止 进程永远地停止了。进程因为三种原因终止:收到一个信号,该信号的默认行为是永远终止进程;从主程序返回;调用exit函数。
    父进程通过调用fork函数来创建子进程。
    子进程几乎但不完全和父进程相同。新诞生的子进程享有父进程用户级的虚拟地址空间相同的一份副本,包括代码和数据段、对、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,即子进程可以读写父进程中打开的任意文件。子进程与父进程之间最大的区别是它们的ID。
    fork函数只被调用一次,却会返回两次:一次是在父进程中,另一次是在子进程中。在父进程中,fork返回诞生的子进程的PID;在子进程中,fork返回0。因为子进程的PID不为0,所以可以通过返回值来分辨程序是在子进程还是在父进程中运行了。
    一个嵌套fork函数的程序,fork的返回次数为2的被嵌套次数次方个。
    2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结
2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结

回收进程

当一个进程由于某种原因终止是,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到它被父进程回收。
如果一个父进程终止了,内核会安排init进程成为它的孤儿进程的养父。init进程的PID为1,是在系统启动时由内核创建的,它不会终止,是所有进程的祖先。如果父进程没有回收它的僵死子进程就终止了,那么内核会安排init进程来回收。
一个进程可以通过函数waitpid来等待其子进程终止或停止。

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statusp, int options);

wait函数是waitpid函数的简单版本:

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statusp);

调用wait(&status)等价于调用waitpid(-1,&status,0)。

让进程休眠

sleep函数将一个进程挂起一段指定的时间。

#include <unistd.h>
unsigned int sleep(unsigned int secs);

如果请求时间到了,sleep函数返回0,否则就是返回剩余休眠秒数。

信号

系统信号列表:
2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结
2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结

用kill函数发送信号

进程通过调用kill函数发送信号给其他进程(包括它们自己)。

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

如果pid大于0,那么kill函数发送信号号码sig给进程pid。如果pid等于0,那么kill发送信号sig给调用进程所在进程组的每个进程,包括调用进程自己。如果pid小于0,kill发送信号sig给进程组|pid|中的每一个进程。
所以函数kill并不能够杀死任意进程,而是只能发送信号。

课后习题

8.25

编写fegts函数的一个版本,叫做tfgets,它5秒钟后会超时。tfgets函数接收和fgets相同的输入。如果用户在5秒内不键入一个输入行,tfgets返回NULL。否则,它返回一个只想输入行的指针。

分析

fgets的定义如下:
char *fgets(char *buf, int bufsize, FILE *stream);
参数:
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明buf指向的字符数组的大小。
*stream: 文件结构体指针,将要读取的文件流。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
sigjmp_buf env;
void tfgets_handler(int sig)
{
    signal(SIGALRM, SIG_DFL);
    siglongjmp(env, 1);
}

char *tfgets(char *buf, int bufsize, FILE *stream)
{
    static const int TimeLimitSecs = 5;
    signal(SIGALRM, tfgets_handler)
    alarm(TimeLimitSecs);
    int rc = sigsetjmp(env, 1);
    if(rc == 0) return fgets(buf, bufsize, stream);
    else return NULL; //alarm,time out
}

显然,在tfgets里一开始需要调用fgets。然而,因为五秒时间到了,fgets还没有返回,所以我们必须在处理程序里直接跳转到某个地方进行tfgets的NULL返回。这就需要用到非本地跳转。

代码托管

2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结
2017-2018-1 20155223 《信息安全系统设计基础》第14周学习总结

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 31/31 1/1 20/20
第三周 24/55 2/3 24/44 知道浮点数怎么储存的
第四周 177/328 2/5 17/61 现在我的C语言程序也会在Linux命令行下使用了:*)
第五周 54/382 2/7 18/79 复习一遍汇编语言
第七周 2360/2722 1/8 12/91
第八周 624/3344 2/10 19/110 了解多线程和多进程
第九周 1112/4456 3/13 15/125 学习怎么实现pwd命令
第十一周 157/4613 2/15 10/135 在紧急情况下恢复不可使用的虚拟机
第十三周 999/5471 2/17 17/152 回顾了线程部分的内容
第十四周 181/5652 1/18 10/162

参考资料