[Linux]进程控制

news/2024/10/22 18:26:51/

[Linux]进程控制

文章目录

  • [Linux]进程控制
    • 进程退出情况分类
    • 进程退出码的理解
    • 进程退出方式
    • 进程等待

进程退出情况分类

  • 进程正常执行完成
    • 运行结果正确
    • 运行结果错误
  • 进程异常终止 – (进程产生错误后,收到了操作系统的信号)

进程退出码的理解

进程主体功能执行完毕后会(return)返回退出码,进程正常执行完成后,根据运行结果的不同会返回不同的退出码,不同的退出码代表不同的含义,其中0退出码代表运行结果正确,其他数字分别代表一种运行错误结果。通过查验退出码可以得知,进程运行情况。

查看C语言的退出码含义

C语言库函数遵循C语言的退出码标准,使用C语言的库函数strerror函数可以将C语言标准中的退出码转换成说明其情况的字符串,编写如下代码查看:

#include <stdio.h>
#include <string.h>int main()
{int i = 0;for (i = 0; i < 150; i++)//150并非准确的退出码个数,为预估值{printf("%d -> %s\n", i, strerror(i));}return 0;
}

编译代码执行程序查看结果:

image-20230828103023569

查看进程退出码

Linux系统中echo $?可以查看最近一次退出的进程的退出码:

image-20230828153143673

ls进程遵守C语言标准的返回码,因此在文件不存在时返回码是2。

进程退出方式

  1. 调用_exit函数

_exit函数是Linux操作系统提供的系统接口,用户可以调用该系统调用接口让操作系统退出进程,该函数所处的头文件及参数列表如下:

image-20230828160542400

_exit的参数会作为进程退出的退出码,编写如下代码测试:

#include <stdio.h>
#include <unistd.h>int main()
{_exit(123);return 0;
}

编译代码执行程序查看结果:

image-20230828160936074

说明: _exit函数无论在任何位置调用都能退出进程。

  1. 调用exit函数

exit函数是C语言提供的库函数,用户可以调用该库函数让操作系统退出进程,该函数所处的头文件及参数列表如下:

image-20230828161328625

exit的参数会作为进程退出的退出码,编写如下代码测试:

#include <stdio.h>
#include <unistd.h>int main()
{exit(222);return 0;
}

编译代码执行程序查看结果:

image-20230828161539175

说明: exit函数无论在任何位置调用都能退出进程。

_exit函数和exit函数的区别

exit函数是C语言的库函数,库函数的实现要依赖于开发环境的具体实现,由于只有操作系统有退出进程(释放pcb+代码和数据)的权利,因此exit函数是封装系统接口_exit函数实现的,此外exit函数还会执行用户定义的清理函数,冲刷缓冲区,关闭流等操作。如下图:

image-20230828162246361

为了验证exit函数和_exit函数的区别,首先编写如下代码:

#include <stdio.h>
#include <unistd.h>int main()
{printf("hello world");sleep(2);_exit(123);return 0;
}

编译代码执行程序查看结果:

_exit代码演示

然后编写如下代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{printf("hello world");sleep(2);exit(222);return 0;
}

编译代码执行程序查看结果:

exit代码演示

可以看到调用_exit的进程由于没有冲刷缓冲区,导致最终输出没有打印到屏幕上,而调用exit的进程冲刷了缓冲区,使得输出打印到了屏幕上。

  1. main函数中调用return

return是一种更常见的退出进程方法。在main函数中执行return(n)等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做exit的参数。

进程等待

进程等待是调用系统接口,释放僵尸状态的子进程所占的内存资源,并且接收进程的退出码和退出信号。

wait函数

wait函数会一直等待直到子进程退出进入僵尸状态,然后将子进程回收。该函数的头文件和参数列表如下:

image-20230828165005257

  • 参数列表为NULL的功能只有回收自己成所占的内存资源。
  • 参数为输出型参数,接收子进程的退出码和退出信号。
  • 等待成功返回等待到的子进程的id,等待失败返回-1。

