Home Article Practice 03_进程

03_进程

2024-04-24 14:28  views:287  source:拼搏百天我要上蓝翔    

知识点1【进程的概述】1、程序和进程的区别(重要)2、单道和多道程序设计3、并行和并发的区别(重要)4、进程控
制块(PCB)5、进程的状态知识点2【进程号PID】1、获取进程号的函数2、获取父进程的ID3、获取进程组的I
D案例:获取进程号、父进程号、进程组号知识点3【创建进程fork】(重要)1、fork函数2、fork出来的子
进程和父进程之间的关系3、子进程 复制 父进程的资源(各自独立)4、父子进程同时运行5、父进程 给子进程 足够
的准备时间知识点4【特殊的进程】(了解)1、孤儿进程(无危害)2、僵尸进程(有害)3、守护进程知识点5【父进程
回收子进程的资源】1、wait函数案例:2、waitpid函数案例1:waitpid等价于wait的案例知识点
6【创建多个子进程】(重要)1、知识点引入 创建2个子进程2、防止子进程 创建孙进程多进程的重要代码知识点7【
进程的补充】(了解)1、终端2、进程组3、会话创建会话的步骤:案例1:创建一个会话4、创建守护进程知识点8【v
fork创建子进程】(了解)案例1:vfork创建子进程vfork创建的子进程 和父进程 共用一个空间。知识点
9【exec函数族】(了解)案例1:在代码中使用execl执行ls命令案例2:在代码中使用execlp执行ls
命令案例3:在代码中使用execvp执行ls命令案例4:vfork和exec配合使用知识点1【进程的概述】1、
程序和进程的区别(重要)程序 静态的 占磁盘空间进程 动态的 (调度、执行、消亡),占内存空间。(进程是程序执
行到结束间的这个过程)2、单道和多道程序设计1 单道程序设计 所有进程一个一个排队执行。若A阻塞,B只能等待,
即使CPU处于空闲状态。2 多道程序设计 在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相
互穿插的运行3、并行和并发的区别(重要)并行和并发 都是值多个任务同时执行。并行(parallel):指在同一
时刻,有多条指令在多个处理器上同时执行。(多核)并发(concurrency):指在同一时刻只能有一条指令执行
,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时
间分成若干段,使多个进程快速交替的执行(单核)4、进程控制块(PCB)进程运行时,内核为每个进程分配一个PCB
(进程控制块),维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。PCB存在于进程
的内核空间里面。系统会为每一个进程分配一个进程ID,其类型为pid_t(非负整数)进程是系统分配资源的基本单位
。5、进程的状态进程的状态:就绪态、执行态、等待态就绪态:执行条件全部满足,等待CPU的执行调度执行态:正在被
CPU调度执行等待态:不具备CPU调度执行的执行条件,等待条件满足。查看进程的状态:ps -auxstat中的
参数意义如下:参数 含义D 不可中断 Uninterruptible(usually IO)R 正在运行,或在
队列中的进程S(大写) 处于休眠状态T 停止或被追踪Z 僵尸进程W 进入内存交换(从内核2。6开始无效)X 死
掉的进程< 高优先级N 低优先级s 包含子进程+ 位于前台的进程组ps命令可以查看进程信息:选项 含义-a 显
示终端上的所有进程,包括其他用户的进程-u 显示进程的详细状态-x 显示没有控制终端的进程-w 显示加宽,以便
显示更多的信息-r 只显示正在运行的进程以树状显示进程:pstree知识点2【进程号PID】每个进程都由一个进
程号来标识,其类型为 pid_t(整型),进程号的范围:0~32767。进程号总是唯一的,但进程号可以重用。当
一个进程终止后,其进程号就可以再次使用。进程号(PID): 标识进程的一个非负整型数父进程号(PPID):父进
程号进程组号(PGID): 进程组是一个或多个进程的集合。1、获取进程号的函数1 #include <sys/
types。h>2 #include <unistd。h>3 pid_t getpid(void);功能:获取
本进程号(PID)参数:无返回值:本进程号2、获取父进程的ID1 #include <sys/types。h>
2 #include <unistd。h>3 pid_t getppid(void);功能:获取调用此函数的进
程的父进程号(PPID)参数:无返回值:调用此函数的进程的父进程号(PPID)3、获取进程组的ID1 #inc
lude <sys/types。h>2 #include <unistd。h>3 pid_t getpgid(
pid_t pid);功能:获取进程组号(PGID)参数:pid:进程号返回值:参数为 0 时返回当前进程组号
,否则返回参数指定的进程的进程组号案例:获取进程号、父进程号、进程组号1 #include <stdio。h>
2 #include <sys/types。h>3 #include <unistd。h>4 int main
(int argc, char const *argv[])5 {6 printf("当前进程的ID:%d\n
", getpid());7 printf("父进程的ID:%d\n", getppid());8 print
f("当前进程所在的进程组号:%d\n", getpgid(0));9 getchar();10 return
0;11 }知识点3【创建进程fork】(重要)1、fork函数系统允许一个进程创建新进程,新进程即为子进程
,子进程还可以创建新的子进程,形成进程树结构模型。1 #include <sys/types。h>2 #inc
lude <unistd。h>3 pid_t fork(void);功能:用于从一个已存在的进程中创建一个新进
程,新进程称为子进程,原进程称为父进程。参数:无返回值:成功:子进程中返回 0,父进程中返回子进程 ID。pi
d_t,为整型。失败:返回-1。失败的两个主要原因是:1)当前的进程数已经达到了系统规定的上限,这时 errn
o 的值被设置为 EAGAIN。2)系统内存不足,这时 errno 的值被设置为 ENOMEM2、fork出来
的子进程和父进程之间的关系使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空
间。使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。 地址空间: 包括
进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。 子进程所独有的只有它的进程号,
计时器等。因此,使用fork函数的代价是很大的。父子进程 从fork后开始继续执行。1 #include <s
tdio。h>2 #include <unistd。h>3 int main(int argc, char c
onst *argv[])4 {5 //创建子进程6 pid_t pid = fork();7 if (pid
< 0)8 {9 perror("创建失败\n");10 return 0;11 }12 else if (
pid == 0) //子进程13 {14 printf("子进程ID:%d\n", getpid());15
}16 else if (pid > 0) //父进程17 {18 printf("父进程ID:%d\n",
getpid());19 }20 getchar();21 return 0;22 }父子进程是同时运行,空
间独立,子进程复制 父进程的所有空间,谁先运行不确定。3、子进程 复制 父进程的资源(各自独立)1 #incl
ude <stdio。h>2 #include <unistd。h>3 int main(int argc,
char const *argv[])4 {5 int num = 10;6 //创建子进程7 pid_t p
id = fork();8 if (pid < 0)9 {10 perror("创建失败\n");11 ret
urn 0;12 }13 else if (pid == 0) //子进程14 {15 //在子进程中 修改n
um的值16 num = 1000;17 printf("子进程ID:%d 中num=%d\n", getpi
d(), num);18 }19 else if (pid > 0) //父进程20 {21 printf("
父进程ID:%d 中num=%d\n", getpid(), num);22 }23 getchar();24
return 0;25 }4、父子进程同时运行1 #include <stdio。h>2 #include
<unistd。h>3 int main(int argc, char const *argv[])4 {5
int num = 10;6 //创建子进程7 pid_t pid = fork();8 if (pid <
0)9 {10 perror("创建失败\n");11 return 0;12 }13 else if (pi
d == 0) //子进程14 {15 while (1)16 {17 printf("子进程ID:%d 中n
um=%d\n", getpid(), num);18 sleep(1);19 }20 }21 else if
(pid > 0) //父进程22 {23 while (1)24 {25 printf("父进程ID:%d
中num=%d\n", getpid(), num);26 sleep(1);27 }28 }29 getc
har();30 return 0;31 }5、父进程 给子进程 足够的准备时间1 #include <std
io。h>2 #include <unistd。h>3 int main(int argc, char con
st *argv[])4 {5 int num = 10;6 //创建子进程7 pid_t pid = for
k();8 if (pid < 0)9 {10 perror("创建失败\n");11 return 0;12
}13 else if (pid == 0) //子进程14 {15 printf("子进程ID:%d 中n
um=%d\n", getpid(), num);16 }17 else if (pid > 0) //父进程
18 {19 sleep(5);20 printf("父进程ID:%d 中num=%d\n", getpid(
), num);21 }22 getchar();23 return 0;24 }知识点4【特殊的进程】(了解
)孤儿进程、僵尸进程、守护进程。1、孤儿进程(无危害)父进程先结束、子进程就是孤儿进程,会被1号进程接管(1号
进程负责给子进程回收资源)1 #include <stdio。h>2 #include <unistd。h>3
int main(int argc, char const *argv[])4 {5 //创建子进程6 pi
d_t pid = fork();7 if (pid < 0)8 {9 perror("创建失败\n");10
return 0;11 }12 else if (pid == 0) //子进程13 {14 while (
1)15 {16 printf("子进程ID:%d 父进程号=%d\n", getpid(), getppid
());17 sleep(1);18 }19 }20 else if (pid > 0) //父进程21 {2
2 printf("父进程ID:%d 3秒后结束\n", getpid());23 sleep(3);24 }
25 return 0;26 }2、僵尸进程(有害)子进程结束,父进程没有回收子进程资源(PCB),子进程就是
僵尸进程。3、守护进程守护进程 是脱离终端的 孤儿进程。在后台运行。为特殊服务存在的。(一般用于服务器)知识点
5【父进程回收子进程的资源】在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是
仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的信息(包括进程号、退出状态、运行时间等)父进程可以
通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。注意:一次wait或waitpid调用
只能清理一个子进程,清理多个子进程应使用循环。wait、waitpid基本上都是在父进程调用1、wait函数1
#include <sys/types。h>2 #include <sys/wait。h>3 pid_t w
ait(int *status);功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程
的资源。参数:status : 进程退出时的状态信息。返回值:成功:已经结束子进程的进程号失败: -1注意:w
ait阻塞若调用进程没有子进程,该函数立即返回子进程已经结束,该函数同样会立即返回,并且会回收那个早已结束进程
的资源状态值:WIFEXITED(status) 如果子进程是正常终止的,取出的字段值非零。WEXITSTAT
US(status) 返回子进程的退出状态,退出状态保存在status变量的8~16位案例:1 #includ
e <stdio。h>2 #include <unistd。h>3 #include <sys/wait。h>
4 int main(int argc, char const *argv[])5 {6 //创建子进程7 p
id_t pid = fork();8 if (pid < 0)9 {10 perror("创建失败\n");
11 return 0;12 }13 else if (pid == 0) //子进程14 {15 int i
= 5;16 for (i = 5; i > 0; i‐‐)17 {18 printf("子进程ID:%d
剩余生命值%ds\n", getpid(), i);19 sleep(1);20 }2122 printf("
子进程ID:%d 退出了\n", getpid());23 //显示结束24 _exit(10);25 }26
else if (pid > 0) //父进程27 {28 printf("父进程ID:%d 等待子进程结束
\n", getpid());29 int status = 0;30 pid_t pid = wait(&s
tatus);31 if (WIFEXITED(status)) //子进程正常退出32 {33 //输出状态
值34 printf("子进程退出的状态值:%d\n", WEXITSTATUS(status));35 }3
637 printf("父进程ID:%d 等到子进程%d结束\n", getpid(), pid);38 }3
9 return 0;40 }2、waitpid函数等待子进程结束1 #include <sys/types。
h>2 #include <sys/wait。h>3 pid_t waitpid(pid_t pid, int
*status, int options);功能:等待子进程终止,如果子进程终止了,此函数会回收子进程的资源
。参数:pid : 参数 pid 的值有以下几种类型:pid > 0 等待进程 ID 等于 pid 的子进程。
pid = 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。p
id = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。pid < -1 等待指定进程组
中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。status : 进程退出时的状态信息。和 wai
t() 用法一样。options : options 提供了一些额外的选项来控制 waitpid()。0:同
wait(),阻塞父进程,等待子进程退出。WNOHANG:没有任何已经结束的子进程,则立即返回。WUNTRAC
ED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,加
之极少用到)返回值:waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:1) 当正
常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;2) 如果设置了选项 WNOHANG,而
调用中 waitpid() 还有子进程在运行,且没有子进程退出,返回0; 父进程的所有子进程都已经退出了 返回
-1; 返回>0表示等到一个子进程退出3) 如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以
指示错误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid()就
会出错返回,这时 errno 被设置为 ECHILD案例1:waitpid等价于wait的案例1 #inclu
de <stdio。h>2 #include <unistd。h>3 #include <sys/wait。h
>4 int main(int argc, char const *argv[])5 {6 //创建子进程7
pid_t pid = fork();8 if (pid < 0)9 {10 perror("创建失败\n")
;11 return 0;12 }13 else if (pid == 0) //子进程14 {15 int
i = 5;16 for (i = 5; i > 0; i‐‐)17 {18 printf("子进程ID:%d
剩余生命值%ds\n", getpid(), i);19 sleep(1);20 }2122 printf(
"子进程ID:%d 退出了\n", getpid());23 //显示结束24 _exit(10);25 }2
6 else if (pid > 0) //父进程27 {28 printf("父进程ID:%d 等待子进程结
束\n", getpid());29 int status = 0;30 pid_t pid = waitpi
d(‐1, &status, 0);31 if (WIFEXITED(status)) //子进程正常退出32
{33 //输出状态值34 printf("子进程退出的状态值:%d\n", WEXITSTATUS(sta
tus));35 }3637 printf("父进程ID:%d 等到子进程%d结束\n", getpid(),
pid);38 }39 return 0;40 }waitpid常用于 等待多个子进程结束。1 pid_t
pid = wait(&status);2 pid_t pid = waitpid(‐1, &status,
0);知识点6【创建多个子进程】(重要)1、知识点引入 创建2个子进程2、防止子进程 创建孙进程多进程的重要代
码1 #include <stdio。h>2 #include <unistd。h>3 #include <s
ys/wait。h>4 #define N 35 int main(int argc, char const
*argv[])6 {7 int i = 0;8 for (i = 0; i < N; i++)9 {10 p
id_t pid = fork();11 if (pid == 0) //防止子进程创建孙进程12 break
;13 }1415 //判断具体的子进程16 if (i == 0) //子进程117 {18 //完成任务A
19 int j = 5;20 for (; j > 0; j‐‐)21 {22 printf("子进程%d
剩余事件%ds\n", getpid(), j);23 sleep(1);24 }25 _exit(‐1);2
6 }27 else if (i == 1) //子进程228 {29 //完成任务B30 int j = 3
;31 for (; j > 0; j‐‐)32 {33 printf("子进程%d 剩余事件%ds\n",
getpid(), j);34 sleep(1);35 }36 _exit(‐1);37 }38 else i
f (i == 2) //子进程339 {40 //完成任务C41 int j = 8;42 for (; j
> 0; j‐‐)43 {44 printf("子进程%d 剩余事件%ds\n", getpid(), j)
;45 sleep(1);46 }47 _exit(‐1);48 }49 else if (i == N) /
/父进程50 {51 //回收所有子进程的资源52 while(1)53 {54 pid_t pid = wa
itpid(‐1,NULL, WNOHANG);//不阻塞55 if(pid > 0)//某个子进程退出了56
{57 printf("子进程%d退出了\n",pid);58 }59 else if(pid == 0)/
/还有子进程在运行60 {61 continue;62 }63 else if(pid == ‐1)//所有子
进程都退出了64 {65 break;66 }67 }68 }6970 return 0;71 }知识点7【进
程的补充】(了解)1、终端用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(
Controlling Terminal),进程中,控制终端是保存在PCB中的信息,而fork会复制PCB中的
信息,因此由Shell进程启动的其它进程的控制终端也是这个终端1 #include <unistd。h>2 c
har *ttyname(int fd);3 功能:由文件描述符查出对应的文件名4 参数:5 fd:文件描述符
6 返回值:7 成功:终端名8 失败:NULL2、进程组多个进程的集合当父进程,创建子进程的时候,默认子进程与
父进程属于同一进程组。进程组ID为第一个进程ID(组长进程):进程ID和进程组ID相同的进程就是 组长进程。可
以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死只要进程组中有一个进程存在
,进程组就存在,与组长进程是否终止无关。 进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组
)。1 #include <unistd。h>2 pid_t getpgrp(void); /* POSIX。
1 version */功能:获取当前进程的进程组ID参数:无返回值:总是返回调用者的进程组ID1 pid_t
getpgid(pid_t pid);功能:获取指定进程的进程组ID参数:pid:进程号,如果pid = 0
,那么该函数作用和getpgrp一样返回值:成功:进程组ID失败:-11 int setpgid(pid_t
pid, pid_t pgid)功能:改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程
组。参数:将参1对应的进程,加入参2对应的进程组中返回值:成功:0失败:-13、会话会话是一个或多个进程组的集
合。 一个会话可以有一个控制终端。如果进程ID==进程组ID==会话ID 那么该进程为会话首进程。创建会话的步
骤:1) 调用进程不能是进程组组长,该进程变成新会话首进程(session header)2) 该调用进程是组
长进程,则出错返回 。3) 该进程成为一个新进程组的组长进程4) 需有root权限(ubuntu不需要)5)
新会话丢弃原有的控制终端,该会话没有控制终端6) 建立新会话时,先调用fork, 父进程终止,子进程调用set
sid1 #include <unistd。h>2 pid_t getsid(pid_t pid);功能:获取
进程所属的会话ID参数:pid:进程号,pid为0表示查看当前进程session ID返回值:成功:返回调用进
程的会话ID失败:-11 #include <unistd。h>2 pid_t setsid(void);功能
:创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。调用了setsid函数的进程,既是新的会长
,也是新的组长。参数:无返回值:成功:返回调用进程的会话ID失败:-1案例1:创建一个会话4、创建守护进程1)
创建子进程,父进程退出(必须) 所有工作在子进程中进行形式上脱离了控制终端2) 在子进程中创建新会话(必须)
setsid()函数 使子进程完全独立出来,脱离控制3) 改变当前目录为根目录(不是必须) chdir()函
数 防止占用可卸载的文件系统 也可以换成其它路径4) 重设文件权限掩码(不是必须) umask()函数 防止继
承的文件创建屏蔽字拒绝某些权限 增加守护进程灵活性5) 关闭文件描述符(不是必须) 继承的打开文件不会用到,浪
费系统资源,无法卸载6) 开始执行守护进程核心工作(必须) 守护进程退出处理程序模型1 #include <s
tdio。h>2 #include <unistd。h>3 #include <sys/types。h>4 #
include <sys/stat。h>5 int main(int argc, char const *ar
gv[])6 {7 pid_t pid = fork();8 //父进程结束9 if (pid > 0)10
_exit(‐1);1112 //子进程设置会话13 setsid();1415 //改变工作目录(非必须)1
6 chdir("/");1718 //设置权限掩码19 umask(0002);2021 //关闭文件描述符
0 1 222 close(0);23 close(1);24 close(2);2526 //守护进程的核心
任务27 while (1)28 {29 //核心任务30 }31 return 0;32 }知识点8【vfo
rk创建子进程】(了解)vfork函数:创建一个新进程pid_t vfork(void)功能:vfork函数和
fork函数一样都是在已有的进程中创建一个新的进程,但它们创建的子进程是有区别的。返回值:创建子进程成功,则在
子进程中返回0,父进程中返回子进程ID。出错则返回-1。案例1:vfork创建子进程1 #include <s
tdio。h>2 #include <unistd。h>3 #include <sys/types。h>45
int main(int argc, char const *argv[])6 {7 //vfork创建子进程
8 pid_t pid = vfork();9 if (pid == 0) //子进程10 {11 int i
= 0;12 for (; i < 5; i++)13 {14 printf("子进程%d中的i=%d\n"
, getpid(), i);15 sleep(1);16 }17 //显示退出18 _exit(‐1);19
}20 else if (pid > 0) //父进程21 {22 int i = 0;23 for (;
i < 5; i++)24 {25 printf("父进程%d中的i=%d\n", getpid(), i);
26 sleep(1);27 }28 }29 return 0;30 }从上面运行结果得出:vfork创建的子
进程 会保证子进程先运行,只有当子进程退出(调用exec)的时候,父进程才运行。vfork创建的子进程 和父进
程 共用一个空间。知识点9【exec函数族】(了解)exec函数族:在进程中 启动另一个进程。#include
<unistd。h>extern char **environ;int execl(const char *
path, const char *arg, 。。。/* (char *) NULL */);int exec
lp(const char *file,cconst char *arg, 。。。 /* (char *) N
ULL */);int execle(const char *path, const char *arg, 。
。。/*, (char *) NULL, char * const envp[]*/);int execv(c
onst char *path, char *const argv[]);int execvp(const c
har *file, char *const argv[]);int execvpe(const char *
file, char *const argv[], char *const envp[]);int execv
e(const char *filename, char *const argv[], char *const
envp[]);函数中有l(list)表明使用列表方式传参,函数中有v(vector)表明使用指针数组传参。
函数中有p(path)表明 到系统环境中 找可执行性文件函数中有e(evn) 表明exec可以使用环境变量值案
例1:在代码中使用execl执行ls命令1 execl(可执行文件位置,可执行文件名,可执行文件的选项,以NU
LL结尾);一个进程调用exec后,除了进程ID,进程还保留了下列特征不变: 父进程号 进程组号 控制终端 根
目录 当前工作目录 进程信号屏蔽集 未处理信号 。。。案例2:在代码中使用execlp执行ls命令案例3:在代
码中使用execvp执行ls命令案例4:vfork和exec配合使用1 #include <stdio。h>2
#include <unistd。h>3 #include <sys/types。h>45 int main
(int argc, char const *argv[])6 {7 int num = 10;8 //vfo
rk创建子进程9 pid_t pid = vfork();10 if (pid == 0) //子进程11 {
12 //子进程负责启动起到程序13 sleep(3);14 execlp("ls", "ls", "‐a",
"‐l", "‐h", NULL);1516 //显示退出17 _exit(‐1);18 }19 else
if (pid > 0) //父进程20 {21 //父进程运行自己的程序22 int i = 0;23 fo
r (; i < 5; i++)24 {25 printf("父进程%d中的i=%d\n", getpid()
, i);26 sleep(1);27 }28 }29 return 0;30 }



Disclaimer: The above articles are added by users themselves and are only for typing and communication purposes. They do not represent the views of this website, and this website does not assume any legal responsibility. This statement is hereby made! If there is any infringement of your rights, please contact us promptly to delete it.

字符:    改为:
去打字就可以设置个性皮肤啦!(O ^ ~ ^ O)