yo!这里是进程控制

目录

前言

进程创建

fork()函数

写时拷贝 

进程终止

退出场景

退出方法

进程等待 

等待原因

等待方法

1.wait函数

2.waitpid函数

等待结果(status介绍)

进程替换

替换原理

替换函数

进程替换例子

shell简易实现

后记


前言

        学习完操作系统中进程部分的入门介绍之后,大家应该进程有了个初步了解,那么,下面就可以很好地进军进程控制部分了,包括进程创建、进程终止、进程等待、进程替换等重点部分,其中的细节很多,也比较难以理解,但是没有关系,在介绍完进程控制之后,会简单实现一个shell程序,也就是类似Xshell的一个软件,也可以执行相关命令进行各种操作,来综合理解一下四个重点部分,快往下看吧!

进程创建

  • fork()函数

        在进程入门理解章节中,我们介绍到了fork()函数,可以创建一个新进程,此进程称为子进程,原进程称为父进程,函数信息如图所示

        还知道,fork失败时返回-1,成功时有两个返回值,给父进程返回子进程的pid,给子进程返回0,所以fork()之后由此分流,使得父子进程去做不同的事。

1)fork()深入理解

        由于进程=内核数据结构+进程的代码和数据,其中内核数据结构由os搞定,而进程的代码和数据一般从磁盘来,也就是c/c++运行的可执行文件。所以fork()之后,os创建子进程,为其分配对应内核数据结构(必须子进程独有,因为进程具有独立性),理论上,子进程也要有自己的代码和数据,这如何拥有?

        对于代码,都是不可写的,所以父子共享,对于数据,可写可读,所以不能共享,必须分离。这里先针对于代码,数据的分离会在下面的写时拷贝讲到。

        见上图想一下,fork()之后,父子进程是共享after的代码还是共享所有的代码?所有的!但子进程从after那里开始执行,而不是从头开始执行。下面先提一下两个认知:

①代码汇编以后会有很多行代码,在加载到内存之后,每行代码都有自己对应的地址;

②cpu中有一个寄存器叫做EIP,也叫做pc指针、程序计数器,记录当前正在执行代码的下一行代码的地址,属于进程的上下文数据。

        创建子进程时,EIP的值无需给子进程,因为父子进程各自调度,会修改EIP,就算给了子进程也用不到,在子进程中会将after的第一行代码的地址赋给EIP,进程就从after开始执行了。

        值得注意的是,fork()之后,父子进程两个执行流分别各自执行,谁先执行完全是由调度器决定的。

2)fork()常规用处

        ①一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

        ②一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec系列函数进行进程替换。

3)fork()调用失败的原因

        ①系统中有太多的进程;

        ②实际用户的进程数超过了限制。

  • 写时拷贝 

        在上文讲到,对于代码,都是不可写的,所以父子共享,对于数据,可写可读,所以不能共享,必须分离。那如何分离呢?直接拷贝一份然后修改?

        不行!这样的话会存在子进程不会用到的空间,造成内存浪费,即使有用到的空间,也可能只是读取,所以,数据分为不会被访问或指挥读取的数据和将来会被父或子进程写入的数据,但一般而言,os无法得知哪些数据不会被访问或者只会读取亦或会被写入,所以os选择了写时拷贝技术。

        也就是说,父子进程两方都没有数据写入操作时,数据也是共享的,当任意一方试图写入时,便以写时拷贝的方式重新分配内存并将原来内存上的内容拷贝到新内存上,再进行修改。

好处:

        ①使父子进程彻底分离,保证了进程的独立性;

        ②写时拷贝是一种延时申请技术,提高了整机内存的使用率。

进程终止

  • 退出场景

        在写c/c++程序时,我们写main函数都要返回一个int,且都返回0,这个操作到底是什么意思呢?实际上,main函数作为一个进程,在结束时是要返回一个结果给操作系统的。对于main函数,返回0代表成功返回,结果正确;而返回非0值代表结果不正确或异常,因为非0值有很多,所以错误结果或异常结果就对应很多,此时这个0或者非0值叫做进程退出码,返回给上一层评判进程的执行结果的。

        但是我们写完一个main函数,也不知道结果到底正不正确,进而也不知道该返回什么啊?其实是可以的,看看下面的例子,我们可以使用if语句判断是否为期望结果决定返回值,结果不正确返回1,正确返回0,当然也可以预判出其他错误返回不同的非0值。

        通过main函数可以总结出,一个进程退出有三种情况:

