Linux-进程控制

目录

1.进程创建

2. 进程终止

2.1 进程退出的场景

2.2 进程常见退出方法

2.3 return返回终止

2.4 exit()和_exit()

3. 进程等待

3.1 进程等待的原因

3.2 wait​编辑

3.3 waitpid

3.4 status

4. 进程替换

4.1 替换原理

4.2 exec函数系列


1.进程创建

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

返回值自进程中返回0,父进程返回子进程id,出错返回-1

对于返回值的理解:上层概念上的理解

fork在创建子进程时,OS会做的:

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

写时拷贝 :通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本

fork函数中return id就是写时拷贝


2. 进程终止

2.1 进程退出的场景

  • 代码执行完毕,结果正确
  • 代码执行完毕,结果错误
  • 代码异常终止

2.2 进程常见退出方法

正常终止:

  • main函数返回 return 
  • 调用exit()
  • _exit()

异常终止:

  • ctrl + c 信号终止
  • kill命令 信号终止

2.3 return返回终止

return n 返回值n为0代表结果正确返回,n非0代表结果不正确,在main函数的return才具有这样的意义,函数的返回值没有

2.4 exit()和_exit()

exit是c库提供的函数,_exit是操作系统提供的函数接口

exit最后也会调用_exit, 但在调用_exit之前,还做了其他工作

  1. 执行用户通过 atexit或on_exit定义的清理函数。
  2. 关闭所有打开的流,所有的缓存数据均被写入 
  3. 调用_exit

3. 进程等待

3.1 进程等待的原因

子进程退出,如果父进程没有作为,就会导致子进程变成僵尸进程,进而导致内存泄漏

父进程派给子进程的任务,子进程完成的怎么样了,父进程需要获取

3.2 wait

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int* status)

等待任意一个子进程

返回值:

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

参数:

        输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

3.3 waitpid

pid_t waitpid(pid_t pid, int* status, int options)

wait函数的功能完善版

返回值:

        正常返回等待子进程的pid

        调用出错返回-1

        options 设置为WNOHANG时 当调用waitpid时发现没有退出的子进程可以收集返回0

参数:

        当pid设置为-1时,标识等待任意一个子进程,等同于wait

        当pid设置为指定的pid,标识等待这个pid的子进程

        status和上面一样下面会介绍

        options,为0时标识阻塞等待,父进程会被加载到阻塞队列里,wait默认也是阻塞等待

        options,为WNOHANG时标识非阻塞等待,若子进程没有结束,返回0,不予以等待 ,WNOHANG,是宏定义的值为1

3.4 status

status是以位图的形式存储信息

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特 位):

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if (id == 0){//子进程int cnt = 3;while(cnt){printf("我是子进程pid: %d ppid: %d\n",getpid(),getppid());sleep(1);}exit(11);}else{//父进程sleep(5);int status = 0;pid_t ret = waitpid(id, &status, 0);printf("返回值:%d  子进程退出码: %d  终止信号:%d\n",ret, (status >> 8) & 0xFF, status & 0x7F);sleep(2);
//        while(1)
//        {
//            printf("我是父进程pid: %d ppid: %d\n",getpid(),getppid());
//            sleep(1);
//        }}return 0;
}

获取退出状态(status >> 8)& 0xFF

获取终止信息   status & 0x7F

也可以使用宏函数来完成获取status中的信息:

WIFEXITED(status)非0表示进程正常结束

WIFSIGNALED(status)非0表示进程信号终止

WEXITSTATUS(status)获取进程退出码

WTERMSIG(status)获取终止信号

