Unix/Linux进程管理
- 多任务处理
一般来说,多任务处理指的是同时进行几项独立活动的能力。在计算机技术中,多任务处理指的是同时执行几个独立的任务。在单处理器(单CPU)系统中,一次只能执行一个任务。多任务处理是通过在不同任务之间多路复用CPU的执行时间来实现的,即将CPU执行操作从一个任务切换到另一个任务。不同任务之间的执行切换机制称为上下文切换,将一个任务的执行环境更改为另一个任务的执行环境。如果切换速度足够快,就会给人一种同时执行所有任务的错觉。这种逻辑并行性称为“并发”。在有多个 CPU或处理器内核的多处理器系统中,可在不同CPU上实时、并行执行多项任务。此外每个处理器也可以通过同时执行不同的任务来实现多任务处理。 - 进程的概念
操作系统是一个多任务处理系统。任务也称为进程。
进程的正式定义:进程是对映像的执行。
操作系统内核将一系列执行视为使用系统资源的单一实体。系统资源包括内存空间、 I/O设备以及最重要的CPU时间。在操作系统内核中,每个进程用一个独特的数据结构表示,叫作进程控制块(PCB)或任务控制块(TCB)等。与包含某个人所有信息的个人记录一样,PROC结构体包含某个进程的所有信息。在实际操作系统中,PROC结构体可能包含许多字段,而且数量可能很庞大。首先,我们来定义一个非常简单的 PROC结构体来表示进程。
typedef struct proc{
struct proc *next;
int *ksp;
int pid;
int ppid;
int status;
int priority;
int kstack[1024];
}PROC;
在PROC结构体中,next是指旨向下一个PROC结构体的指针,用于在各种动态数据结构(如链表和队列)中维护PROC结构体。ksp字段是保存的堆栈指针。当某进程放弃使用 CPU时,它会将执行上下文保存在堆栈中,并将堆栈指针保存在PROC.ksp中,以便以后恢复。在PROC结构体的其他字段中 pid是标识一个进程的进程ID编号,ppid是父进程ID编号,status是进程的当前状态,p riority 是进程调度优先级,kstack是进程执行时的堆栈。操作系统内核通常会在其数据区中定定义有限数量的PROC结构体,表示为:
PROC proc [NPROC]; NPROC a constant,e.g. 64
Unix/Linux中的进程
- 进程来源:当操作系统启动时,操作系统内核的启动代码会强行创建一个PID=0的初始进程,即通过分配PROC结构体(通常是pi roc[0]) 进行创建,初始化PROC内容,并让运行指向 proc[0]。然后,系统执行初始进程1 P0。大多数操作系统都以这种方式开始运行第一个进程。 PO继续初始化系统,包括系统硬件和内核数据结构。然后,它挂载一个根文件系统,使系统可以使用文件。在初始化系统之后后,PO复刻出一个子进程P1,并把进程切换为以用户模式运行 P1。
- 守护进程:当进程 P1开始运行时,它将其执行映像更改为INIT程序。因此,P1通常被称为INIT进程,因为它的执行映像是init程序。P1开始复刻出许多子进程。P1的大部分子进程都是用来提供系统服务的。它们在后台运行,不与任何用户交互。这样的进程称为守护进程。
- 登录进程:除了守护进程外,P1还复刻了许多LOGIN进程,每个终端上一个,用于用户登录。
- 进程的执行模式:
内核模式,用户模式
(1)中断:中断是外部设备发送给CPU的信号,请求CPU服务。当在Umode下执行时,CPU中断是启用的,因此它将响应任何中断。在中断发生时,CPU将进入Kmode来处理中断,这将导致进程进入Kmode。
(2)陷阱:陷阱是错误条件,例如无效地址、非法指令、除以0等,这些错误条件被 CPU识别为异常,使得CPU进入Kmode来处理错误。在Unix/Linux中,内核陷阱处理程序将陷阱原因转换为信号编号,并将信号传递给进程。对于大多数信号,进程的默认操作是终止。
(3)系统调用:系统调用(简称syscall)是一种允许Umode进程进入Kmode以执行内核函数的机制。当某进程执行完内核函数后,它将期望结果和一个返回值返回到Umode,该值通常为0(表示成功)或-1(表示错误)。如果发生错误,外部全局变量errmo(在ermo. h中)会包含一个ERROR代码,用于标识错误。用户可使用库函数
进程管理的系统调用
系统调用:fork(),wait(),exec(),exit()
每个都是发出实际系统调用的库函数:
int syscall(int a, int b, int c, int d)
- fork():创建子进程并返回子进程的pid,如果fork()失败则返回-1 。在fork()成功之后,父进程和子进程都执行它们自己的Umode映像,紧跟着fork()之后的映像是完全相同的。
- wait():使父进程等待子进程的终止,并获取子进程的退出状态。
等待子进程终止:wait() 使父进程暂停执行,直到一个子进程终止。
获取子进程的退出状态:通过 wait() 返回的状态值,可以获取子进程的退出状态。
子进程回收:当子进程终止后,wait() 将回收子进程的资源,包括进程表项等。
阻塞父进程:如果父进程调用 wait() 时没有终止的子进程,父进程将被阻塞,暂停执行,直到有子进程终止。
非阻塞模式:可以使用 WNOHANG 标志来设置非阻塞模式,这样父进程在没有终止的子进程时不会被阻塞,而是立即返回。
多子进程管理:wait() 可以处理多个子进程,但返回的子进程状态是不确定的。
通过使用 wait(),父进程可以控制和监控子进程的执行,了解子进程的终止状态,并根据需要采取进一步的操作。 - exec():更改进程执行映像,进程可以使用exec()将其Umode映像更改为不同的可执行文件。
int execve(const char *filenan me, char *const argv[ ], char *const envp[ ]);
在execve()系统调用中,第一个参数文件名与当前工作目录(CWD)或绝对路径名有关。参数argv[]是一个以NULL结尾的字符串指针数组,每个指针指向一个命令行参数字符
串。按照惯例,argv[0]是程序名,其他argv[]项是程序的命令行参数。 - exit():终止当前进程的运行的,它会通知操作系统该进程已经运行完毕并要求操作系统回收该进程消耗的资源。实现过程中,当一个进程调用exit()系统调用时,操作系统在内核态中立即完成一系列清理工作,包括释放该进程占用的内存、文件描述符、套接字等资源。然后将进程状态设置为停止运行并从调度器中移除掉该进程。同时,操作系统会向父进程发送一个信号,通知父进程该子进程已经终止。最后,操作系统将进程的退出状态传递给父进程,以便父进程可以获取该状态并作出相应的处理。
苏格拉底挑战
Linux/Unix进程管理知识点苏格拉底挑战
Linux/Unix进程管理的系统调用知识点苏格拉底挑战