①正常退出,结果正确;

②正常退出,结果不正确;

③异常退出。

  • 退出方法

1)正常终止的方法

        ①在main函数中return;

        ②调用系统接口_exit函数;

        ③调用库函数exit函数,

注意:

        ①必须在main函数中返回才是终止进程,普通函数返回只是在返回调用结果;

        ②正常终止都会返回进程退出码给os,可以通过【echo $?】查看最近一次的进程退出码,同时可以通过函数【strerror(退出码)】查看对应的退出原因,比如

2)异常终止

        ①ctrl+c;

        ②通过信号终止。

注意:通过信号终止,在下面即将要学到的信号章节中讲解,这里重点讲上面的正常退出的方法。

exit、_exit介绍与对比: 

1)_exit函数

       参数status存储着进程的终止信息(包括进程退出码),父进程通过wait函数接收该值,这里在进程等待部分重点讲解。

2)exit函数

         这里的参数status与_exit函数中的一样。

3)对比

①exit函数与_exit函数在代码的任何地方调用都表示结束进程,无论在main中还是调用的某个函数中。

②其实,exit库函数是_exit系统接口的一个封装,在exit函数内部,也会调用_exit函数,但在这之前,还会执行清理函数,并且清理缓冲区,然后再调用_exit函数,如图。

注意:return结束进程更为常见,return n相当于exit(n)。

进程等待 

  • 等待原因

        在前面说过僵尸进程的问题,即子进程退出但父进程不管不顾,就会造成内存泄漏。按照正常情况,父进程创造出一个子进程肯定是要其完成一个任务,然后子进程去完成,父进程等待子进程终止以后返回的结果,这就是进程等待。通过这种方式,父进程回收子进程的资源及获取子进程退出信息(比如进程退出码)。

  • 等待方法

1.wait函数

返回值:成功接受到被等待进程返回该进程的pid,失败返回-1;

参数:status是一个输出型参数,即传进此参数,函数内会将进程信息放进这个指针中,函数返回后,父进程可通过此值查看子进程信息,若不想得到父进程的结束信息,就传入NULL,关于此参数的构成会在下文提到。

eg:

2.waitpid函数

参数:

        pid:①传入指定被等待的进程pid,②传-1,代表等待任一个进程,与wait等效;

        status:与wait函数一致;

        options:①传WNOHANG,代表若指定进程没有结束,则函数直接返回0,不再等待,若进程已经结束,则函数直接返回子进程pid,即非阻塞等待;②传0,代表当子进程没有结束,父进程处于阻塞状态去等待其结束,与wait等效。

注意:WNOHANG是一个宏定义,一般大写的标记位都是宏定义。

返回值:

        与wait一致,但要注意设置了WNOHANG选项的返回值。

eg:

  • 等待结果(status介绍)

        对于wait/waitpid函数,都有一个输出型参数status,os将子进程信息填入其中,带给父进程。status不能简单的当作一个整形来看,要分开看它的比特位(目前只关心status的低16个比特位),如图

        可以看到,低八位存放终止信号,此低八位存放退出码,对于异常终止时的core dump标志暂时不说明,后面信号章节会说到。明显地,当wait/waitpid函数接受完子进程退出结果之后,正常退出可以通过【(status>>8)&0xFF】获取退出码,异常退出可以通过【status&0x7F】获取终止信号,有点C语言地基础都可以看的懂,不多赘述。因为比较麻烦,所以Linux也提供了可以关于此的宏定义:

①WIFEXITED(status):查看进程是否正常退出,若正常退出返回真,否则返回假;

②WEXITSTATUS(status):查看进程的退出码。

eg(除了else部分,其他部分与上张截图一样):

eg(增加了子进程睡眠时间,中间通过kill指令杀掉进程): 

进程替换

  • 替换原理

        通过特定的接口,加载磁盘上的一个全新的程序(代码和数据)到内存中,并和当前进程的页表重新建立映射,这就叫做替换,而其中加载的方法就是使用exec系列函数。当进程调用一种exec函数时,该进程的代码和数据完全被新程序替换,从新程序的开头开始执行,原理图如下。

注意:

        ①调用exec函数并没有创建新进程,所以调用前后的进程id并没有变化;

        ②当子进程加载新数据时,代码和数据就会被替换,对于代码而言这正是一种写入,即写时拷贝,此时,父子进程彻底分离,虽然曾经并不冲突(之前说过,父子进程代码共享,数据采用写时拷贝的手段)。

  • 替换函数

        如图一,替换函数有6种,统称为exec函数,而图二的一个exec函数是系统调用函数,图一的6个函数都是基于这个系统调用函数封装的函数,以满足不同的需求,这里我们也是重点介绍上面6个函数。

参数:

        path参数是个指针,需要传入一个路径(字符串),

        arg参数也是个指针,需要传入一个指令,而后面的省略号是可变参数列表,可以传入指令的选项,

        file参数:指针变量,传入一个文件名,

        envp:指针数组,里面存放环境变量;

        argv参数:指针数组,里面存放命令行参数,即全部arg参数。

返回值:

        如果调用成功则加载新的程序从新程序的启动代码开始执行,不再返回,如果调用出错就返回-1。

注意:

        l(list) : 表示参数采用列表

        v(vector) : 参数用数组

        p(path) : 有p自动搜索环境变量PATH

        e(env) : 表示自己维护环境变量

eg:

 注意:可变参数列表列出所有的选项后要以NULL结尾,命令行参数数组也是如此。

进程替换例子

1.如何替换自己的c/c++程序

        自己通过vim或者其他编辑器编写一个c/c++程序,这里我编写了一个cmd.c的文件,其中main函数的参数可以传入命令行参数个数,命令行参数数组,及环境变量数组(这里我没有传入),函数体根据命令行参数传入所构成,具体如下图一所示。

        之前说过,我们可以创建一个子进程,让其执行其他的事,父进程等待回收以接受结果,图二就是在这样一个情况下,我们将子进程替换成自己写的如图一所示的子程序,如图二。

2.如何替换其他语言的程序

        替换其他语言的程序与c/c++语言的程序并无二质,都先编译成可执行程序,然后将子进程替换成自己写的程序就行,这里举例python程序和shell程序,具体如下图一

        如图二则是替换python程序的结果,替换shell程序一样。

shell简易实现

        在学习Linux的过程中,离不开xshell的帮助,这个软件可以远程访问服务器,通过指令管理服务器上的文件等,比如,ls、pwd等。思考一下,我们可不可以通过程序控制来简易实现一个shell程序,步骤如下:

①获取命令行;

②解析命令行;

③创建子进程;

④替换子进程;

⑤父进程等待接收子进程;

⑥循环以上步骤。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>#define CMD_LINE_SIZE 1024
#define ARGC_MAX 32char cmd_line[CMD_LINE_SIZE];
char* _argv[ARGC_MAX];int main()
{//程序不退出while(1){//用户名+主机+当前目录//[phan9@iZf8z8xmdh7b2erpqis8sxZ test_os_8_21]$printf("[root@localhost shell]# ");//初始化memset(cmd_line,'\0',sizeof(cmd_line));//获取用户输入的指令if(fgets(cmd_line,sizeof(cmd_line),stdin)==NULL)continue;cmd_line[strlen(cmd_line)-1]='\0';//将指令选项导入指令数组int index=0;_argv[0]=strtok(cmd_line," ");//将指令选项导入指令数组while(_argv[index]){index++;_argv[index]=strtok(NULL," ");}//创建进程pid_t id=fork();if(id==0){//子进程execvp(_argv[0],_argv);exit(1);}//父进程继续int status=0;int res=waitpid(-1,&status,0);//阻塞等待if(res>0)printf("退出码:%d\n",WEXITSTATUS(status));}return 0;
}

以上是基本的shell框架,可以自行加入一些功能,比如【ls -l】指令简写成【ll】指令,文件名变色,如图地方加入

