【Linux】--进程控制

一、进程创建

前面我们学习进程概念的时候,已经知道了,创建进程,我们可以使用fork函数进行创建。其是在当前的进程下创建一个新的进程,新进程为子进程,原进程为父进程。

其给父进程返回子进程的pid,给子进程返回0,出错返回-1。

进程调用fork后,当控制转移到内核中的fork代码后,内核做下面几个工作:

1、分配新的内存块和内核数据结构给子进程

2、将父进程部分数据结构内容拷贝到子进程中

3、添加子进程到系统进程列表当中

对于fork的使用和现象我们前面已经讲解的很清楚了。

二、进程终止

进程终止的本质是释放系统资源,也就是释放掉进程申请的相关内核数据和对应的代码。

那么我们的进程中终止一共有几种情况呢?

一共有三种情况:

1、代码跑完,运行结果是正确的

2、代码跑完,运行结果有误

3、代码都没跑完,运行异常

首先我们看前面两种:

我们是如何判断我们的代码跑完是正确的还是错误的呢?

我们一开始学习语言的时候,在main函数也有返回值,其返回值为int类型,然后我们在main函数的末尾都会返回一个0,然后遇到一种输入错误呀啥的,都会返回1的。

其返回0,说明我们的运行是正确的。

要是返回非0的数那么说明运行错误。

然后就是代码都没走完,就发生异常,这个是如何判断的呢?

异常退出,说明我们的代码都没有运行到return,所以退出码是没有意思的,其实我们的进程管理中,还有一个是用来记录进程异常退出的码。一个是信号,信号就是异常的时候反馈的。

运行结果状态其用两个整型变量表示:

int exit_code。int exit------signal。

那么我们如何查看我们的进程退出码呢?

1、查看进程退出码

在我们的Shell中我们可以使用echo $?查看我们的上一个进程的退出码。

可以看到我们的程序运行错误的退出码是1。

2、error

这个函数我们前面也有使用过,这个是我们对于一些异常情况进行处理的函数,其通常会返回一个特殊值,然后将这个错误编码存放在一个全局变量errno中,如果我们要查看我们拿到的错误编号,那么我们要先安装moreutils,然后执行errno -l查看。

errno是一个整数,其每一个数据就代表一种特定的错误类型

其使用要包含头文件error.h

其返回值有两种情况:

返回0,说明没有问题

返回非0,就说明发生了错误

3、exit和_exit

exit是C++标准库中提供的一个函数,其是用来正常终止一个进程的。

其功能如下:

终止当前的进程,然后将状态码返回给父进程,其函数样子为:void exit(int status)。

注意:虽然我们的status是int类型,但是其只有8位bit位给其使用。'

exit和_exit的区别:

exit是C++标准库中提供的函数,_exit是系统调用,实际上exit的底层还是调用的_exit。

exit其执行退出进程的时候会将缓冲区进行I\O,刷新I\O缓冲区,然后我们的_exit的话是不会对缓冲区进行刷新的,直接就终止我们的进程。

下面是我们常见的信号状态和对应 的编号:

我们可以使用kill -l来查看:

三、进程等待

前面我们提到,进程状态,会有僵尸状态,这是因为我们的父进程先结束,然后子进程运行结束,父进程没有进行回收,获取子进程的退出信息。

所以父进程通过进程等待的方式,回收子进程的资源,获取子进程退出信息

我们在父进程中使用wait()函数或者waitpid()函数进行阻塞等待。

1、wait()

函数作用:

阻塞等待:如果子进程不退出,那么父进程会一直卡在wait这个函数中,直到接收到子进程的退出信息。

其是等待的任意一个子进程,不能指定等待某一个子进程。

其原型如下:

wait(int *status),这个指针可以穿空,就是只需要回收到子进程即可,不关心其运行状态。

其返回值类型为:pid_t,其实就是一个整型,如果等待成功,那么就返回等待到的子进程的pid,如果等待失败,那么就返回-1。

下面我们演示一下:

2、waitpid()

这个函数的功能和wait是一样的,不过其支持我们去指定子进程回收,其使用一次也是回收一个子进程。

函数原型如下:

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

第一个参数pid

传入的是-1的话,那么父进程就会等待任意一个子进程。

传入的为>0的值的话,那么其就会等待pid为这个数的子进程。

第二个参数,我们暂时将其设置为NULL

第三个参数,我们暂时将其设置为0。

下面我们演示其效果:

下面我们来具体看看其参数:

3、status

