Linux: 进程(控制)

目录

1.进程的创建

1.1fork函数

1.2fork创建子进程,OS做了什么?

1.3为什么要写实拷贝?

2.进程的终止

2.1进程终止,操作系统做了什么?

2.2进程常见的退出方式

2.3进程常见的退出方法

3.进程的等待

3.1为什么进行进程等待

3.2如何等待?

wait方法

waitpid方法

3.3测试代码

4.进程的替换

4.1概念以及原理

4.2 怎么做?

1.替换函数exec

2.测试代码

4.3为什么要有程序替换

1.场景需要

2.补充:为什么要创建子进程


学习目标:1.进程的创建 2.进程的终止 3.进程的等待 4.进程的替换

1.进程的创建

1.1fork函数

1.功能:在已有的进程下创建一个新的进程。新进程:子进程 , 原进程:父进程

2.引用的头文件:#include <unistd.h>

3.函数:pid_t fork(void);     使用:pid_t id = fork();

4.返回值:创建成功:a.把子进程的id返回给父进程,b.把0返回给子进程

              创建失败:返回-1

5.特点:

  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

1.2fork创建子进程,OS做了什么?

  • 分配新的内存块和数据结构给子进程
  • 将父进程部分数据结构内容拷贝给子进程(写时拷贝)
  • 添加子进程到系统进程列表中
  • fork返回,开始调度器调度

1.3为什么要写实拷贝?

--因为有写实拷贝技术得存在, 所以, 父子进程得以彻底分离! 完成了进程独立性得技术保证

--写实拷贝是一种延时申请技术, 可以提高整机内存得使用率

1.用的时候,再给你分配,是高效使用内存的一种表现

2.OS 无法再代码执行前, 预知哪些空间会被访问

2.进程的终止

2.1进程终止,操作系统做了什么?

释放 进程申请的相关内核数据结构和对应的数据和代码(本质:释放系统资源)

2.2进程常见的退出方式

a. 代码跑完, 结果正确

b. 代码跑完, 结果不正确 ---->main函数的返回值?有什么意义?

c. 代码没有跑完, 程序崩溃了

2.3进程常见的退出方法

--正常终止(可以通过 echo $? 查看进程退出码):1.return(main函数内)  2.exit函数

--非正常终止:ctrl  + c  ,信号终止

exit函数:头文件:#include <unistd.h>    函数: void exit(int status);

_exit函数:头文件:#include <unistd.h>    函数: void _exit(int status);

参数:status 定义了进程的终止状态,父进程通过wait来获取该值

exit会执行用户定义的清理函数, 然后刷新缓冲, 关闭流等, 然后再退出

_exit直接退出

3.进程的等待

3.1为什么进行进程等待

1.处理僵尸进程,解决内存泄漏的问题

2.父进程可以通过等待知道派给子进程的任务完成的状况:正确与否,是否异常

3.父进程通过等待,回收子进程资源,获取子进程退出信息

3.2如何等待?

wait方法

1.头文件

#include<sys/types.h>      #include<sys/wait.h>
函数:pid_t wait(int*status);


2.返回值:成功返回被等待进程pid,失败返回-1。
3.参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
1.返回值:
        当正常返回的时候waitpid返回收集到的子进程的进程ID;
        如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
        如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
2.参数:

--.pid:

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


--.status:

        
       获取状态码(即次第8位):(status>>8)&0xFF

       获取信号(即低7位):status & 0x7F

        

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


--.options:

        默认为0:阻塞等待
        WNOHANG(非阻塞等待):若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

3.3测试代码

1.阻塞式等待

阻塞式等待,即把waitpid中的参数中options设为0

代码:

  1 #include <stdio.h>                2 #include<sys/types.h>3 #include<sys/wait.h>//wait的头文件4 #include<unistd.h>  //fork的头文件5 #include<stdlib.h>6 7 int main()8 {            9   pid_t id = fork(); //创建子进程10   if(id == 0)11   {             12     //子进程                                                                  13     int cnt = 5;                       14     while(cnt)                         15     {          16       printf("我是子进程: %d\n",cnt--);17       sleep(1);18     } 19     exit(11);20   }          21   else             22   {                                                           23     //父进程                                                  24     int status = 0;25     pid_t ret = waitpid(id,&status,0);//默认是阻塞式等待子进程26     if(ret >0)                                                    27     {     28       if(WIFEXITED(status))                              29         printf("父进程等待成功,退出码: %d\n",WEXITSTATUS(status));30       else31         printf("子进程异常退出: %d\n",WIFEXITED(status));32     }      33   }34   return 0;35 }

效果:

2.非阻塞式等待

非阻塞式等待:即把waitpid中的参数中options设为WNOHANG

我们想要父进程在等待的过程中干点别的事情:

1.这里我们定义了一个类型为函数指针的vector

2.定义了两个函数

3.定义了一个load函数,用来往实例化的vector中填充函数指针

