【Linux后端服务器开发】进程控制与替换

目录

一、进程控制

1. 进程退出

2. 进程等待

2.1 阻塞等待

2.2 status位图结构

2.3 非阻塞等待

二、进程替换

1. exec*系列函数

2. 进程替换高级语言可执行程序


一、进程控制

1. 进程退出

进程退出会进入僵尸态,把自己的退出结果写入到自己的task_struct中

  • exit() 库函数:终止进程时主动刷新缓冲区
  • _exit() 系统调用:终止进程时不刷新缓冲区
#include <stdio.h>int main() 
{printf("hello world");sleep(2);//exit(1);        //两秒后打印//_exit(1);       //两秒后不打印return 0;
}

代码退出的情况:

  1. 代码跑完,结果正确 ---- return 0;
  2. 代码跑完,结果错误 ---- return !0; 退出码有意义
  3. 代码未跑完,报异常 ---- 退出码无意义

进程退出的时候,有对应的退出码,标定进程执行的结果是否正确。

退出码:

  • return 0, 进程退出码为 0,标定进程执行的结果正确
  • 如何设置返回值退出码?
  • 如果不关心进程退出码,直接return 0 即可
  • 如果关心进程退出码,返回 0 表示正确,返回非0用特定的数据表明特定的错误
  • echo $? 查看上一个进程的退出码,? 是一个变量,永远存放上一个进程退出的退出码

打印错误码:

int main() 
{for (int i = 0; i < 100; ++i) {printf("%d : %s\n", i, strerror(i));}return 0;
}

2. 进程等待

  • 去除子进程僵尸态,获取子进程退出结果
  • pid_t wait(int* status):成功则返回被等待进程的pid,失败则返回-1
  • pid_t waitpid(pid_t id, int* status, int option):option为0代表阻塞等待
  • wait/waitpid 是一个系统调用 ---> os有能力和资格去读取进程的task_struct

2.1 阻塞等待

子进程未退出,父进程只能阻塞在此等待子进程结束

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main() 
{pid_t id = fork();if (id == 0) {int cnt = 5;while (cnt) {printf("子进程:%d, 父进程:%d, cnt = %d\n", getpid(), getppid(), cnt--);sleep(1);}exit(10);}//父进程int status = 0;    // 保存退出码pid_t ret = waitpid(id, &status, 0);    //option为 0 代表阻塞等待if (id > 0) {printf("wait success: %d, sign: %d, exit code: %d\n", ret, (status & 0x7F), (status>>8) & 0xFF);}return 0;
}

