进程控制

什么是进程
进程:是程序的一次执行,是系统进行资源分配和调度的基本单位。
程序与进程
-
进程是正在运行的程序的实例
-
进程是动态的、永存的;程序是静态的、暂时的
-
1 个程序可以对应多个进程,但 1 个进程只能对应 1 个程序
-
进程存在并发性和独立性,没有建立 PCB 的程序不能参与并发执行和独立执行。
进程创建
Linux (UNIX) 中使用 fork() 函数创建一个子进程,子进程与父进程同时执行。
由于在子进程创建时复制了父进程的堆栈段,所以两个进程都停留在 fork 函数中等待返回,
所以当 fork 函数在父进程被调用一次,会返回两次(父子进程各一次),返回值有三种情况:
-
在父进程中返回子进程的 PID
-
在子进程中返回 0
-
创建失败返回 -1
例1:
|
|
输出结果 ab 、 ba 随机出现。
Q: 为什么会有两个结果?
A: 调用
fork函数后会产生一个新进程,此时程序中有父子两个相互独立的进程,程序执行一次,两个进程各执行一次,产生了两个结果。Q: if - else 起什么作用?
A: if - else 只是区分父子进程的执行内容,
fork后父子进程分别进入判断,执行对应内容。Q: 父子进程的执行顺序如何?
A: 执行程序时,父进程首先启动,执行到
pid = fork()会创建一个子进程,此时父子进程并发执行,父子进程的执行顺序由内核的进程调度算法决定,具有随机性。
例2:在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符;父进程显示字符 a,子进程分别显示字符 b 和 c 。
|
|
结果为 bac 或 bca 或 acb 或 abc
该程序先后创建了两个进程,最终有三个进程并发执行,输出结果有三个。
进入 if 判断,如果满足 p1 = 0,则是子进程 1,子进程 1执行输出
b,如果满足 p1 > 0为父进程部分,并创建子程序 2。如果满足 p2 = 0,则是子程序 2,子程序2执行输出
c,如果 p2 > 0 则是父进程,父进程执行输出a。综上,父进程
a创建了两个子进程b和c(通过输出各自的父子进程PID,可知b和c的父进程PID都为a的PID)。由于创建进程所需的时间可能多于输出字符的时间,在创建第二个进程之前,子进程 1可能已经执行完毕,所以
b首先输出(不一定,父进程和子进程 1 输出顺序有随机性)。以上父子进程的执行顺序由内核的进程调度算法决定,也可理解为父子进程抢夺系统资源,所以并发执行中a和b、c的输出先后顺序具有随机性。
进程控制
例3:在例 2 的基础上修改,将每个进程的输出由单个字符改为一句话,再观察程序执行的结果。
|
|
结果:
|
|
或
|
|
Q: 例 3 结果与例 2 有什么异同?
A: 例 2 是单字符输出,例 3 循环输出字符串,但是每次
printf输出字符串时不会被中断,因此字符串内部字符顺序输出不变。由于并发执行的父子进程调度顺序和抢占资源的问题,执行输出的顺序与例 2 一样存在随机性。Q: 起始 i 值为什么不是 0?
A: 由于输出行数过多,终端只会输出后面部分,如果循环次数为 10、50 等小数目,终端能从 0 开始完整显示(测试在
vscode调试环境下,如果在 CLI 的 gcc 编译运行能完整输出)。若将循环次数改为 5,结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15daughter 0 daughter 1 daughter 2 daughter 3 daughter 4 son 0 parent 0 son 1 parent 1 son 2 parent 2 parent 3 son 3 parent 4 son 4
例4:使用系统调用 lockf() 来给每个程序加锁,观察并分析结果。
|
|
结果:
|
|
Q: 例 3 结果与例 4 有何异同?
A: 加锁前和加锁后输出的内容基本一致,而且
parent、daughter、son每块的输出顺序同样存在随机性,不过加锁后每块进程的执行不会被打断,即执行完一个进程再去执行另外一个进程。Q:
lockf()有何作用?用法如何?A:
lockf(files,function,size),其中,file为文件描述符,1 表示标准输出设备,function是锁定和解锁:1 表示加锁,0 表示解锁;size是锁定或者解锁的字节数,一般为 0,表示从文件的当前位置到文件尾。给进程加锁后,会将标准输出设备锁住,其他进程无法获取资源输出,在该进程输出语句执行完后,将标准输出设备解锁,其他进程才可输出。这样就可防止父子进程竞争输出资源,实现进程之间的互斥。Q: 解锁条件有哪些?
A: 调用
lockf函数解锁,或者等当前加锁的进程结束后自动释放锁。
进程树创建
例5:创建进程树,在每个进程中显示当前进程识别码和其父进程识别码。
|
|
|
|
结果:
|
|
进程树:
1 2 3 4a PID = 3918 └── b PID = 3923 └── c PID = 3924 └── d PID = 3925
b的父进程 PID 是a的 PID,所以a和b是父子关系,而c的父进程 PID 是b的 PID,即b和c是父子关系,这样就可以构建成一个由abc组成的进程树,a就是b,c的父进程。abcd进程树的创建同理。