首页 > 系统相关 >[Linux]进程控制

[Linux]进程控制

时间:2024-11-21 19:41:26浏览次数:1  
标签:status 控制 cnt int pid Linux 进程 include

进程控制

进程创建

fork函数

作用

fork函数的作用是用来创建一个新进程,新进程被称为子进程,而原来的进程称为父进程。

返回值

fork函数有两个返回值,给父进程返回子进程pid,给子进程返回0。

  • 为什么有两个返回值?

    在执行fork函数的时候,当fork函数的核心逻辑已经执行完成,准备return时,子进程就已经被创建了出来,此时就已经分为了两个执行流,所以return就被执行了两次。

  • 为什么给父进程返回子进程pid,给子进程返回0?

    对于子进程来说,它的父进程是唯一的;而对于父进程来说,它可能会有多个子进程。为了更好的管理子进程,所以给父进程返回的是子进程的pid。

  • 同一个id(存储fork的返回值),怎么会同时存储两个不同的值?

    当fork函数执行完之后,父子进程谁先返回,就谁先向变量id写入数据,由于进程具有独立性,会发生写时拷贝。因此,同一个id,地址相同但内容却不同。

fork调用失败

当系统中的进程超过限制时,fork创建子进程会失败。下面是一个测试代码。

#include <stdio.h>
#include <unistd.h>

int main () 
{
    int cnt = 1;
    while(1) 
    {
        pid_t id = fork();
        if (id == 0)
        {
            while(1)
            {
                sleep(1);
            }
        }
        else if(id < 0)
        {
            printf("fork error!!!\n");
            break;
        }
        cnt++;
        printf("%d\n", cnt);
    }

    return 0;
}

运行结果

进程终止

进程退出码

进程退出码是进程结束时返回的一个整数值,用来标定进程执行的结果是否正确。通常0表示进程正常结束,没有出现错误;非零表示出现了某种异常或错误情况,不同的非零值可能代表不同的错误类型或原因。

echo $?:用于显示上一个命令的退出状态码。其中$?是一个变量,叫做”退出状态变量“。

执行如下代码

#include <stdio.h>

int Add(int begin, int end)
{
    int sum =  0;
    for (int i = begin; i < end; i++) sum += i;
    return sum;
}

int main()
{
    int ret = Add(0, 100);

    if (ret == 5050) return 0;
    else return 1;
}

从上面的结果发现为什么只有第一次的退出码是1,而之后都是0呢?那是因为echo本身就是一个进程,当第一次执行时,它显示的mytest的退出码,当第二次执行时,它显示的是上一个echo的退出码,由于上一个echo进程是正常退出的,那么肯定显示的就是0了。

常见的退出方法

  1. main返回
  2. 调用exit()
  3. _exit()

其中exit()是库函数,而_exit()是系统调用。exit()会在进程终止前刷新并关闭所有打开的缓冲区,将未输出的数据输出;_exit()则不会刷新缓冲区,直接终止进程,可能导致未输出的数据丢失。

示例:

return退出return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返 回值当做exit的参数。

进程等待

子进程退出,父进程如果不管,那么就有可能造成僵尸进程的问题,进而造成内存泄漏;另外,进程一旦变成僵尸进程,那么使用kill -9命令都无法结束该进程。最后父进程派给子进程的任务完成的如何,父进程同样要知道,例如子进程是否正常退出,运行结果是否正确。因此父进程需要通过进程等待的方式回收子进程资源,获取子进程的退出信息。