2.2 status位图结构

  • status退出信号是一个32bit位的位图,只有低16个bit位存储退出信息
  • (status & 0x7F) 为终止信号, (status>>8) & 0xFF) 为退出状态
  • 异常退出,被信号杀死
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main() 
{pid_t id = fork();if (id == 0) {int cnt = 5;while (cnt) {printf("子进程:%d, 父进程:%d, cnt = %d\n", getpid(), getppid(), cnt--);sleep(1);}int a = 10;a /= 0;         //除 0 异常exit(10);}//父进程int status = 0;pid_t ret = waitpid(id, &status, 0);    //option为 0 代表阻塞等待if (id > 0) {printf("wait success: %d, sign: %d, exit code: %d\n", ret, (status & 0x7F), (status>>8) & 0xFF);}return 0;
}

8号终止信号代表浮点数异常

2.3 非阻塞等待

  • 非阻塞等待:子进程未退出,父进程检测之后立即返回
  • 非阻塞等待的意义:不用占用父进程的所有精力,可以在等待期间执行自己的任务
  • WIFEXITED(status):是否正常退出,若正常退出则返回值非零
  • WEXITSTATUS(status):提取子进程退出码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>#define NUM 10typedef void (*func_t)();   //函数指针func_t handlerTask[NUM];//测试样例
void Task1() 
{printf("handler task1\n");
}void Task2() 
{printf("handler task2\n");
}void Task3() 
{printf("handler task3\n");
}//给父进程装载任务 
void LoadTask() 
{memset(handlerTask, 0, sizeof(handlerTask));handlerTask[0] = Task1;handlerTask[1] = Task2;handlerTask[2] = Task3;
}int main() 
{pid_t id = fork();  //返回的本质就是写入, 写入时发生写时拷贝if (id < 0) {printf("folk error\n");return 1;}else if (id == 0) {int cnt = 5;while (cnt) {printf("我是子进程: %d, 父进程: %d, cnt = %d\n", getpid(), getppid(), cnt--);sleep(1);// int* p = NULL;// *p = 100;       //野指针报错, 退出信号为11}exit(10);}LoadTask();int status = 0;     //不是被整体使用的, 有自己的位图结构//非阻塞轮旋方式等待while (1) {pid_t ret = waitpid(id, &status, WNOHANG);  //非阻塞等待: 子进程未退出, 父进程检测之后立即返回if (ret == 0) {//子进程未退出, waitpid等待失败, 仅仅是检测到子进程状态未退出for (int i = 0; handlerTask[i] != NULL; ++i)handlerTask[i]();}//等待子进程退出成功else if (ret > 0) {//是否正常退出if (WIFEXITED(status)) {//正常退出, WIFEXITED()返回值为!0//判断子进程运行结果是否正确printf("exit_code: %d\n", WEXITSTATUS(status));    }else {//异常退出,被信号杀死printf("child process not normal\n");}break;}else {// waitpid调用失败printf("waitpid call failed\n");break;}sleep(1);}return 0;       
}

二、进程替换

进程替换:将指定进程的代码加载到指定位置,覆盖自己的代码和数据

1. exec*系列函数

加载器的底层接口,可替换任何后端语言的可执行程序

  • int execl(const char* path, const char* arg, ...),可变参数列表以NULL结尾
  • int execlp(const char* file, const char* arg, ...),可自动在环境变量中寻找路径
  • int execv(const char* path, char* const arg[]),可变参数数组以NULL结尾
  • int execvp(const char* file, char* const arg[])
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>int main() 
{printf("process is running\n");pid_t id = fork();if (id == 0) {sleep(1);//execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);//execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);char* const arg[] = {"ls", "-a", "-l", "--color=auto", NULL};//execv("/usr/bin/ls", arg);execvp("ls", arg);exit(10);   //若exc*调用成功,则此句代码被替换}int status = 0;pid_t ret = waitpid(id, &status, 0);if (ret > 0) {printf("wait success, sig = %d, exit code = %d\n", (status & 0x7F), (status>>8) & 0xFF);}printf("process is running done\n");return 0;       
}

execve是系统调用,其他都是封装

  • int execle(const char* path, const char* arg, ... , char* const envp[]),可传环境变量
  • int execve(const char* path, char* const arg[], char* const envp[])
  • int execvpe(const char* file, char* const arg[], char* const envp[])

2. 进程替换高级语言可执行程序

创建一个test.c文件并编译 gcc -o test test.c

#include <stdio.h>
#include <stdlib.h>int main() 
{printf("C语言程序\n");printf("C语言程序\n");printf("PATH: %s\n", getenv("PATH"));printf("PWD: %s\n", getenv("PWD"));printf("MYENV: %s\n", getenv("MYENV"));printf("C语言程序\n");printf("C语言程序\n");exit(15);
}

创建一个py_test.py文件,chmod +x py_test.py

用进程替换这个程序,并打印环境变量

#!/bin/python
print('python process')
print('python process')
print('python process')
print('python process')
print('python process')

进程替换程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>int main() 
{printf("process is running\n");pid_t id = fork();if (id == 0) {sleep(1);putenv((char*)"MYENV=123456");     //添加自定义环境变量extern char** environ;execle("./test", "test", NULL, environ);exit(10);   //若exc*调用成功,则此句代码被替换}int status = 0;pid_t ret = waitpid(id, &status, 0);if (ret > 0) {printf("wait success, sig = %d, exit code = %d\n", (status & 0x7F), (status>>8) & 0xFF);}execl("./py_test.py", "py_test.py", NULL);printf("process is running done\n");return 0;       
}

运行结果:

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

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

相关文章

QT打开和保存文件对话框的操作笔记

QT打开和保存文件对话框的操作&#xff0c;需要先包含头文件QFileDialog&#xff0c;一般通过按钮实现打开和保存文件对话框的操作。 代码如下&#xff1a; #include <QDebug> #include <QFileDialog>void Form::on_pushButton_clicked() {QString fileName;fileN…

【尚医通】vue3+ts前端项目开发笔记 2 —— 创建项目、封装网络请求、集成elment-plus 、重置样式、准备状态管理/路由 等开发前准备

尚医通开发记录(Vue3TypeScriptPiniaAxios) 一、接口地址 服务器地址:http://syt.atguigu.cn 医院接口&#xff1a;http://139.198.34.216:8201/swagger-ui.html 公共数据接口&#xff1a;http://139.198.34.216:8202/swagger-ui.html 会员接口&#xff1a;http://139.198.34…

POI下载excel通用方法

POI下载excel通用方法 最近遇到一个业务是需要下载excel&#xff0c;使用POI,这里记录一下实现过程 1、导包 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency>…

Python实现将pdf,docx,xls,doc,wps链接下载并将文件保存到本地

前言 本文是该专栏的第31篇,后面会持续分享python的各种干货知识,值得关注。 在工作上,尤其是在处理爬虫项目中,会遇到这样的需求。访问某个网页或者在采集某个页面的时候,正文部分含有docx,或pdf,或xls,或doc,或wps等链接。需要你使用python自动将页面上含有的这些信…

什么是RPC并实现一个简单的RPC

1. 基本的RPC模型 主要介绍RPC是什么&#xff0c;基本的RPC代码&#xff0c;RPC与REST的区别&#xff0c;gRPC的使用 1.1 基本概念 RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;简单的理解是一个节点请求另一个节点提供的服务本地过程调用&am…

C++STL:顺序容器之vector

文章目录 1. 概述2. 成员函数3. 创建 vector 容器的几种方式4. 迭代器vector容器迭代器的基本用法vector容器迭代器的独特之处 5. 访问元素5.1 访问vector容器中单个元素5.2 访问vector容器中多个元素 6. 添加元素6.1 push_back()6.2 emplace_back()6.3 emplace_back()和push_b…

【Java/大数据】Kafka简介

Kafka简介 Kafka概念关键功能应用场景 Kafka的原理Kafka 的消息模型早期的队列模型发布-订阅模型Producer、Consumer、Broker、Topic、PartitionPartitionoffsetISR Consumer Groupleader选举Controller leaderPartition leader producer 的写入流程 多副本机制replicas的同步时…

SOKIT软件的使用

1.模拟客户端向服务器发送报文 客户端---->TCP连接---->填写数据----->点击“发送” 2.模拟服务器接受报文 服务器---->设置IP地址与端口---->点击TCP侦听 就可以往该地址发送报文信息了

回归预测 | MATLAB实现WOA-CNN-LSTM鲸鱼算法优化卷积长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现WOA-CNN-LSTM鲸鱼算法优化卷积长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现WOA-CNN-LSTM鲸鱼算法优化卷积长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计学习总结参考资料 预测效果 基本介绍 回归预测 …

机器学习(13)--支持向量机

目录 一、支持向量机概述 二、Sklearn中的SVM概述 三、线性SVM损失函数 四、sklearn中进行可视化 1、导入模块 2、实例化数据集&#xff0c;可视化 3、网格点制作 4、建立模型并绘制决策边界和超平面 5、常用接口 6、如果数据是环形呢&#xff1f; 五、核函数 1、…

mysql什么情况下行锁(表锁)(锁的概念)

1&#xff1a;数据表aa的设计结构 2&#xff1a; 使用navicat编写手动控制事务 3&#xff1a;先选择开启事务和执行更新操作&#xff0c;where b1&#xff08;表锁&#xff09;是不带索引&#xff0c;不提交事务&#xff0c;&#xff08;如果where b1&#xff0c;b是索引就行锁…

Spring MVC 系列2 -- 创建连接、获取与输出

上个系列总结了关于Spring MVC 项目的一些基础 ,Spring MVC就是在Spring Boot的基础上引入了Spring Web依赖.接下来就进行总结一下Spring MVC项目的创建,连接,获取参数,输出响应. 目录 1. Spring MVC 创建和连接 1.1 创建Spring MVC 项目实现与浏览器互通 1.2 RequestMapping…