为了验证wait函数的作用,编写如下代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程int cnt = 5;while(1){printf("我是子进程,我还能活%dS,我的pid是:%d, 我的ppid是:%d\n", cnt--, getpid(), getppid());sleep(1);if (cnt == 0) exit(123);}}//父进程sleep(10);pid_t ret_id = wait(NULL);printf("我是父进程,我等待子进程成功,我的pid:%d, 我的ppid:%d, ret_id:%d\n", getpid(), getppid(), ret_id);sleep(3);return 0;
}

编译代码执行程序查看结果:

wait函数演示

在子进程执行5秒后退出进入了僵尸状态,再然后父进程调用了wait函数回收了子进程。

waitpid函数

wait函数会等待子进程退出进入僵尸状态,然后将子进程回收。该函数的头文件和参数列表如下:

image-20230828172628033

  • 参数pid

    • Pid=-1,等待任一个子进程。与wait等效。
    • Pid>0.等待其进程ID与pid相等的子进程。
  • 参数status

    • 输出型参数,接收等待进程的退出码和退出信号。
  • 参数options

    • WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
    • 0:若pid指定的子进程没有结束,一直等待直到指定的子进程结束。
  • 返回值

    • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
    • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
    • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数status的结构示意图如下:

image-20230828173304524

status中的次低8位(倒数第9位到倒数第16位)二进制数转化成十进制就是进程退出码,status中低7位二进制数转换成十进制就是进程退出信号。(wait函数接收的status参数结构相同。)

为了验证waitpid函数的作用,编写如下代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程int cnt = 5;while(1){printf("我是子进程,我还能活%dS,我的pid是:%d, 我的ppid是:%d\n", cnt--, getpid(), getppid());sleep(1);if (cnt == 0) exit(123);}}//父进程int status = 0;waitpid(id, &status, 0);printf("我是父进程,我等待子进程成功,我的pid:%d, 我的ppid:%d, ret_code:%d, ret_signal:%d\n", getpid(), getppid(), (status>>8)&0xFF, status&0x7F);return 0;
}

编译代码执行程序查看结果:

waitpid函数演示1

可以看出waitpid函数确实接收到了子进程的退出码123,由于是正常执行完的退出信号为0。

wait/waitpid获取退出码和退出信号的原理

在子进程的pcb中会存在存储进程退出码和退出信号的字段,子进程退出后进入僵尸状态时,pbc还保留在内存中,wait/waitpid函数只需要找到对应pid的pcb就可以获得到子进程的退出码和退出信号。

waitpid的阻塞等待和非阻塞等待

waitpid第三个参数设置为0,表示waipid采用阻塞等待的方式,也就是调用waitpid后,进程会一直等待子进程的退出,如果子进程不退出,调用waitpid的进程就会阻塞,直到子进程退出。

waitpid第三个参数设置为WNOHANG,表示waipid采用非阻塞等待的方式,也就是调用waitpid后,立刻查看进程是否退出,如果没有退出就什么都不做并返回0,如果进程退出了,就获取退出码和退出信号并释放内存空间。为了验证waitpid函数的非阻塞等待作用,编写如下代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程int cnt = 5;while(1){printf("我是子进程,我还能活%dS,我的pid是:%d, 我的ppid是:%d\n", cnt--, getpid(), getppid());sleep(1);if (cnt == 0) exit(123);}}int status = 0;while(1){pid_t ret_id = waitpid(id, &status, WNOHANG);if (ret_id < 0){perror("子进程出错\n");exit(1);}else if (ret_id == 0){printf("子进程还没退出,我再等等\n");sleep(1);continue; }else {printf("我是父进程,我等待子进程成功,我的pid:%d, 我的ppid:%d, ret_code:%d, ret_signal:%d\n", getpid(), getppid(), (status>>8)&0xFF, status&0x7F);exit(0);}}return 0;
}

编译代码执行程序查看结果:

waitpid函数演示2

使用宏获取进程退出码

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