后记

        本篇文章的知识点加上上篇进程入门介绍文章的知识点,大家应该对操作系统中的进程所涉及的知识点有了比较全面的认识了,相信反复阅读两篇文章,再加上自己尝试实现一个简易的shell程序,可以更加的深入认识,两篇文章有不懂的地方可以私我或者发在评论区有大伙共同解答哦,加油,拜拜!


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

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

相关文章

Springboot 实践(14)spring config 配置与运用--手动刷新

前文讲解Spring Cloud zuul 实现了SpringbootAction-One和SpringbootAction-two两个项目的路由切换&#xff0c;正确访问到项目中的资源。这两个项目各自拥有一份application.yml项目配置文件&#xff0c;配置文件中有一部分相同的配置参数&#xff0c;如果涉及到修改&#xf…

【前端】CSS-Grid网格布局

目录 一、grid布局是什么二、grid布局的属性三、容器属性1、display①、语句②、属性值 2、grid-template-columns属性、grid-template-rows属性①、定义②、属性值1&#xff09;、固定的列宽和行高2&#xff09;、repeat()函数3&#xff09;、auto-fill关键字4&#xff09;、f…

L1-012 计算指数 C++

#include<iostream> #include<math.h> using namespace std; int main() {int n;int ret;cin >> n;if (n < 10) {ret pow(2, n);cout << "2^" << n << " " << ret<<endl;}return 0; } 所用知识点 …

suning苏宁API接入说明(苏宁商品详情+关键词搜索商品列表)

API地址:https://o0b.cn/anzexi 调用示例&#xff1a;https://api-gw.onebound.cn/suning/item_get/?keytest_api_key& &num_iid0070134261/703410301&&langzh-CN&secret 参数说明 通用参数说明 version:API版本key:调用key,测试key:test_api_keyapi_na…

轻松学会WiFi模块(ESP8266)—基于STM32,学到就是赚到!

目录 前言 一、ESP8266介绍 二、如何实现WiFi传输&#xff1f;代码详解附上 三、结果实现流程与展示 四、总结 题外话&#xff1a; 前言 哎哎哎&#xff0c;发觉好久没有更新博客了&#xff0c;最近一直事情比较多&#xff0c;也没什么时间注意博客&#xff0c;不过接下…

GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图教程

详情点击链接&#xff1a;GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图教程 前沿 GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。 如在科研编程、绘图领域&#xff1a; 1、编程建议和示例代码: 无论你使用的编程语言是…

PCL入门(四):kdtree简单介绍和使用

目录 1. kd树的意义2. kd树的使用 参考博客《欧式聚类&#xff08;KD-Tree&#xff09;详解&#xff0c;保姆级教程》和《(三分钟)学会kd-tree 激光SLAM点云搜索常见》 1. kd树的意义 kd树是什么&#xff1f; kd树是一种空间划分的数据结构&#xff0c;对于多个维度的数据&a…

向量数据库Milvus Cloud核心组件再升级,主打就是一个低延迟、高准确度

支持 ScaNN 索引 Faiss 实现的 ScaNN,又名 FastScan,使用更小的 PQ 编码和相应的指令集可以更为友好地访问 CPU 寄存器,从而使其拥有优秀的索引性能。该索引在 Cohere 数据集,Recall 约 95% 的时候,Milvus 使用 Knowhere 2.x 版本端到端的 QPS 是 IVF_FLAT 的 7 倍,HN…

SpringMVC的整合完成CRUD(增删改查)

SpringMVC是一种基于Java的Web框架&#xff0c;它是Spring框架的一部分。SpringMVC通过使用MVC&#xff08;Model-View-Controller&#xff09;设计模式来组织和管理Web应用程序的开发。 在SpringMVC中&#xff0c;Model代表数据模型&#xff0c;View代表用户界面&#xff0c;C…

Java“牵手”阿里巴巴商品列表数据,关键词搜索阿里巴巴商品数据接口,阿里巴巴API申请指南

阿里巴巴商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取阿里巴巴商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问阿里巴巴商城的网页来获取商品详情信息。以下是两种常用方…

stm32之30.DMA

