(二)Linux系统编程之进程

fork()

int fork(void)

fork()函数有两个返回值:

(1)父进程返回子进程的PID
(2)子进程返回0

其中:
父进程和子进程的进程ID不一样

用户区数据一样(栈、堆、.text、.bss、.data、动态库加载区、env环境变量、命令行参数)

地址空间一样

.text(代码段)一样:子进程创建成功之后,代码的执行的位置是【父进程执行到了哪儿,子进程就从哪里开始执行】
子进程通常执行fork()之后的语句

父子进程的执行顺序?
不一定,谁先抢到cpu谁先执行(关键看系统的进程调度策略)

循环创建多个子进程:
在这里插入图片描述

在这里插入图片描述
即返回pid==0时,就是其子进程,那么不再fork()

getpid()

获取本进程的进程id

getppid()

获取本进程的父进程的id

进程间的数据共享

在这里插入图片描述

exec函数族

1.exec函数族

让父子进程执行不相干的操作

能够替换进程地址空间中的源代码.txt段

当前程序中调用另外一个应用程序
首先想到exec之前fork

返回值:
如果函数执行成功,不返回
否则执行失败,打印错误信息,退出当前进程
在这里插入图片描述
(1)换核不换壳:
核:原来的堆区、栈区中的存储空间(用户区)发生变化
壳:原来对应的地址空间,没有发生变化,但是地址空间中的源代码发生变化

(2)执行一个另外的程序不需要创建额外的地址空间(子进程对应的地址空间)

(3)有一个运行的程序a,在a中调用另外的应用程序b

2.执行指定目录下的程序

int execl(const char *path,const char *arg,…)

  • path:要执行的程序的绝对路径
  • 变参arg:要执行的程序的需要的参数
  • 第一arg:占位
  • 后面的arg:命令的参数
  • 参数写完之后:NULL
  • 一般执行自己写的程序

execl("/bin/ls","-lah",NULL);
exec("/root/文档/fork.out",“fork.out”,NULL);

2.执行PATH环境变量能够搜索到的程序

int execlp(const char *file,const char *arg,…)

  • file:执行的命令的名字
  • 第一arg:占位
  • 后面的arg:命令的参数
  • 参数写完之后:NULL 【哨兵】
  • 执行系统自带的程序
    /bin 只需要需要指定命令即可不需要指定路径,系统会自己到环境变量去找
    通常是调用程序自带的程序
    execlp执行自定义的程序:file参数绝对路径

int execvp(const char *file,char *const argv[]);

  • 参数:

进程回收

1.孤儿进程

父进程先死,子进程还活着。

一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程就成为了孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

为了释放子进程占用的系统资源:
(1)进程结束之后,能够释放用户区的空间
(2)释放不了pcb,必须由父进程释放

2.僵尸进程

子进程死了,父进程还活着,父进程不去释放pcb

一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或者waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

僵尸进程是一个已经死掉的进程。(僵尸进程实质上是一种数据结构)

kill只能杀死活的进程,因此无法杀死僵尸进程。
因此释放僵尸进程的方法是杀死父进程,僵尸进程将被收养,收养之后,操作系统能够具有自动释放资源的功能,但是如果需要自己释放资源需要比较复杂的释放资源的代码。

僵尸进程的解决办法:

(1)通过信号机制
子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。

(2)fork两次
原理时将子进程称为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。

https://www.cnblogs.com/Anker/p/3271773.html

3.进程回收

wait - 阻塞函数
pid_t wait(int* status);

函数作用:
①阻塞并等待子进程退出
②回收子进程残留资源
③获取子进程结束状态(退出原因)

返回值:
-1:回收失败,已经没有子进程了
>0:回收是子进程对应的pid

参数:status
判断子进程是如何死的。
(1)正常退出
(2)被某个信号杀死了

子进程退出状态–传出参数
调用一次只能回收一个子进程
1、WIFEXITED(status):为非0 -->进程正常结束
WEXITSTATUS(status):
如上宏为真,使用此宏 --> 获取进程退出状态(exit/return)的参数

2、WIFSIGNALED(status):为非0 --> 进程异常终止
WTERMSIG(status):如上宏为真,使用此宏 --> 取得使进程终止的那个信号的编号。

waitpid
pid_t waitpid(pid_t pid,int *status,int options);

函数作用:同wait函数(调用一次只能回收一个进程,那么可以选择其中一个子进程进行回收)
所有的进程都想回收的话:使用while循环,pid的参数为-1或者0。

参数:
①pid:

pid==-1 等待任一子进程。与wait等效。
循环回收,
while((wpid=waitpid(-1,&status,xx)!=-1))
在这里插入图片描述
【父进程A的子进程4由进程B领养,那么通过父进程通过进程组就无法回收该子进程4,只能由进程B回收】

pid>0 等待其进程ID与pid相等的子进程。

id==0 等待其组ID等于调用进程的组ID的任一子进程

pid<-1 等待其组ID等于pid的绝对值的任一子进程
子进程的pid取反(加减号)
在这里插入图片描述
②status:子进程的退出状态,用法同wait函数

③options:
WNOHANG—函数非阻塞;
0—函数阻塞

返回值:
>0:返回清理掉的子进程ID;
-1:无子进程
如果为非阻塞:
=0:子进程处于运行状态

=0:参3为WNOHANG,且子进程正在运行
while((wpid=waitpid(-1,&status,xx)!=-1))

由于该函数是非阻塞的,判断子进程是否死的时候,若没有死,则说明回收失败:不能返回-1,说明无子进程;因此返回0,说明正在运行。

猜你喜欢

转载自blog.csdn.net/CarmenIsOK/article/details/89503606