2 Process and Thread Management 进程和线程管理
Tanenbaum的章节标题是进程和线程。我更喜欢加上“管理”这个词。主题是进程、线程、调度、中断处理和IPC(Inter-Process Communication—and Coordination进程间通信和协调)。
2.1 Process 进程
定义:A process is a program in execution. 进程是一个正在执行的程序。
我们假设一个多道程序操作系统可以从一个进程切换到另一个进程。
- 多道程序设计必须区别于多处理器,前者是可以同时运行多个程序,后者是由多个CPU的硬件体系上运行的单个OS。
- 我们有时会说多个CPU是并行运行的,这个多处理器系统显示出真正的并行性。
- 相比之下,多道程序设计编程有时被称为伪并行,因为它可以产生多个CPU的错觉。
- 在本课程中,我们不学习真正的并行性、分布式系统或多处理器。
2.2 The Process Model进程模型
尽管实际上系统在多个进程之间快速切换,但操作系统给每个进程一种它在单独运行的错觉。这是操作系统提高用户进程抽象级别的一个示例。 实际上,我们有左边的图表; 但用户会看到右上角的图表。
Virtual Time虚拟时间
虚拟时间就是这个进程所使用的时间,在一些调度算法中可能会选取当前运行时间占比最少的进程进入运行态,因此使用虚拟时间可以体现出不同进程间的优先级别。虚拟时间以独立于其他进程的速度进行(每个进程在实际时间内增长的虚拟时间是不一样的)。 操作系统再次提高了抽象级别,为流程提供了一个更愉快的环境。(实际上,在用于进程切换的系统调用期间,虚拟时间通常会增加一点;因此,如果存在其他进程,则会产生虚拟时间开销。)
https://www.cnblogs.com/linhaostudy/p/10298511.html#_label4
Virtual Memory虚拟内存
虚拟内存是进程所查看的内存。每个进程通常认为它有一个从位置0开始的连续内存块。当然,这不可能对所有进程都是这样的(否则它们将使用相同的内存),在现代系统中,没有进程是这样的(分配给单个进程的实际内存不是连续的,也不包括位置0)。x86下每个进程的虚拟内存是4GB,其中内核空间1GB(共享),进程空间3GB。
考虑输入到您的Lab1连接器的单个模块。每个模块从零开始为其地址编号;链接器最终将这些相对地址转换为绝对地址。也就是说,你的链接器为它的用户(编译器/汇编器)提供了一个虚拟内存,其中每个模块的地址从0开始。
虚拟时间和虚拟内存是操作系统为用户进程提供的抽象示例,以便后者体验到比实际存在的更愉快的虚拟机。
2.2.1 Process Creation进程创建
从用户或外部的角度来看,有几种创建流程的机制。
a. 系统初始化,包括守护进程;
b. 由正在运行的进程执行进程创建系统调用,fork();
c. 创建新进程的用户请求,windows双击打开一个.exe;
d. 批处理作业的启动,windows的.bat脚本;
但从内部来看,从系统的角度来看,第二种方法占主导地位。实际上,在Unix的早期版本中,系统初始化时只创建一个进程(称为init);其他的都是由fork()系统调用创建的。
Question: Why have init? That is, why not have all processes created via method 2?
Answer: Because without init there would be no running process to create any others.
Definition of daemon守护进程
许多系统都有潜伏的守护进程,在需要时执行任务。我很确定这个术语与神话有关,但直到一个学生在https://developer.syndetic.org/query_jargon.pl?term=demon上找到了术语词典,我才有了参考
Daemon:来自神话意义,后来被合理地解释为未被显式调用,但处于休眠状态等待某些条件发生的程序。其思想是,这种情况的犯罪者不需要知道潜伏着一个守护进程(尽管程序通常只会提交一个动作,因为它知道它将隐式调用一个守护进程)。例如,在ITS(一个非常早期的操作系统)下,在LPT假脱机程序目录上写入文件将调用假脱机守护进程,然后该守护进程将打印文件。这样做的好处是,想要打印文件的程序(在本例中)既不需要竞争对LPT的访问,也不需要理解LPT的任何特性。它们只需输入隐式请求,并让守护进程决定如何处理它们。守护进程通常由系统自动生成,可能永远存在,也可能每隔一段时间重新生成一次。通常情况下,wikipedia.org被证明是有用的。这是一个更大条目的第一段。*也有daemon的其他用途的条目。
在Unix和其他计算机多任务操作系统中,守护进程是在后台运行的计算机程序,而不是在用户直接控制下运行的程序,它们通常被实例化为进程。通常守护进程的名称以字母“d”结尾;例如:syslogd是处理系统日志的守护进程。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> void handler(int sig); int main() { __pid_t pid; if ((pid = fork()) == -1) { perror("fail to fork!"); exit(1); } else if (pid > 0) { printf("parent process exit!\n"); exit(0); } else { printf("daemon process start!\n"); if (signal(SIGQUIT, handler) == SIG_ERR) { perror("fail to signal"); exit(1); } while (1) { } exit(0); } } void handler(int sig) { if (sig == SIGQUIT) { printf("daemon process has been trigged\n"); } }
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> int main() { while (1) { kill(daemon pid, SIGQUIT); sleep(1); } return 0; }
2.1.2 Process Termination进程终止
从外部来看,进程的几种终止机制如下:
- Normal exit正常退出(自愿):return 0;exit(0);_exit(0);
- Error exit错误退出(自愿):exit(1);
- Fatal error致命错误(非自愿):执行非法指令;读取无效数据或代码;执行当前的用户态或核心态所不允许的操作;除零错误(只对整数有效,对浮点数操作时按IEEE浮点数标准规定会产生一个无穷数)。https://zh.wikipedia.org/wiki/%E8%87%B4%E5%91%BD%E9%94%99%E8%AF%AF
4.Killed by another process被其它进程杀死(非资源):其它进程向当前进程发送SIGKILL,SIGINT等可杀死当前进程的信号。
同样,内部的情况更简单。在Unix术语中,有两个系统调用kill()和exit()。
- kill():向另一个进程发送信号。对于许多类型的信号,如果没有捕获到这种信号(通过signal()或sigaction()系统调用),进程就会终止。当然还有一些无法捕捉的信号。
- exit()系统调用用于自我终止,可以指示成功或失败。
2.1.3 Process Hierarchies进程层次
现代通用操作系统允许用户创建和销毁进程。
- 在Unix中,这是通过fork()系统调用来完成的,它创建了一个子进程,exit()系统调用来终止当前进程(kill()系统调用来终止另一个进程)。
- 对应的Win32系统调用是CreateProcess和ExitProcess。
- 在fork之后,父进程和子进程都可以继续运行(实际上它们有相同的程序文本),每个进程都可以fork其他进程。
- 进程树的结果。树的根是操作系统在启动过程中创建的一个特殊进程。
- 进程可以选择等待子进程终止。例如,如果C发出一个wait()系统调用,提到G, C将阻塞直到G完成。
像MS-DOS这样的旧操作系统并没有完全的多程序设计。当一个进程创建另一个进程时,父进程被自动阻塞,必须等待子进程终止。结果是,没有进程可以有超过一个子进程,这意味着进程树退化为一条线。