第三章学习笔记
本章讨论了Unix/Linux中的进程管理;
介绍了进程的概念;
多任务处理原则和多任务处理的基本系统,并给出了实例与代码,稍后将会实现之;
进程同步的概念与基本运行原理;
MT系统中的进程管理;
Unix/Linux系统进程的来源;
进程管理的系统调用;
I/O重定向和管道相关的内容。
1、概念介绍
多任务处理
计算机技术概念中的多任务处理指的是同时执行若干独立任务。无论是在多处理机系统还是单处理机系统都可以实现多任务处理。对于单处理机系统,多任务处理的实现依靠着多路复用技术,通过上下文的快速切换实现逻辑上的多任务并行处理。这种并行性被称为并发。
进程的概念
进程是对映像的执行。一个进程是一个对资源的动态利用的过程,系统内核通过一个独特的数据结构来表示,它被称为进程控制块PCB或任务控制块TCB。在本章中直接称之为PROC结构体。以下是PROC的一般定义:
其中,next是指向下一个PROC结构体的指针;
ksp字段是保存的堆栈指针,存储了放弃使用CPU的进程的上下文;
pid标识一个进程的ID编号;
ppid为父进程的编号;
status是进程的当前状态;
priority是进程的调度优先级;
kstack是进程执行时的堆栈。
2、多任务处理系统
多任务处理系统,简称MT,具有如下格式:
type.h文件,定义了系统常数和简单的PROC结构体:
/*********** type.h file ************/
define NPROC 9 //numbers of PROC
define SSIZE 1024 //stack size = 4KB
// PROC status
define FREE 0
define READY 1
define SLEEP 2
define ZOMBIE 3
typedef struct proc{
struct proc *next; //next proc pointer
int *ksp; //saved stack pointer
int pid; //pid = 0 to NPROC-1
int ppid; //parent pid
int status; //PROC status
int priority; //scheduled priority
int kstack [SSIZE]; //process stack
}PROC;
ts.s文件,32汇编代码,用于实现上下文切换:
-------------- ts.s file ----------------
.globl running,scheduler, tswitch
tSwitch:
SAVE: pushl %eax :
pushl %ebx
pushl %ecx
pushl %edx
pushl %ebp
pushl %esi
pushl %edi
pushf1
movl running, Sebx
mov1 # esp,4(%ebx)
FIND: call scheduler
RESUME: movl running,8ebx
movl 4(%ebx),%esp
popf1
popl %edi
popl %esi
popl %ebp
popl %edx
popl %ecx
popl %ebx
popl %eax
ret
stack contents=|retPC|eax|ebx|ecx|edx|ebp|esi|edi|eflag|
-1 -2 -3 -4 -5 -6 -7 -8 -9
queue.c文件,可实现队列和链表操作函数:
/******************************* queue.c file *******************************/
int enqueue(PROC **queue,PROC *p)
{
PROC *q = *queue;
if(q == 0 || p->priority> q->priority){
*queue = p;
p->next = q;
}
else{
while(g->next && p->priority <= q->next->priority)
q = q->next;
p->next = q->next;
q->next = p;
}
}
PROC *dequeue (PROC **queue)
{
PROC *p = *queue;
if (p)
queue =(queue)->next;
return p;
}
int printList(char *name,PROC *p)
{
printf("%s = ",name);
while(p){
printf("[8d %d]->",p->pid,p->priority);
p = p->next;
}
printf("NULL\n");
}
t.c文件,定义MT系统数据结构、系统初始化代码和进程管理函数:
/*********** t.c file of A Multitasking System ***********/
include <stdio.h>
include "type.h"
PROC proc[NPROC]; //NPROC PROCs
PROC *freeList; //freeLists
PROC *readyQueue;
PROC *running;
include "queue.c"
/***********************************************************
kfork() creats a child process and returns a chile pid,
When scheduled to run , child PROC resumes to body;
***********************************************************/
int kfrok()
int kexit() //to exit
int do_kfork()
int do_switch()
int do_exit()
int body() //prcdess body function
int init()
int main()
int scheduler()
{...}
3、进程同步与终止
睡眠模式
顾名思义,当进程需要某些当前没有的东西时就会进入睡眠模式,在PROC结构体添加event字段即可实现休眠功能:
typedef struct proc{
...
int event;
int exitevent;
struct proc *child;
struct proc *sibling;
struct proc *parent;
...
}PROC
唤醒操作
当等待的东西准备好后,就需要等待者醒来,进行下一步操作,调用kwakeup()就可以唤醒进程。
进程终止
在操作系统中,进程会终结或死亡,即进程终止。进程终止有两种方式:
正常终止:调用结束函数exit(value)进行终止,由进程自身操作。
异常终止:进程因为某个信号而异常终止,最终会调用kexit():
- 进程家族树
进程家族树是PROC中指向进程的兄弟进程和父进程的指针集合:
PROC *child , *siblings , *parent;
它们构成了一个进程树或二叉树,借此可以方便的找到进程的兄弟进程和父进程。
等待子进程终结
任何时候,进程都可以调用内核函数:
pid = kwait(int *status);
此内核函数能等待僵尸进程,并将其内存空间释放。
当进程被终结时,它会发出
kwakeup(running -> parent);
来唤醒父进程。
当一个进程死亡时,其所有子进程都会成为INIT进程P1的子进程,P1进程的功能如下:
它是除了P0之外所有进程的祖先,所有用户进程的始祖,所有登录进程都是它的子进程。
它管理所有没有父进程的进程,所有失去父进程的子进程都会认它为父进程。
寻找并处理所有已死亡的僵尸进程,释放系统内存。
4、MT系统中的进程管理
MT系统管理函数的一般格式:
用二叉树实现的进程家族树
实现 ksleep() 和 kwakeup() 进程同步函数
实现 kexit() 和 kwait() 进程管理函数
添加 "w" 命令来进行测试或演示操作
5、Unix/Linux中的进程
进程的来源
当操作系统启动时,操作系统内核会强行创建一个 PID=0 的初始进程,即通过分配 PROC 结构体 (通常是proc[0])来初始化 PROC 的内容,并让运行指向 proc[0] ,然后它会挂载一个根文件系统,使得系统可以使用文件。在初始化后P0会复刻出子进程P1,并将进程切换为用户模式继续运行P1。
P1进程
P1进程也被称为INIT进程,因为P1开始运行后的执行映像被更改为了INIT程序。P1中的大部分子进程都是用来提供服务的,不进行用户交互,它们被称为守护进程。
登录进程
P1中用于用户登录的LOGIN进程,每个进程打开三个相关文件流(stdin、stdout 和 stderr),指向的是堆栈区中的FILE结构体。
进程的执行模式
Linux/Unix系统中的进程有内核模式(Kmode)和用户模(Umode)式两种。进程在两种模式下动态变化,内核模式下进程可以随意进入用户模式,但用户模式进入内核模式只有三种方法:
中断
外部设备发给CPU请求服务信号,在用户模式下的CPU将会响应该信号并中断进程,然后CPU进入内核模式处理中断。
陷阱
即错误条件,CPU识别到错误条件后会尽行异常处理,此时CPU会进入内核模式处理异常。
系统调用
系统调用只可在内核模式下使用,这将会使CPU进入内核模式。
6、I/O重定向
sh进程有三个用于终端I/O的文件流:
stdin:标准输入,文件名描述符为0
stdout:标准输出,文件描述符为1
stderr:标准错误,文件描述符为2
重定向标准输入/输出
重定向标准输入
include <fcntl.h>
close(0);
int fd=open("filename",O_RDONLY);
/*
close()关闭文件描述符0,open()系统调用打开文件
/
/ OR /
int fd=open("filename",O_RDONLY);
clode(0);
dup(fd);
/
系统调用dup(0)复制fd到数值最小的未使用文件描述符中
*/
重定向标准输出
printf("filename=%s\n",items);
/* 想要实现上述操作需要发出一个write系统调用: */
close(1);
open("filename",O_WRONLY|O_CREAT,0644);
7、管道
管道可以实现进程间的单向数据交换,每个管道有一个输入端,一个输出 端。可以从管道的读取端读取写入管道写入端的数据。
管道命令处理
命令行有如下格式:
cmd1 | cmd2
其中的"|"符号就是管道符号,cmd1的输出会变成cmd2的输入。
命名管道
又称为FIFO,管道的名称以某种文件形式存储在文件系统之中,一旦命名后就会一直存在,直到使用rm或unlink将其删除。一个实例:
mknod mypepe p
或者:
int r=mknod("mypipe",S_IFIFO,0);
以上两种命令都可以在当前目录下创建一个名为mypipe的特殊文件,使用ls命令就可看到该文件,使用如下命令:
ls -l mypipe
即可显示管道链接的详细信息:
prw-r-r- 1 root root 0 time mypipe
文件类型p表示它是管道,连接数是1,大小为0。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!