Linux系统进程控制

目录

一、进程创建

1.进程创建过程

2.写时拷贝

3.fork函数的两种常规用法

二、进程终止

1.进程终止的三种情况

2.进程退出信息

(1)退出码

(2)退出信号

3.进程终止的方式

三、进程等待

1.为什么要有进程等待?

2.进程等待方法

(1)wait函数

(2)waitpid函数

四、进程替换

1.进程替换原理

2.进程替换函数

(1)execl函数

(2)execlp函数

(3)execle函数

(4) execv函数

(5)execvp函数

(6)execve函数

 (7)execvpe函数

补充:


一、进程创建

进程创建即使用fork函数创建子进程,fork函数的返回值有三种情况:

对于子进程返回0;对于父进程返回新创建子进程pid;对于进程创建失败返回-1

1.进程创建过程

调用fork函数后,操作系统为子进程开辟一份新空间,将父进程的部分内核数据结构(task_struct,mm_struct,页表)拷贝给子进程,添加子进程到系统调度队列中,fork函数返回,系统开始调度进程。

注意:进程创建一定是先创建其内核数据结构,再加载其代码和数据

2.写时拷贝

子进程会继承父进程的数据,父子进程共用一份数据,当子进程要修改数据时,为了不影响父进程,因此要发生写时拷贝:即将父进程的数据重新拷贝一份,子进程再进行修改。

3.fork函数的两种常规用法
  1. 使用fork函数创建子进程,使父子进程同时执行不同的代码段,例如父进程等待客户端生成请求,生成子进程处理请求
  2. 使用fork函数创建子进程,再调用exec函数,使得子进程执行其他进程(进程替换)

二、进程终止

进程终止就是要释放进程加载到内存中的代码和数据,以及进程的内核数据结构。

进程终止通常是先释放其代码和数据,此时进程处于僵尸状态,等待其退出信息被父进程读取后再释放其内核数据结构。

1.进程终止的三种情况

一个进程终止,有三种情况:

  1. 代码运行完毕,结果正确
  2. 代码运行完毕,结果不正确
  3. 代码异常终止

每种情况进程终止时都会返回退出信息

2.进程退出信息

进程退出信息包括:退出码和退出信号

(1)退出码

代码执行完毕,进程会返回退出码,有0和非0,0表示运行结果正确,非0值不同的值有不同的错误描述。使用echo $? 命令查看最近一个子进程的退出码

我们写程序时也可以自定义退出码,不使用系统规定的退出码

