Linux系统进程控制

news2024/12/22 22:31:39

目录

一、进程创建

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) { // 除数为0
        Exit_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));
            else
                printf("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));
            else
                printf("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.coloradmin.cn/o/2171897.html

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

相关文章

成都睿明智科技有限公司赋能商家高效变现

在这个日新月异的数字时代&#xff0c;抖音电商正以不可阻挡之势崛起&#xff0c;成为众多品牌与商家竞相角逐的新战场。在这片充满机遇与挑战的蓝海中&#xff0c;成都睿明智科技有限公司如同一颗璀璨新星&#xff0c;凭借其专业的服务、创新的策略和敏锐的市场洞察&#xff0…

C++你不得不知道的(1)

C你不得不知道的&#xff08;1&#xff09; 【1】引例&#xff1a; 1、C语言在使用的过程中存在冲突问题&#xff01; 解决办法&#xff1a;使用域将想要使用的变量包括进去。 #include<stdio.h> int rand10; int main() {printf("%d\n",rand);return 0; }此…

VS Code激活python虚拟环境常见报错

VS Code激活python虚拟环境常见报错 问题1&#xff1a;执行激活 activate 报错 问题1&#xff1a;执行激活 activate 报错 解决&#xff1a; Win X *执行 set-executionpolicy remotesigned 再输入 Y

【智慧城市】新中地GIS开发实训项目:华农优秀学生学习成果展示(3)智游江城

华农GIS开发实训项目答辩③-智游江城/一个月学习成果展示 项目名称&#xff1a;智游江城 项目功能 主页面展示 菜单功能 控制台 3D城市 查询 导航 游览路线推荐 测量 资讯

Colorful/七彩虹将星X15 AT 23 英特尔13代处理器 Win11原厂OEM系统 带COLORFUL一键还原

安装完毕自带原厂驱动和预装软件以及一键恢复功能&#xff0c;自动重建COLORFUL RECOVERY功能&#xff0c;恢复到新机开箱状态。 【格式】&#xff1a;iso 【系统类型】&#xff1a;Windows11 原厂系统下载网址&#xff1a;http://www.bioxt.cn 注意&#xff1a;安装系统会…

DataWhale x南瓜书学习笔记 task04笔记

线性判别分析&#xff08;LDA&#xff09; 前提假设&#xff1a;各类样本的协方差矩阵相同且满秩LDA的思想&#xff1a;1.设法让训练样例集投影到一条直线上&#xff0c;2.同类样例的投影点尽可能接近&#xff0c;异类样例的投影点尽可能远离&#xff0c;3.在对新样本进行分类时…

嘻哈纸片人仿手绘插画!FLUX一键生成方法!

​ ​ ​ 如何生成这种嘻哈纸片人的仿手绘插画&#xff1f; 只需1个lora&#xff0c;3个步骤&#xff01; 接下来我们来具体的说一下操作方法以及lora使用注意 嘻哈纸片人lora 基于FLUX模型训练 在线使用&下载地址&#xff1a; https://www.liblib.art/modelinfo/53ee…

全新一区PID搜索算法+TCN-LSTM+注意力机制!PSA-TCN-LSTM-Attention多变量时间序列预测(Matlab)

全新一区PID搜索算法TCN-LSTM注意力机制&#xff01;PSA-TCN-LSTM-Attention多变量时间序列预测&#xff08;Matlab&#xff09; 目录 全新一区PID搜索算法TCN-LSTM注意力机制&#xff01;PSA-TCN-LSTM-Attention多变量时间序列预测&#xff08;Matlab&#xff09;效果一览基本…

网络与信息安全工程师(工信部教育与考试中心)

在当今数字化时代&#xff0c;大量的敏感信息与业务流程在网络上传输和处理&#xff0c;使得网络与信息安全成为保障企业运营、政务管理以及金融交易等关键领域不可忽视的一环。 因此&#xff0c;对网络安全专家的需求日益增长。 例如&#xff0c;金融机构、大型电信运营商以…

【AI战略思考5】工欲善其事,必先利其器。我的利器是什么?

目录 导言1.不要忽视时间本身复利的巨大威力2.只赚自己认知以内的钱&#xff0c;只把握自己能力以内的机会3.多做有难度的事来激发自己的潜力和提升自己4.学会抵制诱惑5.减少冗余思考和冗余操作 导言 工欲善其事&#xff0c;必先利其器。我的利器是什么&#xff1f; 虽然我中考…

从零开始搭建UVM平台(二)-加入factory机制

书接上回&#xff1a; 从零开始搭建UVM平台&#xff08;一&#xff09;-只有uvm_driver的验证平台 加入factory机制 前面搭建的平台其实一点都没有用到uvm的特性。 加入factory机制的明显两个优点&#xff1a;&#xff08;1&#xff09;不用自己手动例化类&#xff1b;&…

目标价已被华尔街投行大幅下调,Workday股票还能买入吗?

猛兽财经核心观点&#xff1a; &#xff08;1&#xff09;Needham已经将Workday的目标价下调至300美元&#xff0c;但维持“买入”评级。 &#xff08;2&#xff09;Workday第二季度的财务业绩虽然很强劲的&#xff0c;但面临客户群员工增长疲软挑战。 &#xff08;3&#xff0…

C语言语句、语句分类及注释

文章目录 一、语句和语句分类二、注释&#x1f355;注释是什么&#xff1f;为什么写注释&#xff1f;1. /**/的形式2. //的形式3. 注释会被替换 三、随机数的生成1.rand函数2.srand函数3.time函数4.设置随机数的范围 四、C99中的变长数组五、问题表达式解析表达式1表达式2表达式…

嵌入式的核心能力-Debug调试能力(一)

一、栈回溯 引入&#xff1a;调试程序时&#xff0c;经常会发生这类错误&#xff1a; 读写某个地址&#xff0c;程序报错&#xff1b;调用某个空函数&#xff0c;导致程序报错等等。 解决方法是&#xff0c;可以利用异常处理函数去打印出“发生错误瞬间”的所有寄存器地址 …

网络编程(1)——同步读写api

一、day1 学习了服务器和客户端socket的建立、监听以及连接。 &#xff08;1&#xff09;socket的监听和连接 服务端 1&#xff09;socket——创建socket对象。 2&#xff09;bind——绑定本机ipport。 3&#xff09;listen——监听来电&#xff0c;若在监听到来电&#x…

人脸遮挡检测系统源码分享

人脸遮挡检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

基于微信小程序的特色乡村综合展示平台设计与实现(源码+文档+讲解开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

C++入门基础知识89(实例)——实例14【创建各类三角形图案】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于创建各类三角形图案的相关内容&#xff…

怎样用云手机进行TikTok矩阵运营?

在运营TikTok矩阵时&#xff0c;许多用户常常面临操作复杂、设备过多等问题。如果你也感到操作繁琐&#xff0c;不妨考虑使用云手机。云手机具备丰富的功能&#xff0c;能够帮助电商卖家快速打造高效的TikTok矩阵。接下来&#xff0c;我们将详细解析这些功能如何提升你的运营效…

每日论文6—16ISCAS一种新型低电流失配和变化电流转向电荷泵

《A Novel Current Steering Charge Pump with Low Current Mismatch and Variation》16ISCAS 本文首先介绍了传统的current steering charge pump&#xff0c;如下图&#xff1a; 比起最简单的电荷泵&#xff0c;主要好处是UP和DN开关离输出节点较远&#xff0c;因此一定程度…