进程等待方法

  • wait()

    pid_t wait(int *status)

    作用:用来暂停父进程的执行,获取子进程的终止状态,包括正常结束的退出码或异常终止的信号。

    返回值:成功时返回子进程的pid,失败返回-1。

    示例:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main()
    {
        pid_t id = fork();//创建子进程
        //子进程运行5秒后退出
        if (id == 0)
        {
            int cnt = 5;
            while(cnt)
            {
                printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt);
                cnt--;
                sleep(1);
            }
            exit(0);
        }
        //父进程
        sleep(8);
        pid_t ret = wait(NULL);
        if (ret > 0)	
        {
            printf("wait success:%d\n", ret);
        }
    }
    
  • waitpid()

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

    参数:

    • pid:
      • pid = 1,等待任意一个子进程,与wait等效。
      • pid > 0,等待指定pid的子进程。
    • status:
      • 是一个位图结构,用于获取子进程的终止状态信息。
      • 可以通过宏(如 WIFEXITEDWEXITSTATUS 等)来解析和提取具体的状态细节。
    • options:
      • WNOHANG: 非阻塞等待,若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

    status参数

    在返回的status值中,低16位包含了子进程的终止信息。

    1. 低 8 位(即 0 - 7 位):如果子进程是通过 exit 正常终止的,这 8 位保存了子进程的退出状态码。可以通过 WEXITSTATUS(status) 宏来获取。如果WIFEXITED宏对waitwaitpid返回的状态值进行判断后返回非零值,就表示子进程正常退出。
    2. 高 8 位(即 8 - 15 位):如果子进程是由于信号而终止的,这 8 位保存了导致子进程终止的信号编号。可以通过 WTERMSIG(status) 宏来获取。

    可以使用status & 0xFF获取它的低八位,使用(status >> 8) & 0xFF获取高八位。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    int main()
    {
        pid_t id = fork();//创建子进程
        //子进程运行5秒后退出
        if (id == 0)
        {
            int cnt = 5;
            while(cnt)
            {
                printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt);
                cnt--;
                sleep(1);
            }
            exit(10);//退出码
        }
    
        int status = 0;
        int ret = waitpid(id, &status, 0);//阻塞式等待
        if (ret > 0)
        {
            if (WIFEXITED(status))//判断是否正常退出
            {
                printf("exit code: %d\n", WEXITSTATUS(status));//获取退出状态码
            }
            else printf("child exit not normal\n");
            printf("wait success, exit code : %d, sig: %d\n", (status>>8) & 0xff, status & 0xff);
        }
    

阻塞等待与非阻塞等待

阻塞等待:当子进程还在运行时,父进程一直在等待子进程结束运行,在等待的过程中父进程不会去执行其他任务。

非阻塞等待:当子进程还在运行时,父进程在等待的途中会去执行其他任务。

示例1:阻塞式等待

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();//创建子进程
    //子进程运行5秒后退出
    if (id == 0)
    {
        int cnt = 3;
        while(cnt)
        {
            printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt);
            cnt--;
            sleep(1);
        }
        exit(10);
    }
    //父进程
    sleep(5);
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);//阻塞式等待
    if (ret > 0)
    {
        printf("wait success:%d, sig number: %d, child exit code: %d\n", ret, (status & 0xff), (status >> 8) & 0xff);
    }
    sleep(5);
}

从上面的结果可以看出,在前3秒的时候子进程正在运行,而父进程在阻塞等待子进程执行结束,第4秒时,子进程执行结束,状态变为僵尸状态,第6秒的时候,父进程成功回收子进程,拿到了子进程的退出信号和退出码。

示例2:非阻塞轮询等待

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();//创建子进程
    //子进程运行5秒后退出
    if (id == 0)
    {
        int cnt = 5;
        while(cnt)
        {
            printf("子进程,pid:%d, pid:%d, cnt:%d\n", getpid(), getppid(), cnt);
            cnt--;
            sleep(1);
        }
        exit(10);
    }
    //父进程
    int status = 0;
    while (1)
    {       
        pid_t ret = waitpid(id, &status, WNOHANG);//非阻塞式等待
        if (ret == 0)
        {
            printf("wait done, but child is running\n");
        }
        else if (ret > 0)
        {
            printf("wait success:%d, sig number: %d, child exit code: %d\n", ret, (status & 0xff), (status >> 8) & 0xff);
            break;
        }
        else printf("wait failed\n");
        sleep(1);
    }
}

这段代码和上面的差不多,只不过子进程在执行任务的时候,父进程是以非阻塞的形式去等待子进程的。

标签:status,控制,cnt,int,pid,Linux,进程,include
From: https://www.cnblogs.com/wzhiheng/p/18561298

