20145309信息安全系统设计基础第11周学习总结后篇
进程
- 异常是允许操作系统提供进程的概念所需要的基本构造块。
- 进程:一个执行中的程序的实例。
- 上下文是由程序正确运行所需要的状态组成的,这个状态包括存放在存储器中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
- 进程提供给应用程序的关键抽象:
- 一个独立的逻辑控制流,独占地使用处理器;
- 一个私有的地址空间,独占地使用存储器系统。
- 并发流:一个逻辑流的执行在时间上与另一个流重叠。
- 并发:多个流并发地执行的一般现象。
- 多任务:一个进程和其他进程轮流运行的概念。
- 时间片:一个进程执行它的控制流的一部分的每一时间段。
- 并行流:如果两个流并发的运行在不同的处理器核或者计算机上
用户模式和内核模式
- 模式位:用某个控制寄存器中的一个位模式,限制一个应用可以执行的指令以及它可以访问的地址空间范围。
- 当设置了位模式,进程就运行在内核模式中,一个运行在内核模式中的进程可以中兴指令集中的任何指令,而且可以访问系统中任何存储器位置。
- 没有设置位模式时,进程就运行在用户模式中,不允许执行特权指令,例如停止处理器、改变位模式,或者发起一个I/O操作。
- 用户程序必须通过系统调用接口间接的当问内核代码和数据。
- 进程从用户模式变为内核模式的唯一方法是通过诸如中断、故障、或者陷入系统调用这样的异常。
上下文切换
- 上下文就是内核重新启动一个被抢占的进程所需的状态。
- 调度:内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。有内核中称为调度器的代码处理的。
- 上下文切换机制:
- 保存当前进程的上下文
- 恢复某个先前被抢占的进程被保存的上下文
- 将控制传递给这个新恢复的进程
- 引起上下文切换的情况
- 当内核代表用户执行系统调用时
- 中断时
系统调用错误处理
- 错误处理包装函数:包装函数调用基本函数,检查错误,如果有任何问题就终止。
进程控制
获取进程ID
- 每个进程都有一个唯一的正数的进程ID。
- getpid函数返回调用进程的PID,getppid函数返回它的父进程的PID。上面两个函数返回一个同类型为pid_t的整数值,在linux系统中,它在types.h中被定义为int。
创建和终止进程
- 进程总处于三种状态
- 运行:进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。
- 停止:程序的执行被挂起,,且不会被调度。
- 终止:进程用永远停止了。终止原因:(1)收到一个信号,默认行为是终止进程;(2)从主进程返回(3)调用exit函数。
- 父进程通过调用fork函数创建一个新的运行的子进程。
- 子进程和父进程的异同:
- 异:有不同的PID
- 同:用户级虚拟地址空间,包括:文本、数据和bss段、堆以及用户栈。任何打开文件描述符,子进程可以读写父进程中打开的任何文件。
- fork函数: 因为父进程的PID总是非零的,返回值就提供一个明确的方法来分辨程序是在父进程还是在子进程中执行。
fork函数的特点:- 调用一次,返回两次
- 并发执行
- 相同的但是独立的地址空间
- 共享文件
回收子进程
- 当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。
一个终止了但还未被回收的进程称为僵死进程。 - 一个进程可以通过调用waitpid函数来等待它的子进程终止或者停止。
第八章代码
exec1
代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 int main(){ 4 char *arglist[3]; 5 arglist[0] = "ls"; 6 arglist[1] = "-l"; 7 arglist[2] = 0 ; 8 printf("* * * About to exec ls -l\n"); 9 execvp( arglist[0] , arglist ); 10 printf("* * * ls is done. bye\n"); 11 }
可以看到这个代码中用了execvp函数。
表头文件:
#include<unistd.h>
定义函数:
int execvp(const char file ,char const argv []);
execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
exec2
1 #include <stdio.h> 2 #include <unistd.h> 3 4 int main() 5 { 6 char *arglist[3]; 7 char*myenv[3]; 8 myenv[0] = "PATH=:/bin:"; 9 myenv[1] = NULL; 10 11 arglist[0] = "ls"; 12 arglist[1] = "-l"; 13 arglist[2] = 0 ; 14 printf("* * * About to exec ls -l\n"); 15 16 execlp("ls", "ls", "-l", NULL); 17 printf("* * * ls is done. bye\n");
它与exec1的区别就在于exevp函数的第一个参数,exec1传的是ls,exec2直接用的arglist[0],不过由定义可得这两个等价,所以运行结果是相同的。
exec3
代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 4 int main() 5 { 6 char *arglist[3]; 7 char*myenv[3]; 8 myenv[0] = "PATH=:/bin:"; 9 myenv[1] = NULL; 10 11 arglist[0] = "ls"; 12 arglist[1] = "-l"; 13 arglist[2] = 0 ; 14 printf("* * * About to exec ls -l\n"); 15 16 execlp("ls", "ls", "-l", NULL); 17 printf("* * * ls is done. bye\n"); 18 }
forkdemo1
代码如下:
1 #include <stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 int main() 5 { 6 int ret_from_fork, mypid; 7 mypid = getpid(); 8 printf("Before: my pid is %d\n", mypid); 9 ret_from_fork = fork(); 10 sleep(1); 11 printf("After: my pid is %d, fork() said %d\n", 12 getpid(), ret_from_fork); 13 14 return 0; 15 }
代码解释:
这个代码先是打印进程pid,然后调用fork函数生成子进程,休眠一秒后再次打印进程id,这时父进程打印子进程pid,子进程返回0.
运行结果如下:
forkdemo2
代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 4 int main() 5 { 6 printf("before:my pid is %d\n", getpid() ); 7 fork(); 8 fork(); 9 printf("aftre:my pid is %d\n", getpid() ); 10 11 return 0; 12 }