4.当我们waitpid返回0,表示等待成功,但子进程还没有退出,若vector不为空,往里加载函数指针,让父进程调用这些函数(用来模拟做其它的事情)

注意:这里使用了范围for去遍历vector,是C++11里面的,若是C98编译可能会出错

使用下面这段代码:g++ -std=c++11 -0   myproc    myproc.cc

                                                       生成的执行文件           编译的文件

代码:

  1 #include<iostream>2 #include<vector>3 #include<stdio.h>4 #include<sys/types.h>5 #include<sys/wait.h>//wait的头文件6 #include<unistd.h>  //fork的头文件7 #include<stdlib.h>8 9 typedef void (*handler_t)();//函数指针类型10 std :: vector<handler_t> handlers;//函数指针数组11 12 void func_one()13 {14   printf("这是第一个临时任务\n");15 }16 17 void func_two()18 {19   printf("这是第二个临时任务\n");20 }21 22 void load()23 {24   handlers.push_back(func_one);25   handlers.push_back(func_two);26 }27 28 int main()29 {30   pid_t id = fork(); //创建子进程31   if(id == 0)32   {                                                                                                                                                                 33     //子进程34     int cnt = 5;35     while(cnt)36     {37       printf("我是子进程: %d\n",cnt--);38       sleep(1);39     }40     exit(11); 41   }42   else                                                                                                                                                              43   {44     int quit = 0;45     while(!quit)46     {47       int status = 0;48       pid_t ret = waitpid(id,&status,WNOHANG);49 50       if(ret > 0)51       {52         quit = 1;53         printf("等待成功,退出码: %d\n",WEXITSTATUS(status));54       }55       else if(ret == 0)56       {57         printf("等待成功,但子进程还没有退出,父进程可以做其它的事情\n");58         if(handlers.empty()) load();59         for(auto& iter:handlers)60         {61           iter();62         }63       }64       else65       {66         //等待失败67         printf("等待失败\n");68         quit = 1;69       }70       sleep(1);71     }72   }73 74   return 0;    75 }

效果:

4.进程的替换

4.1概念以及原理

1.概念

程序替换:是通过特定的接口,加载磁盘上一个全新的程序(代码+数据)

2.原理

4.2 怎么做?

1.替换函数exec

#include <unistd.h>`//头文件

1.函数
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

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

2.命名解释

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量

3.返回值

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值

(这是因为exec函数是进程的替换,调用该函数成功之后,会将当前进程的所有代码和数据都进行替换! 包括已执行的和没有执行的!)

加载 , 所谓的exec*函数, 本质就是如何加载程序的函数

2.测试代码

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

代码:

1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/wait.h>4 #include<sys/types.h>5 #include<stdlib.h>6 7 8 const char* path = "/usr/bin/ls";9 10 int main()11 {12   pid_t id = fork();//创建子进程13   if(id == 0)14   {15     printf("我是子进程\n\n");16     execl(path,"ls","-a","-l",NULL);//调用exec函数执行其它程序                                                                                                      17 18     exit(1);19   }20   else21   {22     int status = 0;23     pid_t ret = waitpid(id,&status,0);//阻塞式等待24 25     if(ret > 0)26     {27       printf("\n我是父进程,等待成功,退出码:%d\n",WEXITSTATUS(status));28     }29   }30 31 32   return 0;33 }

效果:


2.int execlp(const char *file, const char *arg, ...);

和上面基本一致,就是可以不用写绝对路径,path:环境变量,OS能直接找到


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

同上,只用把要执行的命令写入字符指针数组内,然后传递这个数组就行(最后一个参数必须是NULL)


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

同上不用带绝对路径

 5.int execle(const char *path, const char *arg, ...,char *const envp[]);

这个可以给其它程序传递环境变量

--我自己设置了一个环境变量val

--调用execle函数

--mycmd.c  可以获取val这个环境变量

--效果:

4.3为什么要有程序替换

1.场景需要

一定和应用场景有关, 我们有时候, 必须让子进程执行新的程序 !!!

例如:1.如何执行其他或我自己写的C. C++二进制程序2.如何执行其它语言的程序

1.如何执行其他或我自己写的C. C++二进制程序

--makefile:

--mycmd.c

--exec.c

--效果:

2.如何执行其它语言的程序

--效果:

2.补充:为什么要创建子进程

为什么要创建子进程

1.如果不创建, 那么进程替换的就是父进程,创建了,替换的就是子进程,而不影响父进程(进程的独立性,且加载属于改写了,由于写时拷贝的特点,会在物理内存上开辟一块新的内存,把新的代码加载到上面,并通过页表,改变映射关系)

2.我们想要父进程聚焦于读取数据,解析数据,指派进程进程执行代码的功能

(父进程读取分析数据,子进程执行代码的功能)

5.总结,实现简单的shell

链接:shell的模拟实现

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

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

相关文章

Spring Boot如何配置CORS支持