相关文章

  • Linux下 GDB调试器的使用
    文章目录1.可执行程序的Debug版和Release版区别一、编译选项与目的二、性能与体积三、功能与特性四、查看可执行文件2.GDB相关命令GDB常用命令1.可执行程序的Debug版和Release版区别一、编译选项与目的Debug版:编译选项:通常使用包含调试信息的编译选项,如/MDd......
  • 使用再生龙CloneZilla进行Linux系统的镜像完全封装和还原
    写文章  使用再生龙CloneZilla进行Linux系统的镜像完全封装和还原巴布改不掉晚睡的咸鱼​关注 71人赞同了该文章CloneZilla可以将Linux完整移植到另一台机器中,保证数据,分区,挂载,启动项。。所有的一切完全一致注意:进行还原的机器需要......
  • Linux基础——yum downgrade和yum localinstall降级安装
    一、问题描述需求:租户需要从openssl-1.1.1t-11降级安装openssl-1.1.1f-36,安装包相关依赖问题;报错:rpm安装提示依赖问题 报错:yumlocalinstall本地rpm包无法安装; 二、解决办法1、方法一:yumdowngrade降级#通过本地rpm包或在线yum源安装,加上参数“--allowerasing”允许......
  • PID 控制算法 | 模糊控制 | 控制规则
    注:本文为几位功夫博主关于PID控制算法的几篇合辑。知识点交集未去重,如有内容异常请看原文。控制算法(一)——PID控制算法fxfreefly于2020-03-1717:25:43发布比例积分微分控制,简称PID控制,其中P表示比例、I表示积分、D表示微分。PID控制算法是最早发展起来......
  • 从0开始学习Linux——Shell编程详解【04】
     期目录:从0开始学习Linux——简介&安装从0开始学习Linux——搭建属于自己的Linux虚拟机从0开始学习Linux——文本编辑器从0开始学习Linux——Yum工具从0开始学习Linux——远程连接工具从0开始学习Linux——文件目录从0开始学习Linux——网络配置从0开始学习Linux——......
  • linux之磁盘管理
    磁盘管理1.磁盘分类机械硬盘:盘片主轴传动手臂做机械运动类似DVD固态硬盘:内部是主板和U盘类似2.硬盘大小3.5英寸:台式机2.5英寸:笔记本服务器3.硬盘接口IDE接口过时scsi接口过时sata接口台式机3.0nvme接口固态硬盘SAS接口企业级4.硬盘存储......
  • Linux中如何批量删除系统中所有的自建用户
    一、需求分析在Linux系统中,一般情况下,系统用户(如root、bin、daemon等)的用户ID(UID)通常是小于1000的。对于自建用户(非系统用户),UID一般从1000开始分配。不过,这不是绝对的规则,具体的UID范围可以由系统管理员在创建用户时指定,或者根据系统的用户管理配置文件(如/etc/login.de......
  • 【linux之clickhouse的问题记录】记由于clickhouse服务内存打满导致cpu/mem都飙升然后
    在记录相关文档的过程中发现监控中关于该节点的clickhouse数据异常,随后在node节点监控中也不见该节点信息于是找到相关机器进行检查,堡垒机发现无法连接clickhouse的节点,随后找同网段的机器尝试ping一下测试连通性,随后发现无法ping通错误信息:From172.21.0.1icmp_seq=1Destin......
  • Metasploit Pro 4.22.5-2024111901 (Linux, Windows) - 专业渗透测试框架
    MetasploitPro4.22.5-2024111901(Linux,Windows)-专业渗透测试框架Rapid7Penetrationtesting,releasedNov19,2024请访问原文链接:https://sysin.org/blog/metasploit-pro-4/查看最新版。原创作品,转载请保留出处。作者主页:sysin.org世界上最广泛使用的渗透测试框......
  • Metasploit Framework 6.4.37 (macOS, Linux, Windows) - 开源渗透测试框架
    MetasploitFramework6.4.37(macOS,Linux,Windows)-开源渗透测试框架Rapid7Penetrationtesting,2024-11-20请访问原文链接:https://sysin.org/blog/metasploit-framework-6/查看最新版。原创作品,转载请保留出处。作者主页:sysin.org世界上最广泛使用的渗透测试框架......