# 第三章 Unix/Linux进程管理
## 多任务处理
Unix/Linux中的多任务处理是指通过操作系统的调度机制,使多个进程可以同时运行,互不影响,并共享计算机系统的资源。这样可以提高系统的效率和利用率。
1. 进程状态:进程可以处于运行(Running)、就绪(Ready)、等待(Blocked)等不同的状态,调度器根据优先级和进程当前的状态来决定哪个进程在某个时刻运行。
2. 调度器:操作系统中的一个组件,负责决定哪个进程在什么时候运行。调度器可以根据不同的调度算法来进行进程调度,如先来先服务(FCFS)、最短作业优先(SJF)、时间片轮转等。
3. 上下文切换:由于单个处理器只能同时执行一个进程,所以当操作系统决定切换到其他进程时,需要保存当前进程的上下文信息,包括寄存器的值、程序计数器等,并恢复切换到的进程的上下文信息,这个过程称为上下文切换。
4. 进程间通信:多个进程之间需要共享数据或进行通信时,可以使用进程间通信(IPC)机制,如管道、信号、共享内存、消息队列等。
## 进程的概念
- 在Linux中,进程的创建通过父进程派生子进程来实现。当一个进程启动另一个程序时,它会调用fork()系统调用创建一个新的子进程,并将父进程的所有资源复制到子进程中。然后,子进程可以通过exec()系统调用加载一个新的程序镜像来执行其他操作。
- 进程的状态包括运行、就绪、阻塞和终止等。运行状态表示进程正在执行中,就绪状态表示进程已经准备好但尚未获得CPU时间片,阻塞状态表示进程被某些事件(如IO操作)阻塞无法执行,终止状态表示进程已经完成执行或出现异常而终止。
- 进程和程序是两个不同的概念,但它们之间有着紧密的联系。
1. 程序是一个静态的实体,是一组指令的集合,它被存储在磁盘中。程序本身并不能直接执行,需要通过操作系统的调用来创建一个进程来运行。
2. 进程是程序在计算机中的一次执行过程。当一个程序被加载到内存中并执行时,它就成为了一个进程。进程是程序运行时的实例,有自己的内存空间、寄存器、栈等资源。
每个进程都有一个唯一的进程ID(PID),用来标识它在系统中的唯一性。进程之间是相互独立的,它们有自己的地址空间和资源,彼此之间不能直接访问。进程之间可以通过进程间通信(IPC)机制来进行数据交换和协作。
3. 一个程序可以被多个进程同时执行,也可以同时执行多个程序,这取决于操作系统的调度策略和系统资源的情况。
4. 总结来说,程序是静态的,是指令的集合,而进程是程序在计算机中的一次执行过程,有自己的资源和运行状态。通过创建进程,程序才能在计算机系统中得到执行。
## Unix/Linux中的进程
### 进程的来源
进程是由操作系统内核(kernel)创建的(通过初始化PROC的内容,并让运行指向proc[0]),当用户运行一个程序时,内核会创建一个新的进程,并为其分配一些资源(如内存、文件描述符、CPU时间等)。每个进程都有一个唯一的进程ID(PID),用于管理和跟踪进程的状态。进程的状态包括运行、等待、挂起、终止等。
### INIT和守护进程
- INIT是一个守护进程,即在Linux系统中负责启动和管理其他进程的一个特殊进程。INIT进程的进程ID(PID)为1,是所有其他进程的祖先进程。
- INIT进程在系统启动时就会被内核启动,并负责启动所有其他进程。它按照特定的顺序启动系统各个子系统和服务,并管理它们的生命周期。当有进程终止时,INIT进程负责清理子进程的资源并记录退出状态。
- 守护进程的例子:
```
syslogd: log daemon process
inetd : Internet service daemon process
httpd: HTTP server daemon process
etc.
```
### 登录进程
- 在Unix/Linux系统中,登录进程是系统中用于处理用户登录的进程。它负责验证用户的身份,创建用户的会话,并为用户提供一个交互式的终端环境。
- 当用户在控制台上输入用户名和密码时,登录进程会验证用户的身份信息。如果身份验证成功,登录进程会为用户创建一个会话,并启动一个新的shell进程以提供交互式终端环境。
### sh进程
当用户成功登录时,LOGIN进程会获取用户的gid 和 uid,从而成为用户的进程。它将目录更改为用户的主目录并执行列出的程序,通常是命令解释程序sh。现在,用户进程执行sh,因此用户进程通常称为sh进程。它提示用户执行命令。一些特殊命令,如cd(更改目录)、退出、注销等,由sh自己直接执行。其他大多数命令是各种bin目录(如/bin、/sbin、/usr/bin、/usr/local/bin等)中的可执行文件。对于每个(可执行文件)命令,sh会复刻一个子进程,并等待子进程终止。子进程将其执行映像更改为命令文件并执行命令程序。子进程在终止时会唤醒父进程sh,父进程会收集子进程终止状态、释放子进程PROC结构体并提示执行另一个命令等。除简单的命令之外,sh还支持 I/O 重定向和通过管道连接的多个命令
### 进程的执行模式
- 进程两种执行模式的执行:内核模式和用户模式。每种模式下,一个进程都有一个执行映像。
- 每个进程都在Kmode 下产生并开始执行。它在Kmode下执行所有相关操作,包括终止。在Kmode模式下,通过将CPU的状态寄存器从K模式更改为U模式,可轻松切换到Umode。但是,一日进入Umode,就不能随意更改CPU的状态了,原因很明显。Umode进程只能通过以下三种方式进入Kmode:
(1)中断:中断是外部设备发送给CPU的信号,请求CPU服务。当在Umode 下执行时,CPU 中断是启用的,因此它将响应任何中断。在中断发生时,CPU将进入Kmode来处理中断,这将导致进程进入Kmode。
(2)陷阱:陷阱是错误条件,例如无效地址、非法指令、除以0等,这些错误条件被CPU识别为异常,使得CPU进入Kmode来处理错误。在Unix/Linux中,内核陷阱处理程序将陷阱原因转换为信号编号,并将信号传递给进程。对于大多数信号,进程的默认操作是终止。
(3)系统调用:系统调用(简称syscall)是一种允许 Umode 进程进入Kmode以执行内核函数的机制。当某进程执行完内核函数后,它将期望结果和一个返回值返回到Umode,该值通常为0(表示成功)或﹣1(表示错误)。如果发生错误,外部全局变量errno(在errno.h中)会包含一个ERROR代码,用于标识错误。用户可使用库函数
perror ("error message");
打印错误信息,打印信息的后面会加一个描述错误的字符串。
## I/O重定向
### 文件流和文件描述符
sh进程有三个用于终端I/O的文件流:stdin(标准输入)、stdout(标准输出)和stderr(标准错误)。每个流都是指向执行映像堆区中FILE结构体的一个指针,如下文所示。
```
FILE *stdin -------> FILE structure
char fbuf[SIZE]
int counter, index, etc.
int £d = O; // £d[0] in PROC <=m from KEYBOARD
-----------------------------------------------------
FILE *stdout ------> FILE structure
char fbuf[SIZE]
int counter, index, etc.
int £d = 17 // £d[1] in PROC -=> CO SCREEN
-----------------------------------------------------
FILE *stderr ------> FILE structure
char fbuf[SIZE]
int counter, index, etc.
int £d- 2; // £d[2] in PROC ==> to SCREEN also
```
## 3.10 管道
管道是用于进程交换数据的单向进程间通信的通道。管道有一个输入端、一个输出端。在之前我们使用man -k | grep xx时,就用到管道的功能。
管道的使用可以通过程序完成,也可以在命令行中处理完成。
询问的问题
苏格拉底挑战