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