这个参数是一个int*类型的参数,其是用来接收被回收的子进程的退出状态的。

如果我们需要知道的话,那么可以创建一个int*类型的数据,获取其退出的状态。

然后要注意的是,其返回的话,是包含了退出信号和退出码的。

我们的退出信号和退出码就是其前16bit位。

那么我们要是想看子进程的退出码,那么我们只需要将status的数据右移8个bit位。

然后终止信号就在前八个bit位置,然后如果其是被信号所杀的话,那么会有一个bit位被用来做为core dump标志。所以其是7个bit位置。

然后我们的Linux下定义了两个宏来获取我们进程退出的状态的:

4、阻塞等待和非阻塞等待

阻塞等待就是我们的父进程等待子进程回收,在这个等待的期间,我们的父进程啥都不做。

非阻塞等待就是我们的父进程在等待回收子进程的时候,我们的父进程还可以去干别的事情。

选择阻塞等待还是非阻塞等待,就是我们的waitpid函数的第三个参数。

如果我们传入的是0,那么就是阻塞等待,如果传入的是WNOHANG,那么就使用的是非阻塞等待。

如下:

运行结果如下:

可以看到在等待子进程的回收中,我们的父进程也还在运行。

四、进程替换

我们前面创建了一个子进程后,父子各自执行父进程的一部分代码,如果我们的子进程想要执行一个全新的进程呢?

那么就需要用到我们的进程替换了,进程替换是不会创建新的进程的。

可以理解其是直接在这个进程内,将其后面的代码覆盖了。

我们要使用到exec系列的函数:

其替换原理如上。

这是我们的进程替换的函数,那么其使用要包含unistd头文件。

那么我们下面来对其一一讲解:

1、execl()

函数原型:

int execl(const char*path,const char *arg,.....);

第一个参数,要传入的是我们要替换的文件的绝对路径或者相对路径

第二个参数,传入我们的执行方式,命令行咋使用的,那么我们这边就怎么调用

最后一个参数,一定要传一个NULL

返回值:调用成功的话,就没有返回值,如果失败返回-1;

2、execlp()

函数原型:

int execlp(const char *file,const char*agr,...);

第一个参数:文件的绝对路径或者相对路径,然后如果这个路径在我们的PATH环境变量中的话, 那么我们就可以直接写文件名。

第二个参数:和execl是一样的,命令行咋写的,这里就咋写。

最后一个参数就必须是NULL。

3、execv()

函数原型:

int execv(const char*path,char*const argv[]);

第一个参数:文件的绝对路径或者相对路径

第二个参数:参数数组,argv[0]一般是我们要执行的文件名字或者命令,注意的是其最后一个元 素必须是NULL。

4、execvp()

函数原型:

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

第一个参数:文件的绝对路径或者相对路径,如果在环境变量PATH中有的话,那么可以只传文件 名字

第二个参数:参数数组,和上面的execv一样。

5、execle()

函数原型

int execle(const char*pathname,chonst char * arg0,.....,NULL,char *const envp[]);

第一个参数:必须是可执行文件的路径

第二个参数:新程序的命令行参数列表,注意最后一个参数一定要为NULL

第三个参数:新的环境变量,替换当前进程的环境变量。

相关推荐
l14372332672 小时前
AI解说大师narrator-ai-cli:影视解说自动化工具,CLI架构让内容生产效率翻倍
运维·人工智能·自动化
Gofarlic_oms12 小时前
构建可视化监控体系实现ANSYS许可证可观测管理
大数据·运维·网络·数据库·人工智能
千里马学框架2 小时前
Ubuntu 24 搭建aosp源码环境详细笔记
android·linux·ubuntu·framework·安卓·aosp·源码环境
wuyoula2 小时前
全新轻量级高性能跨平台 AI聊天+AI网关桌面
服务器·开发语言·c++·人工智能
倦王2 小时前
在docker下部署Xinference
运维·docker·容器
zhang133830890752 小时前
守护水工安全:CG-85D振弦式渗压计在大坝与堤防监测中的核心作用
运维·服务器·网络·人工智能·自动化
SPC的存折2 小时前
自用LNMP-Redis-NFS-Discuz5.0部署指南-脚本版
linux·运维·服务器·数据库·redis·mysql·缓存
阿火~2 小时前
docker完整镜像迁移【亲测有效】
linux·运维·服务器·docker·容器
网硕互联的小客服2 小时前
Linux root用户密码输入错误锁定策略,使用旧密码失败如何处理?
linux·服务器·网络·centos·自动化