status为wait/waitpid函数的参数。

编写如下代码测试宏获取进程退出码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程int cnt = 5;while(1){printf("我是子进程,我还能活%dS,我的pid是:%d, 我的ppid是:%d\n", cnt--, getpid(), getppid());sleep(1);if (cnt == 0) exit(123);}}int status = 0;while(1){pid_t ret_id = waitpid(id, &status, WNOHANG);if (ret_id < 0){perror("子进程出错\n");exit(1);}else if (ret_id == 0){printf("子进程还没退出,我再等等\n");sleep(1);continue; }else {if(WIFEXITED(status)){printf("进程正常退出,code:%d\n", WEXITSTATUS(status));exit(0);}else {printf("进程异常退出,signal:%d\n", status&0x7F);exit(0);}}}return 0;
}

编译代码执行程序查看结果:

宏获取退出码


http://www.ppmy.cn/news/1072624.html

相关文章

Rn实现省市区三级联动

省市区三级联动选择是个很频繁的需求&#xff0c;但是查看了市面上很多插件不是太老不维护就是不满足需求&#xff0c;就试着实现一个 这个功能无任何依赖插件 功能略简单&#xff0c;但能实现需求 核心代码也尽力控制在了60行左右 pca-code.json树型数据来源 Administrative-d…

网络socket服务器开发几种并发模型详解

目录 一、socket创建流程。 二、I/O多路复用 三、服务器开发常见的并发模型 1、模型一:单线程——无IO复用 1.1 模型分析 2、模型二:单线程accept + 多线程读写业务(无IO复用) 模型分析 3、模型三:单线程多路IO复用 模型分析 4、模型四࿱

【前端代码规范】

前端代码规范 vue3版本:【Vue&React】版本TS版本&#xff1a;【TS&JS】版本vite版本&#xff1a;【Webpack&Vite】版本Eslint版本:命名规则:【见名识意】项目命名&#xff1a;目录命名&#xff1a;JS/VUE文件CSS/SCSS文件命名&#xff1a;HTML文件命名&#xff1a;…

嵌入式linux设备网口带宽-测试方法

iperf是一个基于Client/Server的网络性能测试工具&#xff0c;可以测试TCP、UDP和SCTP带宽质量&#xff0c;能够提供网络吞吐率信息&#xff0c;以及震动、丢包率&#xff0c;最大段和最大传输单元大小等统计信息&#xff0c;帮助我们测试网络性能&#xff0c;定位网络瓶颈。其…

Python爬虫基础:使用Scrapy库初步探索

Scrapy是Python中最流行的网页爬虫框架之一&#xff0c;强大且功能丰富。通过Scrapy&#xff0c;你可以快速创建一个爬虫&#xff0c;高效地抓取和处理网络数据。在这篇文章中&#xff0c;我们将介绍如何使用Scrapy构建一个基础的爬虫。 一、Scrapy简介及安装 Scrapy是一个用…

Java 语言哈希查找算法实现

Java 语言哈希查找算法实现 介绍 哈希查找&#xff0c;也称为散列查找&#xff0c;是一种高效的查找算法。它利用哈希函数将关键字映射到数组中的一个位置&#xff0c;通过直接访问该位置来获取元素&#xff0c;从而实现快速查找。Java语言提供了一些类和接口&#xff0c;例如…

【uniapp 配置启动页面隐私弹窗】

为什么需要配置 原因 根据工业和信息化部关于开展APP侵害用户权益专项整治要求&#xff0c;App提交到应用市场必须满足以下条件&#xff1a; 1.应用启动运行时需弹出隐私政策协议&#xff0c;说明应用采集用户数据 2.应用不能强制要求用户授予权限&#xff0c;即不能“不给权…

Git 简单介绍

Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 一、Git 安装 windows安装&#xff1a;进入网站 https://git-scm.com/ 安装&#xff0c;ubuntu配置&#xff1a;apt install git。当前于 Win 下已安装 Git 版本 2.40.1。 二、配置 设…