【C语言】多进程创建和回收

简介: 【C语言】多进程创建和回收

一、多进程创建和回收

  • 孤儿进程:父进程先退出了,子进程没有退出,成为孤儿进程,父进程变成1号进程。
  • 僵尸进程:父进程没有退出,子进程退出了,但是父进程没有回收子进程资源,导致子进程变成僵尸进程。

1. fork()

创建子进程函数,一个进程可以创建多个子进程。

pid_t fork(void)
返回值
    小于0 创建失败
    等于0 是子进程
    大于0 是父进程

示例:

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

int main()
{
   
    pid_t ret = fork();
    if (ret < 0)
    {
   
        perror("fork error");
        return -1;
    }
    else if (ret > 0)
    {
   
        printf("here is father,pid is [%d], child pid is [%d]\n", getpid(), ret);
        sleep(1);
    }
    else if (ret == 0)
    {
   
        printf("here is child pid is [%d],father is [%d]\n", getpid(), getppid());
    }

    return 0;
}

2. wait()

父进程回收子进程,其中分为wait()和waitpid()函数

pid_t wait(int *status);
返回值
    大于0 返回值为回收的进程id
    返回-1 回收失败

参数 status 进程回收状态
    可以使用下面的宏来说明当前的回收状态
    WIFEXITED(wstatus) 如果正常返回,返回true
    WEXITSTATUS(wstatus) 用于输出正常返回的状态 用 %d 格式化输出

    WIFSIGNALED(wstatus) 如果被信号杀死,返回true
    WTERMSIG(wstatus) 用于输出被哪个信号杀死 用 %d 格式化输出

    WIFSTOPPED(wstatus) 如果子进程停止了,返回true
    WSTOPSIG(wstatus) 用于输出进程停止是由于哪个信号
    WIFCONTINUED(wstatus) 如果进程被信号SIGCONT重启,返回true

    WCOREDUMP(wstatus) 如果子进程发生核心转储,返回true

示例:

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

// pid_t wait(int *status);

int main()
{
   
    pid_t ret = fork();
    if (ret < 0)
    {
   
        perror("fork error");
        return -1;
    }
    else if (ret > 0)
    {
   
        int waitstatus;
        printf("here is father,pid is [%d], child pid is [%d]\n", getpid(), ret);
        pid_t waitid = wait(&waitstatus);
        if (waitid == -1)
        {
   
            perror("wait error");
            return -1;
        }
        else if (waitid > 0)
        {
   
            printf("child pid [%d] is over\n", waitid);
        }

        if (WIFEXITED(waitstatus))
        {
   
            printf("exited, status=%d\n", WEXITSTATUS(waitstatus));
        }
        else if (WIFSIGNALED(waitstatus))
        {
   
            printf("killed by signal %d\n", WTERMSIG(waitstatus));
        }
        else if (WIFSTOPPED(waitstatus))
        {
   
            printf("stopped by signal %d\n", WSTOPSIG(waitstatus));
        }
        else if (WIFCONTINUED(waitstatus))
        {
   
            printf("continued\n");
        }
    }
    else if (ret == 0)
    {
   
        printf("here is child pid is [%d],father is [%d]\n", getpid(), getppid());
    }

    return 0;
}

//输出
here is father,pid is [24290], child pid is [24291]
here is child pid is [24291],father is [24290]
child pid [24291] is over
exited, status=0

3. waitpid()

waitpid中的第三个参数可以让回收进程变成非阻塞的。

pid_t waitpid(pid_t pid, int *status, int options);
返回值
    大于0 成功,返回的是回收的进程id
    等于0 返回0是因为第三个参数设置为了 WNOHANG ,等待是非阻塞的,并且这个时候没有子进程需要被回收
    小于0 回收失败

 参数 pid 进程id
    小于-1 :等待该数字绝对值的所在组所有子进程,例如-21328,则等待21328组内所有子进程
    -1 :等待所有子进程
    0 :等待组ID等于调用进程的组ID的子进程
    大于0 :等待某个子进程
参数 status 进程回收状态
        同wait函数的
参数 options 回收选项
        0 :不添加选项
        WNOHANG :如果没有子进程需要被回收,就立即返回
        WUNTRACED :如果一个子进程被停止了,就返回
        WCONTINUED :如果一个子进程被SIGCONT从停止转变为运行,就返回

示例:

创建多个子进程并回收它们

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

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

