笔记
第三章 Unix/Linux进程管理
多任务处理
多任务处理指同时执行几个独立的任务。在单处理器(单CPU)系统中,一次只能执行一个任务。多任务处理是通过在不同任务之间多路复用CPU的执行时间来实现的,即将CPU执行操作从一个任务切换到另一个任务,不同任务之间的执行切换机制称为上下文切换,将一个任务的执行环境更改为另一个任务的执行环境。逻辑并行性称为“并发”。如果切换速度足够快,就给人一种同时执行所有任务的错觉,这种逻辑并行性称为“并发”。在有多个CPU或处理器内核的多处理系统中,可在不同的CPU上实时、并行执行多项任务。
进程的概念
操作系统是一个多任务处理系统。在操作系统中,任务也成为进程。
进程的正式定义:
- 进程是对映像的执行
在操作系统内核中,每个进程用一个独特的数据结构表示,叫做进程控制块(PCB)或任务控制块(TCB),可直接称为PROC结构体,它包含某个进程的所有信息。
多任务处理系统
type.h文件
- 定义了系统常数和表示进程的简单PROC结构体。
ts.s文件
- 在32位GCC汇编 代码中可实现进程上下文切换。
queue.c文件
- 可实现队列和链表操作函数
- enqueue()函数:按优先级将PROC输入队列
- dequeue()函数:返回从队列或链表中删除的第一元素
- printList()函数:打印链表元素
t.c文件
- 定义MT系统数据结构、系统初始化代码和进程管理函数。
进程同步
进程同步是指控制和协调进程交互以确保其正确执行所需的各项规则和机制。
睡眠模式:
- 某进程需要某些当前没有的东西,如申请独占一个存储区域、等
待用户输入字符等。- 实现方法:在PROC结构体中添加event字段,实现ksleep(int event)函数
唤醒操作:
- 多个进程可能会进入休眠状态等待同一个事件,当某个等待时间发生时,另一执行实体(可能是进程或中断处理程序)会调用kwakeup(event)函数来唤醒处于休眠状态等待该事件值的所有程序。
进程终止
正常终止:
- 进程调用exit(value),发出_exit(value)系统调用来执行在操作系统内核中的kexit(value)
异常终止:
- 进程因某个信号而异常终止
kexit()算法
- 在Unix/Linux中,进程只有非常松散的父子关系,,进程可能随时死亡。大多数大型操作系统内核通过维护进程家族树来跟踪进程关系。
进程家族树
- 进程家族树通过PROC结构中的一对子进程和兄弟进程指针以二叉树的形式实现,如:
PROC *child,*sibling,*parent;
等待子进程终止
- 在任何时候,进程都可以调用内核函数来等待僵尸子进程:
pid = kwait(int *status) //成功则返回僵尸子进程的pid,没有子进程则返回-1
- 当进程终止时,将唤醒父进程:
kwakeup(running->parent);
MT系统中的进程管理
- 用二叉树的形式实现进程家族树
- 实现ksleep()和kwakeup()进程同步函数
- 实现kexit()和kwait()进程管理函数
- 添加“W”命令来测试和演示等待操作
Unix/Linux中的进程
进程来源
- 当操作系统启动时,操作系统内核的启动代码会强行创建一个PID=0的初始进程,即通过分配PROC结构体(通常是proc[0])进行创建,初始化PROC内容,并让运行指向proc[0]。然后,系统执行初始进程P0。
- 初始化系统后,P0复刻出一个子进程P1,并把进程切换为以用户模式运行P1。
INIT和守护进程
- 当进程P1开始运行时,它将执行映像更改为INIT程序,P1就是INIT进程,因为它的执行映像是init程序,P1开始复刻出许多子进程。
- P1的大部分子进程都是用来提供系统服务,在后台运行,不与任何用户交互,被称为守护进程。
登录进程
- P1还复刻了许多LOGIN进程,每个终端上一个,用于用户登录。每个LOGIN进程打开三个与自己的终端相关联的文件流。
sh进程
- 用户成功登录是,LOGIN进程获取用户的gid和uid,从而成为用户的进程,它将目录更改为用户的主目录并执行列出的程序。除了简单的命令之外,sh还支持I/O重定向和通过管道连接的多个命令。
进程的执行模式
进程以内核模式(Kmode)和用户模式(Umode)两种模式执行。
Umode进程只能通过以下三种方式进入Kmode:
- 中断
- 陷阱
- 系统调用
进程管理的系统调用
fork()
fork()创建子进程并返回子进程的pid,如果失败则返回-1。
int pid = fork();
进程执行顺序
fork()完成后,运行哪个进程取决于它们的优先级。
进程终止
正常终止:
pid = wait(int *status);
异常终止:
kill -s signal_number pid
等待子进程终止
int pid = wait(int *status);
int pid = waitpid(int pid,int *status,int options);
subreaper进程
prct1(PR_SET_CHILD_SUBREAPER);(这样,init进程P1将不再是孤儿进程的父进程)
exec()
更改进程执行映像:
int execve(const char *filename,char *const argv[],char *const envp[]);
环境变量
- SHELL:指定将解释任何用户命令的sh
- TERM:指定运行sh时要模拟的终端类型
- USER:当前登录用户
- PATH:系统在查找命令时将检查的目录列表
- HOME:用户的主目录
I/O重定向
stdin:标准输入
重定向:
int fd = open("filename",O_RDOMLY);
close(0);
dup(fd);
stdout:标准输出
重定向:
close(1);
open("filename",O_WRONLY|O_CREAT,0644);
stderr:标准错误
管道
管道是用于进程交换数据的单向进程间通信通道,有一个读取端和一个写入端。
- 管道编程
int pd[2];
int r = pipe(pd);
- 管道命令处理
ps x |grep "httpd"
cat filename | more
- 命名管道—FIFO
sh中:
mknod mypipe p
C程序中:
int r = mknod("mypipe",S_IFIFO,0);
实践截图
fork()函数: