Linux: 进程(控制)

news2025/1/12 21:40:51

目录

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       else
 31         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       else
 65       {
 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   else
 21   {
 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.我们想要父进程聚焦于读取数据,解析数据,指派进程进程执行代码的功能

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

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

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

相关文章

SpringCloud Alibaba - Seata 部署 TC 服务,并集成微服务

目录 一、Seata 架构 1.1、Seata 架构重要角色 1.2、部署 TC 服务 1.2.1、前言 1.2.2、下载 seata-server 包&#xff0c;解压 1.2.3、修改配置 1.2.4、在 nacos 中添加配置 1.2.5、创建数据库表 1.2.6、启动 TC 服务 1.3、微服务集成 Seata 1.3.1、引入依赖 1.3.2、…

面试题:熟悉设计模式吗?谈谈简单工厂模式和策略模式的区别

刚刚接触设计模式的时候&#xff0c;我相信单例模式和工厂模式应该是用的最多的&#xff0c;毕竟很多的底层代码几乎都用了这些模式。自从接触了一次阿里的公众号发的一次文章关于 DDD的使用 以后&#xff0c;就逐渐接触了策略模式。现在在项目中运用最多的也是这几种设计模式了…

使用mysql的cmd窗口,运行项目中的mapper层xml里的sql语句,查看运行结果

使用mysql的cmd窗口&#xff0c;运行项目中的mapper层xml里的sql语句&#xff0c;查看运行结果 项目代码或者从控制台复制sql语句从控制台搜索方式 运行效果或者使用idea的console窗口运行查看结果点击进入&#xff0c;查看表结构与字段 其他技巧根据from 表名寻找对应的sql代码…

Linux系统编程系列之线程属性

一、什么是线程属性 线程属性就是线程的属性&#xff0c;是一个用于控制线程行为和功能的参数集合。它可以影响线程的优先级、调度、同步行为和资源管理等方面。线程有许多属性&#xff0c;如分离属性&#xff0c;继承策略&#xff0c;调度策略等。看图 二、线程属性的特性 1、…

ctfshow 命令执行(40-50)

web40 题目过滤了很多 if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/[0-9]|\~|\|\|\#|\\$|\%|\^|\&|\*|\&#xff08;|\&#xff09;|\-|\|\|\{|\[|\]|\}|\:|\|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){eval($c);}}else{highlight_file(__FILE_…

pycharm 中的一个非常好使用的智能提示tabnine(大大提高代码的书写效率)

一. pycharm 中的代码智能提示插件 有时候,我们总是在写代码的时候,敲全部的代码,太过于麻烦了,如果有一个软件可以预知你的后续的操作,提前将代码给你写出来,你只需要检查对错,就可以了,这样就大大提高了工作的效率. 上面的一个插件是汉化pycharm的软件包,也是非常好用的一款…

小谈设计模式(14)—建造者模式

小谈设计模式&#xff08;14&#xff09;—建造者模式 专栏介绍专栏地址专栏介绍 建造者模式角色分类产品&#xff08;Product&#xff09;抽象建造者&#xff08;Builder&#xff09;具体建造者&#xff08;Concrete Builder&#xff09;指挥者&#xff08;Director&#xff0…

分享一款开源的QT的串口示波器

分享一款开源的QT的串口示波器&#xff0c;完全开源&#xff0c;支持串口、TCP、波形显示、通信协议。 Sailor Project功能说明 串口调试助手功能 支持传统的串口调试助手的基本收发功能&#xff0c;同时可以刷新大量的数据而不卡顿 支持保存接收的数据 支持最大200条可编辑…

cadence SPB17.4 S032 - Update Symbols失败的问题

文章目录 cadence SPB17.4 S032 - Update Symbols失败的问题概述笔记END cadence SPB17.4 S032 - Update Symbols失败的问题 概述 铺铜后, 进行DRC, 发现安装孔不太合适, 有DRC警告. 安装孔是一大孔, 上面打了一圈小孔. 这些小孔有警告, 说孔和孔之间不能干涉. 开始将这些DRC…

vue3+ts创建前端blog项目

vue3创建blog项目 cmd创建Manually select featuresChoose Vue versionUse class-style component syntax? (Y/n)Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n)Use history mode for router?Pick a CSS pre…

基于SpringBoot的科研工作量获奖项目管理平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

TinyWebServer学习笔记-MySQL数据库连接

为什么要用数据库连接池&#xff1f; 我们的网站允许用户注册&#xff0c;在没有池的情况下&#xff0c;假设只有一个人&#xff0c;那么流程就是&#xff0c;用户点击注册&#xff0c;通过socket将用户的账号和密码发送到服务器&#xff0c;然后就需要创建MySQL数据库连接&am…

C#制做一个 winform下的表情选择窗口

能力有限&#xff0c;别人可能都是通过其他方式实现的&#xff0c;我这里简单粗暴一些&#xff0c;直接通过点击按钮后弹出个新窗体来实现。 1、先在form1上增加一个toolstrip控件&#xff0c;再增加个toolstripbutton按钮&#xff0c;用来点击后弹出新窗体&#xff0c;如图&a…

【Linux】文件权限详解

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,Java基础学习,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的…

【C++】可变参数模板使用,在emplace中应用

&#x1f30f;博客主页&#xff1a; 主页 &#x1f516;系列专栏&#xff1a; C ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ &#x1f60d;期待与大家一起进步&#xff01; 文章目录 一、可变参数模板1.参数包的展开方式1.递归方式展开2.逗号表达式展开参数包 二、empl…

王杰国庆作业day6

服务器 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <my_head.h> #define PORT 2324 //端口号 #define IP "192.168.10.107" //本机IP int main(int argc, const char *argv[]) {sqlite3* d…

FPGA project : TFT_LCD

实验目标&#xff1a; 驱动TFT_LCD显示十色彩条。 重点掌握的知识&#xff1a; 1&#xff0c;液晶显示器&#xff0c;简称LCD(Liquid Crystal Display)&#xff0c;相对于上一代CRT显示器(阴极射线管显示器)&#xff0c;LCD显示器具有功耗低、体积小、承载的信息量大及不伤眼…

pwnable_hacknote

pwnable_hacknote Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8047000)32位&#xff0c;没开PIE main部分就不贴了&#xff0c;直接贴主要的函数 unsigned int ADD() {int v0; // ebxint i; // [e…

堆栈与堆(Stack vs Heap)有什么区别?

​编写有效的代码需要了解堆栈和堆内存&#xff0c;这使其成为学习编程的重要组成部分。不仅如此&#xff0c;新程序员或职场老手都应该完全熟悉堆栈内存和堆内存之间的区别&#xff0c;以便编写有效且优化的代码。 这篇博文将对这两种内存分配技术进行全面的比较。通过本文的…

【考研复习】union有关的输出问题

文章目录 遇到的问题正确解答拓展参考文章 遇到的问题 首次遇到下面的代码时&#xff0c;感觉应该输出65,323。深入理解union的存储之后发现正确答案是&#xff1a;67,323. union {char c;int i; } u; int main(){u.c A;u.i 0x143;printf("%d,%d\n", u.c, u.i); …