int main()
{
   
    int i = 0;
    for (i = 0; i < 3; i++)
    {
   
        pid_t ret = fork();
        if (ret < 0)
        {
   
            perror("fork error");
            return -1;
        }
        else if (ret > 0)
        {
   

            printf("here is father,pid is [%d], child pid is [%d]\n", getpid(), ret);
            // pid_t waitid = wait(&waitstatus);
        }
        else if (ret == 0)
        {
   
            sleep(3);
            printf("here is child [%d] pid is [%d],father is [%d]\n", i, getpid(), getppid());
            break;
        }
    }

    if (i == 3)
    {
   
        int waitstatus;
        int num = 0;
        do
        {
   
            pid_t waitid = waitpid(-1, &waitstatus, WNOHANG);
            if (waitid == -1)
            {
   
                perror("wait error");
                return -1;
            }
            else if (waitid > 0)
            {
   
                printf("child pid [%d] is over\n", waitid);
                num++;
            }
            else if (waitid == 0)
            {
   
                // 没有进程被回收,可以做其他事情
                sleep(1);
                continue;
            }

            if (WIFEXITED(waitstatus))
            {
   
                printf("[%d] exited, status=%d\n", waitid, WEXITSTATUS(waitstatus));
            }
            else if (WIFSIGNALED(waitstatus))
            {
   
                printf("[%d] killed by signal %d\n", waitid, WTERMSIG(waitstatus));
            }
            else if (WIFSTOPPED(waitstatus))
            {
   
                printf("[%d] stopped by signal %d\n", waitid, WSTOPSIG(waitstatus));
            }
            else if (WIFCONTINUED(waitstatus))
            {
   
                printf("[%d] continued\n", waitid);
            }

            if (num == i)
            {
   
                break;
            }
        } while (1);
    }

    return 0;
}
//输出
here is father,pid is [24553], child pid is [24554]
here is father,pid is [24553], child pid is [24555]
here is father,pid is [24553], child pid is [24556]
here is child [0] pid is [24554],father is [24553]
here is child [2] pid is [24556],father is [24553]
here is child [1] pid is [24555],father is [24553]
child pid [24554] is over
[24554] exited, status=0
child pid [24555] is over
[24555] exited, status=0
child pid [24556] is over
[24556] exited, status=0
目录
相关文章
|
2月前
|
存储 算法 Linux
C语言 多进程编程(一)进程创建
本文详细介绍了Linux系统中的进程管理。首先,文章解释了进程的概念及其特点,强调了进程作为操作系统中独立可调度实体的重要性。文章还深入讲解了Linux下的进程管理,包括如何获取进程ID、进程地址空间、虚拟地址与物理地址的区别,以及进程状态管理和优先级设置等内容。此外,还介绍了常用进程管理命令如`ps`、`top`、`pstree`和`kill`的使用方法。最后,文章讨论了进程的创建、退出和等待机制,并展示了如何通过`fork()`、`exec`家族函数以及`wait()`和`waitpid()`函数来管理和控制进程。此外,还介绍了守护进程的创建方法。
C语言 多进程编程(一)进程创建
|
2月前
|
网络协议 C语言
C语言 网络编程(十三)并发的TCP服务端-以进程完成功能
这段代码实现了一个基于TCP协议的多进程并发服务端和客户端程序。服务端通过创建子进程来处理多个客户端连接,解决了粘包问题,并支持不定长数据传输。客户端则循环发送数据并接收服务端回传的信息,同样处理了粘包问题。程序通过自定义的数据长度前缀确保了数据的完整性和准确性。
|
2月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
2月前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
2月前
|
C语言
C语言 网络编程(八)并发的UDP服务端 以进程完成功能
这段代码展示了如何使用多进程处理 UDP 客户端和服务端通信。客户端通过发送登录请求与服务端建立连接,并与服务端新建的子进程进行数据交换。服务端则负责接收请求,验证登录信息,并创建子进程处理客户端的具体请求。子进程会创建一个新的套接字与客户端通信,实现数据收发功能。此方案有效利用了多进程的优势,提高了系统的并发处理能力。
|
2月前
|
消息中间件 Unix Linux
C语言 多进程编程(五)消息队列
本文介绍了Linux系统中多进程通信之消息队列的使用方法。首先通过`ftok()`函数生成消息队列的唯一ID,然后使用`msgget()`创建消息队列,并通过`msgctl()`进行操作,如删除队列。接着,通过`msgsnd()`函数发送消息到消息队列,使用`msgrcv()`函数从队列中接收消息。文章提供了详细的函数原型、参数说明及示例代码,帮助读者理解和应用消息队列进行进程间通信。
|
2月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
2月前
|
消息中间件 Unix Linux
C语言 多进程编程(二)管道
本文详细介绍了Linux下的进程间通信(IPC),重点讨论了管道通信机制。首先,文章概述了进程间通信的基本概念及重要性,并列举了几种常见的IPC方式。接着深入探讨了管道通信,包括无名管道(匿名管道)和有名管道(命名管道)。无名管道主要用于父子进程间的单向通信,有名管道则可用于任意进程间的通信。文中提供了丰富的示例代码,展示了如何使用`pipe()`和`mkfifo()`函数创建管道,并通过实例演示了如何利用管道进行进程间的消息传递。此外,还分析了管道的特点、优缺点以及如何通过`errno`判断管道是否存在,帮助读者更好地理解和应用管道通信技术。
|
2月前
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。
|
3月前
|
C语言
【C语言】多进程服务器
【C语言】多进程服务器
25 4