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

news2024/9/24 3:27:26

目录

一、进程控制

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 10

typedef 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.coloradmin.cn/o/739285.html

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

相关文章

一种使得大模型输出结构化数据的简易方法

最近在用大模型跑一些数据&#xff0c;于是就不可避免的遇到了如何让大模型输出的格式便于处理这个问题。经过一些研究发现了一套比较有用的方法&#xff0c;在这里总结一下。 背景 任务是这样的&#xff0c;我需要用大模型&#xff08;比如ChatGPT&#xff0c;ChatGLM等等&a…

Springboot启用HTTP响应压缩

官方文档:https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/htmlsingle/#how-to-enable-http-response-compression

Java面试题大全(2023牛客网最新版)大厂面试题附答案详解

很多 Java 工程师的技术不错&#xff0c;但是一面试就头疼&#xff0c;10 次面试 9 次都是被刷&#xff0c;过的那次还是去了家不知名的小公司。 问题就在于&#xff1a;面试有技巧&#xff0c;而你不会把自己的能力表达给面试官。 应届生&#xff1a;你该如何准备简历&#…

解决mysql的count()函数条件表达式不生效的问题

示例 表数据 统计错误的sql select count(age 10) as count from student查询结果 原因 count(‘任意内容’)都会统计出所有记录数&#xff0c;因为count只有在遇见null时才不计数&#xff0c;即count(null)0 解决 方法1&#xff1a;count()函数中条件表达式加上or n…

1、计算机网络核心

序号地址1计算机网络核心2数据库相关3Redis4Linux相关5JVM的内容6GC相关的7Java多线程与并发8Java多线程与并发-原理9Java常用类库与技巧10Java框架-Spring 文章目录 1、OSI开放式互联参考模型2、TCP/IP3、TCP报文头4、TCP的三次握手5、TCP的四次挥手6、为什么会有TIME_WAIT状态…

【unity】燧光MR设备极简教程(使用篇)

燧光MR设备极简教程&#xff08;使用篇&#xff09; 一、硬件的基础使用二、定位信标三、投屏直播1、第三人称视角同步MR2、第一人称视角无线投屏 相关文章&#xff1a;燧光MR设备极简教程&#xff08;开发篇&#xff09; 一、硬件的基础使用 官方使用文档&#xff1a;https:…

【LLM】Prompt tuning大模型微调实战

文章目录 一、Propmt tuning1. peft库中的tuning2. prompt tuning怎么搞 二、Prompt tuning代码实战1. tuning训练2. 模型推理比较3. 其他tuning技术 Reference 一、Propmt tuning 1. peft库中的tuning 之前提到过可以借助peft库&#xff08;Parameter-Efficient Fine-Tuning…

基于MeanShift的图像滤波方法

前言 在视觉领域中&#xff0c;图像滤波是一种常用的技术&#xff0c;用于去除图像中的噪声和平滑图像。其中&#xff0c;MeanShift滤波是一种基于颜色和空间信息的非参数化滤波算法。 MeanShift滤波原理 MeanShift滤波是一种基于密度估计的非参数化滤波技术&#xff0c;它对每…

【Go】Go 语言教程--GO语言数组(十一)

往期回顾&#xff1a; Go 语言教程–介绍&#xff08;一&#xff09;Go 语言教程–语言结构&#xff08;二&#xff09;Go 语言教程–语言结构&#xff08;三&#xff09;Go 语言教程–数据类型&#xff08;四&#xff09;Go 语言教程–语言变量&#xff08;五&#xff09;Go …

希尔排序及其时间复杂度(图文详解)

&#x1f63e; 博客主页: 爱吃bug的猿 &#x1f680;博客专栏: 数据结构,C语言初阶进阶全流程讲解 &#x1f63d;&#x1f63d;&#x1f63d;如果喜欢博主的文章&#xff0c;可以给博主点波赞和关注加速博主更新 文章目录 前言1. 代码思路代码实现法1代码实现法2&#xff08;不…

【KingbaseES】数据库如何查询数据库,模式及表大小

新建数据kingbase及kingbase模式 CREATE DATABASE kingbase OWNER kingbase; CREATE SCHEMA kingbase AUTHORIZATION "kingbase";在数据库kingbase的kingbase模式下新建两张测试表test_size,test_size1并插入数据 CREATE TABLE "kingbase"."test_sz…

课时9:PKI证书基础知识

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:Secureboot从入门到精通-[目录] 👈👈👈目录 1、CA证书的一般用法2、x509证书3、openssl证书命令行4、X509v3证书格式

第十五章 原理篇:YOLOv8

找工作也太难了吧根本找不到工作我哭死。 参考教程&#xff1a; https://mmyolo.readthedocs.io/en/latest/recommended_topics/algorithm_descriptions/yolov8_description.html https://zhuanlan.zhihu.com/p/599761847【这个写的挺不错】 https://zhuanlan.zhihu.com/p/63…

Linux进程监控及控制【命令ps的使用】

ps命令详解 Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照&#xff0c;就是执行ps命令的那个时刻的那些进程&#xff0c;如果想要动态的显示进程信息&#xff0c;就可以使用top命令。 要对进程进行监测…

EcoVadis 2023年最新评分细则

【EcoVadis 2023年最新评分细则】 Ecovadis 的四大主题 EcoVadis 企业社会责任评级方法的目标是通过其方针政策、实施执行和绩效反馈来衡量一家公司的企业社会责任管理系统的质量。 EcoVadis企业社会责任&#xff08;CSR&#xff09;评估方法基于七项基本原则&#xff08;如图&…

Blazor前后端框架Known-V1.2.3

V1.2.3 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 Gitee&#xff1a; https://gitee.com/known/KnownGithub&#xff1a;https://github.com/known/Known 概述 基于C#和Blazor…

1.SpringBoot编写第一个接口(保姆级)

1.下载SpringBoot框架 下载地址&#xff1a;https://start.spring.io/ 选择对应的springboot 版本&#xff0c;工具&#xff0c;依赖等。 2.用Idea打开项目 下载完后&#xff0c;解压文件后&#xff0c;用Idea打开,进行项目的JDK和Maven的相关配置。 将项目的JDK配置成自己…

数据结构与算法——最小生成树问题(什么是最小生成树、Prim算法、Kruskal算法)

目录 什么是最小生成树 贪心算法 Prim算法 思路 代码&#xff08;C语言&#xff09; Kruskal算法 思路 代码 什么是最小生成树 1.是一颗树 无回路个顶点一定有条边 2.是生成树 包含全部顶点条边都在图里 3.边的权重和最小 向生成树中任加一条边都一定构成回路 最…

前端vue入门(纯代码)25_路由vueRouter

如果耐不住寂寞&#xff0c;你就看不到繁华。 【23.Vue Router--路由】 [可以去官网看看Vue Router文档](入门 | Vue Router (vuejs.org)) 用 Vue Vue Router 创建单页应用非常简单&#xff1a;通过 Vue.js&#xff0c;我们已经用组件组成了我们的应用。当加入 Vue Router …

Linux基础服务9——lamt架构

文章目录 一、基本了解二、安装tomcat三、 常用文件3.1 bin目录文件3.2 conf目录文件3.2.1 server.xml文件3.2.2 tomcat-users.xml 文件 四、分离部署lamt架构4.1 安装httpd4.2 安装mysql4.3 部署tomcat4.4 配置apache 一、基本了解 前提背景&#xff1a; Tomcat是Apache 软件基…