Spring Boot如何配置CORS支持 CORS&#xff08;跨源资源共享&#xff09;是一种Web浏览器的安全性功能&#xff0c;用于控制网页上的脚本文件从不同的源加载其他网页资源。在开发现代Web应用程序时&#xff0c;通常需要跨域请求不同的资源&#xff0c;如API服务或其他Web应用程…

uniapp 在uni.scss 根据@mixin定义方法 、通过@include全局使用

在官方文档中提及到uni.scss中变量的使用&#xff0c;而我想定义方法&#xff0c;这样写css样式更方便 一、官方文档的介绍 根据官方文档我知道&#xff0c;在这面定义的变量全局都可使用。接下来我要在这里定义方法。 二、在uni.scss文件中定义方法 我在uni.scss文件中定义了…

分类预测 | MATLAB实现POA-CNN鹈鹕算法优化卷积神经网络多特征分类预测

分类预测 | MATLAB实现POA-CNN鹈鹕算法优化卷积神经网络多特征分类预测 目录 分类预测 | MATLAB实现POA-CNN鹈鹕算法优化卷积神经网络多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现POA-CNN鹈鹕算法优化卷积神经网络多特征分类预测&#xff0…

头戴式耳机什么牌子最好?头戴式耳机推荐性价比高

脖子上挎个头戴式作为随身装备&#xff0c;不仅给服装配饰添加了潮流感&#xff0c;还可以可以随时聆听音乐&#xff0c;随着广大消费者的生活水平不断提高&#xff0c;市面上的头戴式耳机越来越多。 选择头戴式耳机时无非就是听音质、看外观、舒适度等等&#xff0c;那么头戴…

QT UI控件汇总介绍

按钮 ToolButton 和pushbutton没什么区别&#xff0c;可以用来设置图标 设置展示策略 RadioButton 一般用Container可以将其框起来设置互斥域&#xff0c;推荐选用GroupBox 使用方法 qDebug()<<ui->radioButton_3->isChecked(); CheckBox 可以勾选三态 stat…

主从复制的实现方案

读写分离技术架构图 实现读写分离的技术架构选型如上;需要自己去实践主从复制;为了节省资源&#xff0c;当然系统并发量并没有那么大,选择一主一丛;强制读主库,为了解决主从同步延迟带来的影响&#xff1b;对于实时性要求高的强制读主库&#xff1b;GTID 主要是一种事务标识技术…

【多线程进阶】线程安全的集合类

文章目录 前言1. 多线程环境使用 ArrayList2. 多线程环境使用队列3. 多线程环境使用哈希表3.1 HashTable3.2 ConcurrentHashMap 总结 前言 本文主要讲解 Java 线程安全的集合类, 在之前学习过的集合类中, 只有 Vector, Stack, HashTable, 是线程安全的, 因为在他们的关键方法中…

【debian 12】:debian系统切换中文界面

目录 目录 项目场景 基础参数 原因分析 解决方案 1.ctrlaltT 打开终端 2.查询当前语言环境&#xff08;我的已经设置成了中文 zh_CN.UTF-8&#xff09; 3.打开语言配置界面 4.最后一步&#xff1a;重启 不要放弃任何一个机会&#xff01; 项目场景&#xff1a; 这两…

【Linux基础】Linux的基本指令使用(超详细解析,小白必看系列)

&#x1f449;系列专栏&#xff1a;【Linux基础】 &#x1f648;个人主页&#xff1a;sunnyll 目录 &#x1f4a6; ls 指令 &#x1f4a6; pwd指令 &#x1f4a6;cd指令 &#x1f4a6;touch指令 &#x1f4a6;mkdir指令&#xff08;重要&#xff09; &#x1f4a6;rmdir指令…

产品需求分析师的基本职责

产品需求分析师的基本职责1 职责 1、主要对用友司库云产品进行调研及产品规划; 2、根据司库云业务需求进行详细需求的用户故事、原型设计、需求分析、详细需求文档编写等; 3、进行产品的需求管理、需求验证、产品演示等需求工作; 4、配合开发、UE人员完成对产品的开发任务;…

MOM与MES管理系统有哪些本质上的区别

随着企业业务的不断发展&#xff0c;许多制造企业开始面临车间管理失控、生产不透明等问题。这时候&#xff0c;很多企业选择上线MES生产管理系统来提高生产管理水平。然而&#xff0c;随着企业业务的不断拓展&#xff0c;MES系统也逐渐暴露出其局限性。于是&#xff0c;MOM平台…

高端品牌如何利用软文抓住顾客的心?

如今高端品市场价值巨大&#xff0c;但之前由于“口罩”影响和冲击&#xff0c;高端品牌的线上销售份额占比较少&#xff0c;同时得益于互联网和新媒体技术的发展&#xff0c;高端品的利润来源大多数是线上推广进行销售&#xff0c;而软文就是高端品常用的推广方式&#xff0c;…