Linux之进程控制(上)

目录

进程创建

进程终止 

进程退出码

进程终止的方式

进程等待 

进程等待的方式

 status概述

总结


上期我们学习了Linux中进程地址空间的概念,至此进程的所有基本概念已经全部学习完成,今天我们将开始学习进程相关的操作。

进程创建

进程创建其实之前我们已经讨论过,进程创建有两种方式。

1.程序编译之后形成可执行程序,运行可执行程序,可创建进程。

 

2.使用fork函数创建子进程。

#include<stdio.h>
#include<unistd.h>
int a = 100;
int main()
{if(fork()==0){printf("#############我是子进程更改前a的值等于%d ###########\n",a);a = 200;printf("#############我是子进程更改后a的值等于%d 地址为%p \n",a,&a);}else{printf("##############我是父进程,a的值等于%d 地址为%p \n",a,&a);sleep(2);}return 0;
}

 创建子进程的目的就是让子进程去做跟父进程不一样的事情(任务)。在fork函数执行之后,会去执行fork函数之后的代码,因为子进程会继承父类的pcb,pcb中有pc指针,用于记录进程的上下文数据,正是因为如此,子进程拥有和父类一样的pc指针,而且子进程和父进程是共享代码的,所以子进程会去执行fork函数之后的代码。

进程终止 

什么是进程终止呢?进程终止其实就是进程退出,进程退出总共有三种情况。

1.代码运行完毕,结果正确。

2.代码运行完毕,结果不正确。

3.代码异常终止。 

那么既然进程退出总共有三种情况,那么我们怎样去分辨这三种情况呢?其实这就涉及到了下一个概念------进程退出码

进程退出码

直接给出进程退出码的概念。

进程退出码:一个整型变量,在进程退出时返回,返回0为代码运行完毕,结果正确;返回非0(非0有很多种可能,每一个可能为一种错误的原因)为代码运行完毕,结果不正确;当代码异常终止时,进程退出码是没有任何意义的。

通过一个场景为大家熟悉一下进程退出码。比如学校考试,进程退出码可以看做考试成绩,满分只有一种,但是非满分有很多种,用满分对应退出码为0,非满分对应退出码我为非0,当考场作弊时,分数已经没有了任何意义,对应为代码异常终止,进程退出码无意义。

进程终止的方式

进程终止有三种方式。

1.使用main函数作为返回。

#include<stdio.h>int main()
{printf("hello world!\n");return 0;
}

 上述代码大家并不陌生,大家可能经常会无脑写return 0,但是有多少人想过为什么要写return 0呢。这个return 0其实就是进程退出的标志,返回0证明代码运行结束结果正确,进程退出。可以使用echo $?查看当前进程的退出码。

当然,除过return 0 之外我们可以自己返回任意退出码。我们将return 0改为了return 1.

 2.使用exit函数进行进程退出,函数的参数为进程退出码。

#include<stdio.h>
#include<unistd.h>int main()
{printf("hello world!\n");//return 1;exit(0);
}

 

通过运行结果可知,exit可以进行进程的退出,退出码也与我们设置的一样。

3.使用_exit函数进行进程退出,函数的参数为进程退出码。 

#include<stdio.h>
#include<unistd.h>int main()
{printf("hello world!\n");_exit(1);
}

 

 通过运行结果可知,exit可以进行进程的退出,退出码也与我们设置的一样。

那么问题来了,exit函数和_exit函数都可以进行进程退出,那么它们的区别是什么呢?我们以下述代码为大家解释。

 1.exit函数。

#include<stdio.h>
#include<unistd.h>int main()
{printf("hello world!");exit(0);}

2._exit函数。

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

 

上述代码,除了函数不同之外,其它部分全部相同,为什么运行结果不一样呢,exit函数打印出了对应的结果,而_exit函数没有打印对应的结果。这究竟是为什么呢?

其实,这也就是exit函数和_exit函数的区别,因为上述打印代码都没有带‘\n’,所以在打印的时候时不会刷新缓冲区,所以要最终在显示器上打印出来,就必须刷新缓冲区。两个函数只有exit会在函数运行结束时刷新缓冲区,而_exit不会刷新缓冲区,所以就有了第一份代码打印对应的结果,而第二份代码不去打印。

进程等待 