#include <stdio.h>
// 自定义退出码
enum{Success=0,Div_Zero,Mod_Zero
};
// 退出描述
const char* Exit_Description(int Exit_Code)
{switch(Exit_Code){case Success:return "Success!";case Div_Zero:return "div zero!";case Mod_Zero:return "mod zero!";default:return "unknow error!";}
}
// 默认退出码
int Exit_Code=Success;
// 除法函数
int Div(int x, int y) {if (y == 0) { // 除数为0Exit_Code = Div_Zero;return -1;} else {return x / y;}
}
int main() {int result = Div(10, 100);printf("result: %d [%s]\n", result, Exit_Description(Exit_Code));result = Div(10, 0);printf("result: %d [%s]\n", result, Exit_Description(Exit_Code));return 0;
}
//result: 0 [Success!]
//result: -1 [div zero!]
(2)退出信号

当进程运行异常终止时,会返回退出信号,退出码就无意义了

使用kill -l命令查看所有的退出信号

3.进程终止的方式
  1. main函数return
  2. exit函数
  3. _exit函数

void exit(int status)
头文件:#include <unistd.h>   status是自定义的退出码

void _exit(int status);
头文件:#include <unistd.h>   status是自定义的退出码 

_exit函数是系统调用指令,exit函数是库函数,不管在任何位置调用_exit或者exit,都表示整个进程终止了。exit库函数底层就是调用_exit指令实现的。

exit终止进程时会冲刷缓冲区,_exit终止进程时不会冲刷缓冲区

三、进程等待

1.为什么要有进程等待?

父进程要通过等待,回收子进程资源(僵尸进程),获取子进程退出信息

2.进程等待方法

进程等待通过wait和waitpid函数实现

(1)wait函数

pid_t wait(int*status)

头文件:#include <sys/types.h>  #include <sys/wait.h>

等待最近子进程返回,等待成功返回被等待进程pid,等待失败返回-1;

status为输出型参数,用于获取子进程的退出信息,不关心则可设置为NULL。status

是一个32位整型数,使用其低16位来保存退出信息。其中,0~7存储退出信号,8~15存储退出码

(status>>8)&0xFF即为退出码,status&0x7F即为退出信号

wait函数代码演示

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void ChildRun()
{int i=0;for(i=0;i<5;++i){printf("Child process is running!\n");}
}
int main()
{pid_t id=fork();//子进程if(id==0){                                                                             ChildRun();                                 exit(0);//子进程退出      }              //父进程                                   int status=0;                   pid_t rid=wait(&status);if(rid>0)                           {                  printf("Father process wait success! status:%d, child quit code:%d, child quit signal:%d\n",status,(status>>8)&0xFF,status&0x7F);}                                                              else                                                    {                                                     printf("Father process wait fialed!\n");                                                                                                                                       }                                                  return 0;                                  
}
(2)waitpid函数

pid_ t waitpid(pid_t pid, int *status, int options);
头文件和wait函数相同,pid是等待指定进程返回,status是输出型参数,options用于确定阻塞等待还是非阻塞等待

status输出型参数除了可以使用位运算获取退出码和退出信号,还可以使用WIFEXITED和WEXITSTATUS

WIFEXITED(status):查看进程是否正常终止(正常终止返回真,异常终止返回假)

WEXITSTATUS(status):若WIFEXITED非零(进程正常终止),则提取进程退出码

options参数可以使用WNOHANG,来实现非阻塞等待(在等待的过程中,父进程可以去完成其他任务)

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

如果pid传入-1,options传入0则与wait函数等效

waitpid函数实现非阻塞等待代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>void ChildRun()
{int i=0;for(i=0;i<5;++i){printf("Child process is running!\n");}
}void DoOtherthing()
{printf("Child process is not end! Father process is do other thing...\n");
}
int main()
{pid_t id = fork();if (id < 0){printf("fork error!\n"); // 子进程创建失败}else if (id == 0) // 子进程执行{ChildRun();exit(123);}else // 父进程执行{int status = 0;pid_t rid = waitpid(id, &status, WNOHANG); // 非阻塞等待// 子进程未终止while (rid == 0){DoOtherthing(); // 父进程做其他事情rid = waitpid(id, &status, WNOHANG);}// 子进程等待失败if (rid == -1){printf("wait failed!\n");}else // 子进程等待成功{if (WIFEXITED(status)) // 子进程正常终止{printf("wait success! child exit code:%d\n", WEXITSTATUS(status));}else // 子进程异常终止{printf("wait success! child abnormal exit\n");}}}return 0;
}

四、进程替换

1.进程替换原理

用fork函数创建子进程后,父子进程执行的是相同的程序。当子进程调用exec函数,子进程要写发生代码和数据的写时拷贝,然后子进程的代码和数据会被新程序的代码和数据替换,这个过程中并没有创建新的进程。

2.进程替换函数
(1)execl函数

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

execl中的 l 表示路径,path是参数路径,arg参数传参只需要像Linux命令一样带上选项即可,其最后一个选项必须是NULL(本质就是命令行参数)

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>int main()
{pid_t id = fork();if (id == 0) // 子进程{execl("/usr/bin/ls", "ls", "-l", "-a", "--color", NULL);exit(1);}else if (id > 0) // 父进程{int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("father process wait success! child exit code:%d\n", WEXITSTATUS(status));}else{printf("father process wait failed!\n");}}else{printf("fork failed!\n");}
}
(2)execlp函数

int execlp(const char *file, const char *arg, ...);
execlp函数和execl函数只有第一个参数不同,file参数表示只需要传文件名,不需要传路径,系统会自动在环境变量PATH保存的路径中查找文件

execl("ls", "ls", "-l", "-a", "--color", NULL);
(3)execle函数

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

execle函数比execl函数多了第三个参数,参数envp用于接收环境变量(可以是自定义的环境变量,也可以是bash父进程的环境变量)

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>int main()
{pid_t id = fork();if (id == 0) // 子进程{//传入自定义环境变量char* const envp[] = {(char*)"HAHA=111", (char*)"HEHE=222", NULL};execle("/home/zz/240927/test1cpp.exe", "test1cpp.exe", NULL, envp);//传入父进程的环境变量// extern char** environ;// execle("/home/zz/240927/test1cpp.exe", "test1cpp.exe", NULL, environ); }else if (id > 0) // 父进程{int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid == -1)printf("wait failed\n");else{if (WIFEXITED(status))printf("wait success! child exit code:%d\n", WEXITSTATUS(status));elseprintf("wait success! child abnormal exit\n");}}else{printf("fork failed\n");exit(-1);}return 0;
}
(4) execv函数

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

execv中的 v 表示容器,path是参数路径,argv是将命令选项都放入其中

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>int main()
{pid_t id = fork();if (id == 0) // 子进程{char* const argv[] = {(char*) "ls",(char*) "-l",(char*) "-a",(char*) "--color",NULL};execv("/usr/bin/ls", argv);exit(1);}else if (id > 0) // 父进程{int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){printf("father process wait success! child exit code:%d\n", WEXITSTATUS(status));}else{printf("father process wait failed!\n");}}else{printf("fork failed!\n");}
}
(5)execvp函数

int execvp(const char *file, char *const argv[]);
同execlp函数,不需要传路径,只要传文件名即可

char* const argv[] = {(char*) "ls",(char*) "-l",(char*) "-a",(char*) "--color",NULL
};
execv("ls", argv);
(6)execve函数

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

execve函数比execv函数多了第三个参数envp,用于接收环境变量

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>int main()
{pid_t id = fork();if (id == 0) // 子进程{char* const argv[] = {(char*)"test1cpp.exe", (char*)"-a", (char*)"-b", (char*)"-c", NULL};char* const envp[] = {(char*)"HAHA=111", (char*)"HEHE=222", NULL};execve("/home/zz/240927/test1cpp.exe", argv, envp);// extern char** environ;// execle("/home/zz/240927/test1cpp.exe", "test1cpp.exe", NULL, environ); }else if (id > 0) // 父进程{int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid == -1)printf("wait failed\n");else{if (WIFEXITED(status))printf("wait success! child exit code:%d\n", WEXITSTATUS(status));elseprintf("wait success! child abnormal exit\n");}}else{printf("fork failed\n");exit(-1);}return 0;
}
 (7)execvpe函数

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

根据上述所有函数,execvpe函数很容易能理解,此处不做代码演示

补充:

execve既是一个系统调用,也是一个C语言库函数,其他的所有函数都是基于execve封装实现的

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

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

相关文章

5个最佳开源RPA框架之一UI.Vision介绍

博主介绍&#xff1a; 大家好&#xff0c;我是Yuperman&#xff0c;互联网宇宙厂经验&#xff0c;17年医疗健康行业的码拉松奔跑者&#xff0c;曾担任技术专家、架构师、研发总监负责和主导多个应用架构。技术范围&#xff1a; 目前专注java体系&#xff0c;以及golang、.Net、…

Golang优雅关闭gRPC实践

本文主要讨论了在 Go 语言中实现gRPC服务优雅关闭的技术和方法&#xff0c;从而确保所有连接都得到正确处理&#xff0c;防止数据丢失或损坏。原文: Go Concurrency — Graceful Shutdown 问题 我在上次做技术支持的时候&#xff0c;遇到了一个有趣的错误。我们的服务在 Kubern…

webshell-HTTP常见特征

一、总体特点 二、蚁剑 数据中可以看到一些明文字符串函数&#xff0c;响应中可以看到响应的明文数据。 ant特征以及对数据base64可以解码 chr类别的会出现大量的chr编码 大量的百分号字符 三、哥斯拉 第一个请求包很大 响应为0 密钥被拆分到数据前后 响应包cookie带&#xf…

SUP-NeRF-ECCV2024: 单目3D对象重建的新突破

2024-09-25&#xff0c;由Bosch Research North America和Michigan State University联合发布的SUP-NeRF&#xff0c;是一个基于单目图像进行3D对象重建的新型方法。一个无缝集成姿态估计和物体重建的统一网格。 ECCV&#xff1a;欧洲计算机视觉会议的缩写&#xff0c;它是计算…

Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)

文章目录 esp-rs 简介GithubRust 包仓库Wokwi 电子模拟器开发环境Rust 环境esp-rs 环境创建 ESP32C3 项目项目结构编译项目命令运行模拟器ESP32C3 烧录 esp-rs 简介 esp-rs 是一个专注于为 Espressif 系列芯片&#xff08;如 ESP32、ESP32-S2、ESP32-C3 等&#xff09;提供 Ru…

学日语必备神器!这4款翻译APP你用过吗?

小伙伴们&#xff0c;你们有没有在日常生活或工作中遇到过需要翻译日语的场景呢&#xff1f;无论是阅读日本原著、工作文档还是和日本小伙伴交流&#xff0c;一个好的翻译工具绝对能成为你的贴心小助手&#xff1b;今天&#xff0c;我就来跟大家分享几款我个人非常喜欢的日语翻…

2024年合肥市职业院校技能大赛(中职组)赛 网络安全 竞赛样题

2024年合肥市职业院校技能大赛(中职组)赛 网络安全 竞赛样题 (总分100分) 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 极安云科专注于技能提升&am…

数据结构与算法——Java实现 19.队列

目录 一、概述 二、链表实现队列 接口定义 接口实现类 测试类 三、环形数组实现队列 优点 下标计算 判满和判空 判满 判空 辅助变量size判空和判满 方法1 接口定义 接口实现类 测试类 方式2 接口定义 接口实现类 测试类 方法3 接口定义 接口实现类 测试类 生活鲜少给人留下退…

Nginx配置及部署前端项目,安排!

哈喽小伙伴们大家好&#xff01;我是程序媛小李&#xff0c;一位正在往全栈方向发展的前端小姐姐&#xff0c;今天给大家分享一下在日常编码中我们是怎么使用nginx来部署前端项目的&#xff01; Nginx安装 浏览器输入nginx&#xff0c;来到官网 右边找到doewnload&#xff0c…

短剧向左,体育向右,快手前途未卜?

最近&#xff0c;辗转于多项业务的快手收到了来自于市场“寓褒于贬”的评价。 麦格理发表报告表示&#xff0c;短剧业务正成为快手近期新的增长动力&#xff0c;亦维持对快手的正面看法&#xff0c;给予“跑赢大市”评级&#xff0c;预期上市前投资者出售2%股份对基本面没有太…

掌握AI提示词的艺术:应用、防护与成为提示词专家的策略

掌握好提示词的编写&#xff0c;可以用来做的事情&#xff1a; 写简历、输出面试题、输出ppt、思维导图、提取摘要、翻译、总结会议纪要、总结审计报告、数据分析、写广告/营销/请假等跟文字相关的文案、爆款文章、小说、写周报/月报。 如何写提示词 4大原则 1、 指令要精简…

Python精选200Tips:176-180

针对图像的经典卷积网络结构进化史及可视化 P176--LeNet-5【1988】模型结构说明模型结构代码模型结构可视化 P177--AlexNet【2012】模型结构及创新性说明模型结构代码模型结构可视化 P178--VGGNet【2014】VGG19模型结构及创新性说明VGG19模型结构代码VGG19模型结构可视化 P179-…

广东自闭症寄宿学校:为大龄自闭症儿童提供全寄宿制教育

在广东这片温暖的土地上&#xff0c;有一类特殊的孩子&#xff0c;他们以自己独特的方式感知世界&#xff0c;却往往因为自闭症的障碍而在成长的道路上步履维艰。为了给予这些大龄自闭症儿童更加全面、专业的关怀与教育&#xff0c;广东地区涌现出了一批自闭症寄宿学校&#xf…

Java中的Junit、类加载时机与机制、反射、注解及枚举

目录 Java中的Junit、类加载时机与机制、反射、注解及枚举 Junit Junit介绍与使用 Junit注意事项 Junit其他注解 类加载时机与机制 类加载时机 类加载器介绍 获取类加载器对象 双亲委派机制和缓存机制 反射 获取类对象 获取类对象的构造方法 使用反射获取的构造方法创建对象 获…

从0-1搭建海外社媒矩阵,详细方案深度拆解

做买卖&#xff0c;好的产品质量固然是关键&#xff0c;但如何让更多的消费者知道&#xff1f;营销推广是必不可少的。在互联网时代&#xff0c;通过社交媒体推广已经成为跨境电商卖家常用的广告手段。 如何通过海外社交媒体矩阵扩大品牌影响力&#xff0c;实现营销目标&#…

【开源项目】数字孪生智慧停车场——开源工程及源码

飞渡科技数字孪生停车场管理平台&#xff0c;基于国产数字孪生3D渲染引擎&#xff0c;结合数字孪生、物联网IOT&#xff0c;以及车牌自动识别、视频停车诱导等技术&#xff0c;实现停车场的自动化、可视化和无人化值守管理。 以3D可视化技术为基础&#xff0c;通过三维场景完整…

240927-各种卷积最清晰易懂blender动画展示

240927-一些常用卷积清晰易懂的blender动画展示&#xff08;Conv、GConv、DWConv、1*1Conv、Shuffle&#xff09; 在几个月前&#xff0c;写过一篇关于卷积过程中输入图像维度变化的博客240627_关于CNN中图像维度变化问题_图像的尺寸为什么又四个维度-CSDN博客&#xff0c;但是…

猫鱼分干(模拟---拆分步骤)

算法分析&#xff1a; 注意&#xff1a;总是更新遍历方向上的元素&#xff08;eg. 左 i-1 和 i &#xff1a;更新i&#xff09;区分水平和分配量从左向右&#xff1a;只要右侧水平大于左侧&#xff0c;即右侧等于左侧值加一从右向左&#xff1a;若左侧水平大于右侧&#xf…

一次实践:给自己的手机摄像头进行相机标定

文章目录 1. 问题引入2. 准备工作2.1 标定场2.2 相机拍摄 3. 基本原理3.1 成像原理3.2 畸变校正 4. 标定解算4.1 代码实现4.2 详细解析4.2.1 解算实现4.2.2 提取点位 4.3 解算结果 5. 问题补充 1. 问题引入 不得不说&#xff0c;现在的计算机视觉技术已经发展到足够成熟的阶段…

c++day08

思维导图 栈 #include <iostream>using namespace std;template <typename T> class Stack { private:static const size_t MAX 100; // 定义固定容量T data[MAX]; // 存储栈元素的数组size_t len; // 当前栈的大小public:…