DMA&#xff08;硬件加速方法&#xff09;一般用于帮运比较大的数据&#xff08;如&#xff1a;摄像头数据图像传输&#xff09;&#xff0c;寄存器-》DMA-》RAM 或者 RAM-》DMA-》寄存器提高CPU的工作效率 源码-- #include "myhead.h" #include "adc.h"#…

STM32 Nucleo-144开发板开箱bring-up

文章目录 1. 开篇2. 开发环境搭建2.1 下载官方例程2.2 ST-Link安装 3. STM32F446ZE demo工程3.1 STM32F446ZE简介3.2 跑个demo试一试 1. 开篇 最近做项目&#xff0c;用到STM32F446ZET6这款MCU&#xff0c;为了赶进度&#xff0c;前期软件需要提前开发&#xff0c;于是在某宝买…

826. 安排工作以达到最大收益;2257. 统计网格图中没有被保卫的格子数;816. 模糊坐标

826. 安排工作以达到最大收益 核心思想&#xff1a;排序维护最大利润。首先我们需要对工人按照能力排序&#xff0c;前面工人满足的最大利润后面的工人肯定是满足的&#xff0c;所以我们只需要用一个tmp来维护小于等于当前工人的最大利润&#xff0c;然后如何得到tmp&#xff…

【web开发】5.Mysql及python代码执行数据库操作

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、MYSQL二、MySQL管理查看已有数据库创建数据库删除数据库进入数据库创建表删除表展示表的行列插入数据查看表中的数据删除数据修改数据 三、python代码执行数据库…

STM32定时器的One Pulse Mode,OPM应用

文章目录 OPM应用1-精准延时应用2-精准定时 OPM T IMx_CR1的OPM位 位 3 OPM&#xff1a;单脉冲模式 (One-pulse mode) 0&#xff1a;计数器在发生更新事件时不会停止计数 1&#xff1a;计数器在发生下一更新事件时停止计数&#xff08;将 CEN 位清零&#xff09; 应用1-精准延时…

C++设计模式_03_模板方法Template Method

文章目录 1. 设计模式分类1.1 GOF-23 模式分类1.2 从封装变化角度对模式分类 2. 重构&#xff08;使用模式的方法&#xff09;2.1 重构获得模式 Refactoring to Patterns2.2 重构关键技法 3. “组件协作”模式4. Template Method 模式4.1 动机&#xff08; Motivation&#xff…

磐基2.0搭建es集群

参考&#xff1a; k8s安装elasticsearch集群 k8s安装elasticsearch集群_k8s部署elasticsearch集群_MasonYyp的博客-CSDN博客1 环境简述搭建es集群需要使用的技术如下&#xff1a;k8s集群、StatefulSet控制器、Service&#xff08;NodePort&#xff09;服务、PV、PVC、volumeC…

洛谷 LGR SCP-J 2023 c++语言模拟试题 10. 以下程序片段的时间复杂度为( )

之前在牛客的一个群中看到有位哥们发的题 好像是洛谷哪次的模拟题&#xff0c;还写着什么 LGR SCP-J 2023 c语言模拟试题 题目 就是给段代码询问时间复杂度 for (int i1; i<n; i){for (int j1; j<n; ji){for (int k1; k<n; k j){}} } 跑代码 一开始想不出怎么解就…

数据结构(C语言版)概念、数据类型、线性表

数据结构&#xff08;C语言&#xff09;基本概念 数据的基本单位 数据的基本单位是位&#xff08;bit&#xff09;和字节&#xff08;byte&#xff09;。位是最小的存储单位&#xff0c;它可以表示一个二进制的0或1。字节由8个位组成&#xff0c;用于表示一个字符或数字。在计…

ElasticSearch第二讲:ES详解 - ElasticSearch基础概念

ElasticSearch第二讲&#xff1a;ES详解 - ElasticSearch基础概念 在学习ElasticSearch之前&#xff0c;先简单了解下ES流行度&#xff0c;使用背景&#xff0c;以及相关概念等。本文是ElasticSearch第二讲&#xff0c;ElasticSearch的基础概念。 文章目录 ElasticSearch第二讲…