我们知道任何进程在退出时都会先转为Z状态(僵尸态),因为僵尸态的进程是已经死亡的进程,操作系统无法杀死已经死亡的进程,所以当系统中存在大量的僵尸进程时,就会占用大量的系统资源,造成资源的浪费。那么究竟如何避免系统产生大量的僵尸进程呢?进程等待就是最好的一个解决方式。

我们知道僵尸进程的产生原因就是子进程退出时,产生大量的退出信息而父进程不对这些信息进行处理所造成的,所以要避免产生僵尸进程,就要使用进程等待的方式,让父进程去处理子进程产生的退出信息,从而避免僵尸进程的产生。

进程等待的方式

1.wait函数。

wait函数由两个返回值,返回值>0,意味着等待成功,有子进程退出,返回值为子进程的pid。返回值<0,意味着等待失败。 

参考下述代码。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if(id == 0){//childint cnt=5;while(cnt){printf("child[%d] is running,cnt is %d \n",getpid(),cnt);sleep(1);cnt--;}}else{//parentsleep(10);//父进程开始进行等待int ret=wait(NULL);if(ret>0){printf("父进程等待成功!\n");}else{printf("父进程等待失败!\n");}sleep(10);}

整个执行逻辑是,让父进程先休眠10s,在父进程休眠期间,子进程进行打印,打印完成之后,子进程退出,但是由于父进程在休眠,所以父进程不对子进程的退出信息进行处理,所以子进程变成了僵尸进程,但是当父进程睡眠10s被唤醒之后,就开始进行等待,将子进程的退出信息进行了处理,此时子进程就从僵尸态变成了死亡状态,从Z变X状态。然后只剩父进程,父进程继续休眠10s,父进程退出,然后后进程数量变成了0。运行结果如下。

运行结果符合预期。

子进程状态由S变成了Z状态,符合预期。

然后由Z变成了X状态,符合预期。

  

最终系统进程变成了0,符合预期。

2.waitpid函数。

waitpid有三个返回值,返回值>0,意味着父进程等待成功,返回值为子进程的pid,返回值=0,意味着等待成功,但是没有子进程退出,返回值<0,意味着等待失败。 

 waitpid函数的功能与wait函数的功能类似。二者的区别仅仅是参数的区别。

wait中的status和waitpid中的status是同一个参数,意义相同,所以我们重点介绍waitpid的三个参数。

第一个参数pid:当pid为-1时,意味着父进程等待任意一个子进程,即与wait函数功能一致。当pid不为-1时,则表明等待pid为当前值的进程。

第二个参数status: 一个输出型参数。

我们知道父进程等待其实就是获取子进程的退出信息,子进程有什么退出信息呢。进程退出我们知道有三种情况,前两种都是代码执行完毕,进程正常退出,所以我们用退出码可以识别进程的退出状态。最后一种是异常退出,异常退出我们用是否存在信号来识别。综上子进程退出的信息无非就有两种进程的退出码和信号。所以父进程通过这两种信息来判断子进程的退出状态。

 status概述

status可以看做是一个整型,所以总共有32个比特位,但是我们往往不关心高16位,我们只关心低16位。图示如下:

低16位中的次低8位用于存放子进程的退出码,低8位的0-6位存放信号信息,第7位是core dump标志,这个我们后期在为大家讲解。

所以父进程判断子进程是否是正常退出时,先判断信号为是否为0,如果为0意味着正常退出,则去获取退出码,判断运行结果是否正确。 

那么具体的这些退出码信息和信号信息是怎样写入status中的呢?

其实在子进程退出时,它的退出信息(退出码和信号)全部保存在了其pcb中,父进程从子进程的pcb中获取到了退出码信息和信号信息,最终让status和退出码信息和信号信息进行与操作和移位操作,使得退出码信息和信号信息存储在了status中,最终可以通过status获得子进程的退出码和信号信息。但是移位和与操作比较麻烦,操作系统给了两个函数用于获取这两个信息。WIFEXITED(status)用于判断进程是否正常退出,为真则正常退出,否则为异常退出,WEXITSTATUS(status)用于获取子进程的退出码。

 第三个参数options:即用于指定父进程是阻塞等待还是非阻塞等待。为0则为阻塞等待,为WNOHANG则为非阻塞等待。

所谓阻塞等待和非阻塞等待其实就是,在等待的过程中父进程是否可以干自己的事情。若为阻塞状态,则意味着父进程的pcb被加载到了等待队列中,父进程不能被cpu运行,所以父进程也就不能干其它的事;若为非阻塞状态,意味着父进程的pcb仍运行队列中,可以被cpu调度,所以可以执行其它事情。

代码如下。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if(id == 0){//childint cnt=5;while(cnt){printf("child[%d] is running,cnt is %d \n",getpid(),cnt);sleep(1);cnt--;}}else{//parentsleep(10);//父进程开始进行等待// int ret=wait(NULL);int status = 0;int ret=waitpid(-1,&status,0);if(ret== 0){//等待成功,但是没有子进程退出printf("父进程等待成功,没有子进程退出!\n");}else if(ret > 0){//等待成功,有子进程退出printf("等待成功,子进程pid为:%d\n",ret);}else{//等待失败printf("父进程等待失败!\n");}sleep(10);}}

 运行结果如下:

运行结果符合预期。

 

子进程有由S状态转为Z状态。符合预期。

子进程由Z状态转为X状态,只剩一个父进程,符合预期。 

父进程休眠结束,进程退出,进程数量变为0,符合预期。

总结

进程相关的操作已经基本完成,只剩下进程替换,进程替换我们下期单独来讲,因为进程替换相对比较重要。本期的主要内容为进程创建,进程退出,进程等待的操作,大家要掌握相关的概念以及实现这些操作要使用的函数接口以及每个参数的具体含义,特别是进程等待的第二个方法waitpid的参数。

本期内容到此结束^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/39256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

理解MySQL存储引擎:掌握数据存储与管理

在工作或学习过程中&#xff0c;作为一名数据库管理员或开发者&#xff0c;我们常常需处理大量数据&#xff0c;同时确保数据的可靠性与高效性。MySQL作为最受欢迎的开源数据库之一&#xff0c;其强大的性能和灵活性广为人知。而在MySQL背后的存储引擎则起到了至关重要的作用。…

强对抗的 SquidLoader 针对中国企业发起攻击

研究人员近期发现了一种高对抗强度的 Loader&#xff0c;其通过钓鱼邮件附件传递给受害者。根据恶意软件所具备的引诱和规避行为&#xff0c;研究人员将其命名为 SquidLoader。SquidLoader 最早在 2024 年 4 月下旬被发现&#xff0c;但研究人员认为其至少已经活跃了一个月以上…

Vue 数据大屏适配

1、准备俩个盒子 .dataScreen-content 盒子内容根据设计稿给的px单位进行正常的布局就行 2、盒子的CSS样式 .dataScreen-container {width: 100%;height: 100%;// 有背景图需要的样式background: url("./images/bg.png") no-repeat;background-repeat: no-repeat;b…

入门PHP就来我这(纯干货)04

~~~~ 有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 ~~~~ 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 我们接着《想入门PHP就来我这&#xff08;纯干货&#xff09;03》继续往下学习&am…

安装Rabbitmq遇到的坑

&#xff01;&#xff01;&#xff01;一定要对号版本号 不同的虚拟机unbontu、cetenos和不同的erlang和不同的rabbitmq之间要对应下载对应版本 下面给出我的版本centos7erlangrabbitmq 分割线 安装好后&#xff0c;如果在虚拟机的服务器上可以打开&#xff0c;在本地浏览器…

JavaScript中的Array(数组)对象

目录 一、Array数组对象 1、介绍 2、创建数组对象并赋值 3、访问数组元素 二、Array对象属性 1、constructor属性 2、length属性 3、prototype属性 三、Array对象的常用方法 1、isArray() 2、concat() 3、pop() 4、shift() 5、push() 6、unshift() 7、reverse(…

压缩算法LZW

LZW算法 步骤如下&#xff1a; 初始化字典&#xff1a;创建一个初始字典&#xff0c;其中包含所有可能的输入符号。 输入处理&#xff1a;读取输入数据&#xff0c;并将第一个输入符号作为当前模式。 模式匹配和字典更新&#xff1a;从输入中读取下一个符号&#xff0c;并将…

高性价比宠物空气净化器分享,希喂、霍尼韦尔、有哈PK

近期&#xff0c;家中的小猫咪仿佛化身为行走的“蒲公英”&#xff0c;掉毛现象愈发严重&#xff0c;家中每个角落乃至空气中都弥漫着难以忽视的猫毛&#xff0c;衣物更是无一幸免&#xff0c;披上了毛茸茸的“外衣”。更令人啼笑皆非的是&#xff0c;就连不经意间清理的眼屎中…

React 组件 API

React 组件 API React 组件 API 是 React 应用程序开发中的核心部分&#xff0c;它提供了一系列的接口和方法&#xff0c;使得开发者能够创建和管理组件的状态、属性以及生命周期。在本篇文章中&#xff0c;我们将深入探讨 React 组件 API 的各个方面&#xff0c;包括组件的定…

VQA视觉问答系统

这是一个典型的多模态问题,融合了CV与NLP的技术,计算机需要同时学会理解图像和文字。 Joint embedding 首先,图像和问题分别由CNN和RNN进行第一次编码得到各自的特征,随后共同输入到另一个编码器中得到joint embedding,最后通过解码器输出答案。 值得注意的是,有的工作…

k8s 答疑

1 如何修复容器中的 top 指令以及 /proc 文件系统中的信息呢? 这段自问自答的内容解释了如何通过使用 lxcfs 来修复 Docker 容器中 top 指令和 /proc 文件系统中的信息。让我们分步骤来详细说明: 背景信息 在容器化环境中,通常会遇到一个问题,即容器中的一些命令(如 to…

Java核心技术【十八】Java集合框架精讲:List、Set、Map

Java集合框架精讲&#xff1a;List、Set、Map的使用详解与代码示例 Java集合框架是Java编程中不可或缺的一部分&#xff0c;它提供了一系列容器类&#xff0c;用于存储和操作不同类型的数据集。在Java集合框架中&#xff0c;List、Set和Map是最常用的三种集合类型&#xff0c;…

小红书运营教程02

小红书大致会分享10篇左右。微博、抖音、以及视频剪辑等自媒体运营相关技能以及运营教程相关会陆续的进行分享。 上次分享涉及到的对比,母婴系列,或者可以说是服装类型,不需要自己过多的投入,对比知识类博主来说,自己将知识讲述出来,然后要以此账号进行变现就比较麻烦,…

道可云元宇宙每日资讯|山东出台人形机器人产业发展路线

道可云元宇宙每日简报&#xff08;2024年7月1日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 山东出台人形机器人产业发展路线 近期&#xff0c;山东省工业和信息化厅等部门印发《山东省促进人形机器人产业创新发展实施方案&#xff08;2024—2027年&#xff0…

如果这时你还不清理C盘,那只能眼睁睁看着电脑越来越卡 直到系统崩溃

如果这时候你还不清理C盘&#xff0c;那只能眼睁睁看着电脑越来越卡 直到系统崩溃。很多人就是想偷懒&#xff0c;当然这是人的天性&#xff0c;明明知道自己的C盘空间就那么大&#xff0c;一天天看着C盘空间越来越小&#xff0c;还不去清理C盘。 这样的人有两种&#xff0c;一…

CesiumJS【Basic】- #048 绘制闪烁线(Primitive方式)

文章目录 绘制闪烁线(Primitive方式)- 需要自定义着色器1 目标2 代码2.1 main.ts绘制闪烁线(Primitive方式)- 需要自定义着色器 1 目标 使用Primitive方式绘制闪烁线 2 代码 2.1 main.ts import * as Cesium from cesium;const viewer = new Cesiu

软件开发案例参考

前言&#xff1a;基于平台现有需求进行新功能模块开发与实现&#xff0c;以下内容为部分源码解析&#xff0c;仅提供一些思路参考&#xff0c;不予以客观指导&#xff0c;毕竟条条大路通罗马嘛&#xff1b; 语言&#xff1a;C# 工具&#xff1a;visual studio 2017/visual st…

【MySQL List插入】MySQL List格式数据插入

其中id为对应的函数名&#xff0c;useGeneratedKeys是否主键自动生成&#xff0c;keyProperty主键关联的属性。 <foreach collection"list" item"element" index"index" separator",">确定集合类型&#xff0c;item每个元素表示…

C++基础(二):C++入门(一)

C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式 等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本篇博客主要目标&#xff1a; 1. 补充C语言语法的不足&#xff0c;以及C是如何对C语言设计…

【RabbitMQ实战】邮件发送(直连交换机、手动ack)

一、实现思路 二、异常情况测试现象及解决 说明:本文涵盖了关于RabbitMQ很多方面的知识点, 如: 消息发送确认机制 、消费确认机制 、消息的重新投递 、消费幂等性, 二、实现思路 1.简略介绍163邮箱授权码的获取 2.编写发送邮件工具类 3.编写RabbitMQ配置文件 4.生产者发起调用…