进程的非阻塞等待,以及宏函数获取status 

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<stdlib.h>
#include<iostream>
#include<vector>using namespace std;
typedef void (*handler_t)(); void func1()
{printf("任务1\n");
}void func2()
{printf("任务2\n");
}std::vector<handler_t> handlers;
//加载函数
void Load()
{handlers.push_back(func1);handlers.push_back(func2);
}int main()
{pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if(id == 0){//子进程int cnt = 3;while(cnt--){printf("子进程 pid: %d ppid: %d\n",getpid(),getppid());sleep(1);}exit(11);}else{//父进程int status = 0;int quit = 0;while(!quit){pid_t ret = waitpid(id, &status, WNOHANG);if(ret < 0){perror("waitpid");exit(1);}else if(ret == 0){//子进程没有退出if(handlers.empty())Load();for(auto iter:handlers){iter();}sleep(1);}else{if(WIFEXITED(status)){printf("退出码: %d\n",WEXITSTATUS(status));}else if(WIFSIGNALED(status)){printf("退出信号: %d\n",WTERMSIG(status));}quit = 1;}}//printf("父进程 ret: %d 退出码: %d 退出信号: %d\n", ret, (status >> 8)&0xFF, status&0x7F);}return 0;
}

4. 进程替换

4.1 替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

4.2 exec函数系列

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

exec命名的规律:

  • l(list): 表示参数采用列表的形式,可变参数列表
  • v(vector): 表示参数采用数组的形式
  • p(path): 表示路径通过环境变量PATH获取
  • e(environ): 表示自己维护环境变量

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve

实现shell

//整体结构:创建子进程,由子进程获取指令,父进程判断完成的怎么样
//1.打印标识开头
//2.获取指令字符串
//3.分析字符串提取指令到grev[]
//4.部分指令的特殊处理,例如cd
//5.替换进程execvpe
//
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>#define SIZE 1024
#define NUM 32char str[SIZE];
char* _grev[NUM];
char _env[NUM][NUM];int main()
{int num_env = 0;while(1){//1.printf("[root$loadhost myshell]# ");fflush(stdout);//2.memset(str,SIZE,'\0');fgets(str, SIZE, stdin);int sz = strlen(str);str[sz - 1] = '\0';//3._grev[0] = strtok(str, " ");int index = 1;//4.if(strcmp(_grev[0],"ls") == 0){_grev[index++] = (char*)"--color=auto";}if(strcmp(_grev[0], "ll") == 0){_grev[0] = (char*)"ls";_grev[index++] = (char*)"--color=auto";_grev[index++] = (char*)"-l";}while(_grev[index++] = strtok(NULL, " "));if(strcmp(_grev[0], "cd") == 0){if(_grev[1]) chdir(_grev[1]);continue;}if(strcmp(_grev[0], "export") == 0 && _grev[1]){memcpy(_env[num_env],_grev[1],strlen(_grev[1])+1);putenv(_env[num_env]);num_env++;continue;}pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if(id == 0){//child//5.execvp(_grev[0], _grev);exit(2);}else {//fatherint status = 0;pid_t ret = waitpid(id, &status, 0);if(ret < 0){printf("等待子进程失败\n");exit(2);}else{if(WIFEXITED(status)){printf("子进程退出码:%d\n",WEXITSTATUS(status));}else if(WIFSIGNALED(status)){printf("子进程终止信号:%d\n",WTERMSIG(status));}}}}return 0;
}

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

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

相关文章

【学习笔记】rt-thread

任务 创建好任务&#xff0c;不管是动态还是静态创建&#xff0c;任务的状态是init &#xff0c;通过start方法来启动任务&#xff1b;线程大小 设置小了&#xff0c;无法正常工作&#xff1f;显示占空间100% 启动过程 TODO 这是编译器特性&#xff1f; 因为RT-Thread使用编…

代码随想录-算法训练营day14【二叉树01:理论基础、递归遍历、迭代遍历、统一迭代】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第六章 二叉树part01今日内容&#xff1a; ● 理论基础 ● 递归遍历 ● 迭代遍历 ● 统一迭代详细布置 理论基础 需要了解 二叉树的种类&#xff0c;存储方式&#xff0c;遍历方式 以及二叉树的定义 文章讲解&#x…

ARM_day7:实现三个按键中断

程序代码&#xff1a; mykey.h: #ifndef __MYKEY_H__ #define __MYKEY_H__ #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h" extern void printf(const char …

03_信号和槽

信号和槽 系统的信号和槽自定义信号和槽Lambda表达式 系统的信号和槽 下面我们完成一个小功能&#xff0c;上面我们已经学习了按钮的创建&#xff0c;但是还没有体现出按钮的功能&#xff0c;按钮最大的功能也就是点击后触发一些事情&#xff0c;比如我们点击按钮&#xff0c;…

FAGLL03H 新增自定义字段

1、SGLPOS_N_GL_CT、SGLPOS_N_CT两个结构新增自定义字段 2、执行t-code:HDBVIEWS 3、实施增强 FAGL_LIB 4、使用select data方法 5、代码示例: method IF_FAGL_LIB~SELECT_DATA.FIELD-SYMBOLS: <fs> TYPE any.FIELD-SYMBOLS <ls_data> TYPE any.F…

ctf.show_web13

上传一句话木马 1.php文件&#xff0c;显示 再改后缀为.jpg&#xff0c;显示错误文件大小 用dirsearch扫一下 备份文件.bak 下载文件源码 <?php header("content-type:text/html;charsetutf-8");$filename $_FILES[file][name];$temp_name $_FILES[file][tm…

腾讯清华联合提出图像到视频生成方法-Follow-Your-Click:点击图像并加上简单提示词就可让图像动起来!

Follow-Your-Click只需单击一次和简短的提示就可以让图像的某一部分动起来&#xff0c;还支持不同的动作表达&#xff0c;比如微笑&#xff0c;悲伤&#xff0c;跳舞…… 相关链接 论文链接&#xff1a;https://arxiv.org/abs/2403.08268 项目链接&#xff1a;https://github…

html 引入vue Element ui 的方式

第一种&#xff1a;使用CDN的方式引入 <!--引入 element-ui 的样式&#xff0c;--> <link rel"stylesheet" href"https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <!-- 必须先引入vue&#xff0c; 后使用element-ui --> <…

基于Docker构建CI/CD工具链(六)使用Apifox进行自动化测试

添加测试接口 在Spring Boot Demo项目里实现一个简单的用户管理系统的后端功能。具体需求如下&#xff1a; 实现了一个RESTful API&#xff0c;提供了以下两个接口 &#xff1a; POST请求 /users&#xff1a;用于创建新的用户。GET请求 /users&#xff1a;用于获取所有用户的列…

凡泰极客亮相2024 亚马逊云科技出海全球化论坛,为企业数字化出海赋能

随着「不出海&#xff0c;即出局」登上热搜榜单&#xff0c;企业出海已成燎原之势&#xff0c;3月29日&#xff0c;2024 亚马逊云科技出海全球化论坛在深圳成功举办&#xff0c;凡泰极客创始人梁启鸿受邀出席&#xff0c;并以 「App 2.0&#xff1a;以SuperApp构建智能数字生态…

HADOOP大数据处理技术8-JavaSe

投入地跳舞 就像没有人在一旁看着你一样 2024/4/8 5&#xff09;方法重载&#xff1a;在方法中 方法名称相同 但参数列表不同 方法名称相同 但是参数类型或个数不一样 好处&#xff1a;好记 6&#xff09;super &#xff08;只在具有继承关系的子类中使用&#xff09; 作用&a…

JAVA基础07-封装,类加载原理以及分析(内有练习代码)

目录 封装的理解 概念 目的 权限修饰符 访问权限从大到小 如何快速定义一个标准的Java类 1.普通方法 2.快捷键 练习 static 定义 静态与非静态区分 使用静态与非静态的场合 成员变量和局部变量 成员变量 局部变量 例子讲解&#xff1a;两数交换 解决方法 变…

研发岗-统信UOS系统配置npm git等前端常用配置

第一步 获取root权限 配置环境等都需要用到root权限&#xff0c;所以我们先获取到root权限&#xff0c;方便下面的操作 下载软件 在UOS应用商店下载的所需应用 版本都比较低 安装node 官网下载了【arm64】的包&#xff0c;解压到指定文件夹&#xff0c;设置链接&#xff0…

如何降低漏测, 避免上线后出bug,6年测试心得分享

一、漏测原因总结 &#xff08;1&#xff09;需求评审质量低&#xff0c;需求设计简单、只是简单描述功能&#xff0c;功能逻辑较少   &#xff08;2&#xff09;需求变更频繁   &#xff08;3&#xff09;缺少需求分解&#xff08;sql 文档、用例设计&#xff09;   &…

Unity 左右折叠显示与隐藏UI的简单实现

要实现一个简单的UI左右折叠显示与隐藏&#xff0c;可以结合遮罩&#xff0c;通过代码控制UI区块的宽度和位移来实现。 具体可以按以下步骤实现&#xff1a; 1、新建一个Image组件&#xff0c;并添加精灵&#xff0c;调整大小后&#xff0c;复制一份作为该UI的父物体&#xf…

element UI 设置type=“textarea“ 禁止输入框缩放

背景 在 Element UI 中&#xff0c;当您使用 el-input 组件并设置 type"textarea" 时&#xff0c;默认情况下&#xff0c;用户可以通过拖动输入框的右下角来调整其大小。如果您想禁止这种缩放行为&#xff0c;需要使用 CSS 来覆盖默认的浏览器行为。 注意上图&#x…

WPS的JS宏如何实现全文件路径字符串中截取文件名(excel)

从全文件路径的字符串中&#xff0c;截取文件名称&#xff0c;例如&#xff1a; 全文件路径字符串为&#xff1a;C:\Windows\System32\drivers\acpi1.sys 需要截取文件名&#xff1a;acpi1.sys 方法如下&#xff1a; 1、简单的方式&#xff1a;把全文件路径字符串拷贝&…

[Linux - C] 自主Shell

[Linux - C] 自主Shell [Linux - C语言] 自主Shell逻辑策划 main()打印命令行 void MakeCommandLineAndPrint()用户名 USER主机名 HOSTNAME当前目录 PWDSkipPath 切割目录打印命令行 获取用户字符串 int GetUserCommand()检查重定向 void CheckRedir()切割字符 void SplitComma…

基于Springboot的餐厅点餐系统

基于SpringbootVue的餐厅点餐系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 首页展示 菜品详情页 菜品信息 个人中心 后台管理 菜品信息管理 用户管理 菜…

小阳同学嵌入式学习日记-QFile读写文件

一、QFile简介 在Qt中&#xff0c;QFile是一个用于文件I/O操作的类。它提供了一种方便的方式来读取和写入文件内容&#xff0c;以及获取有关文件的信息。 QFile类提供了许多函数&#xff0c;用于打开、关闭、读取和写入文件。一些常用的QFile函数包括&#xff1a; open(): 打开…