胧の宝藏之地
首页项目归档照片墙音乐灵境说说杂谈友链关于
封面

进程控制

写作时间:2026-07-11 20:27:31

进程创建

fork

#include <unistd.h>

pid_t fork(void);

返回值:⼦进程中返回 0 ,⽗进程返回⼦进程 id ,出错返回-1

‍

进程调⽤ fork ,当控制转移到内核中的 fork 代码后,内核会进行以下操作

分配新的内存块和内核数据结构给⼦进程

将⽗进程部分数据结构内容拷⻉⾄⼦进程

添加⼦进程到系统进程列表当中

fork 返回,开始调度器调度

当⼀个进程调⽤fork之后,就有两个⼆进制代码相同的进程。且它们都运⾏到相同的地⽅。但每个进程都将可以开始它们⾃⼰的流程。

样例代码

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

int main(void)
{
    pid_t pid;
    printf("Before: pid is %d\n", getpid());

    if ((pid = fork()) == -1)
    {
        perror("fork()");
        exit(1);
    }

    printf("After:pid is %d, fork return %d\n", getpid(), pid);
    sleep(1);

    return 0;
}

2、 进程终⽌

进程常⻅退出⽅法

正常终止

1、 从main返回

2、 调⽤exit

3、 _exit

异常退出:

ctrl+c ,信号终⽌

‍

退出码

退出码(退出状态)最后⼀次执⾏的命令的状态。在命令结束以后,我们可以知道命令是成功完成的还是以错误结束的。程序返回退出代码 0 时表⽰执⾏成功,没有问题。 代码 1 或 0 以外的任何代码都被视为不成功。

‍

_exit函数

#include<unistd.h>

void _exit(int status);

参数: status 定义了进程的终⽌状态,⽗进程通过 wait 来获取该值

exit函数

#include<unistd.h>

void exit(int status)

‍

exit函数 最后也会调⽤_exit函数,但在调⽤_exit之前,还做了其他⼯作:

  1. 执⾏⽤⼾通过atexit或on_exit定义的清理函数。
  2. 关闭所有打开的流,所有的缓存数据均被写⼊
  3. 调⽤_exit

‍

return退出

return是⼀种常⻅的退出进程⽅法。执⾏return等同于执⾏exit(n),因为调⽤main的运⾏时函数会将main的返回值当做exit的参数。

‍

3、进程等待

进程等待的原因

⼦进程退出,⽗进程如果处理,就可能造成‘僵⼫进程’的问题,进⽽造成内存泄漏。

另外,进程⼀旦变成僵⼫状态,无法处理,kill-9也无法终止。

最后,⽗进程派给⼦进程的任务完成的如何,我们需要知道。如⼦进程运⾏完成,结果对还是 不对,或者是否正常退出。⽗进程通过进程等待的⽅式,回收⼦进程资源,获取⼦进程退出信息

‍

wait⽅法

‍

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int* status);

返回值: 成功返回被等待进程 pid ,失败返回-1 。

参数:输出型参数,获取⼦进程退出状态 , 不关⼼则可以设置成为 NULL

‍

waitpid⽅法

pid_ t waitpid(pid_t pid, int *status, int options);

返回值: 当正常返回的时候 waitpid 会返回对于⼦进程的进程 ID ;

如果设置了选项 WNOHANG, ⽽调⽤中 waitpid 发现没有已退出的⼦进程 , 则返回 0 ;

如果调⽤中出错 , 则返回-1, 这时 errno 会被设置成相应的值以指⽰错误所在;

参数:

pid : Pid=-1, 等待任意一个⼦进程。与 wait 等效。

Pid>0. 等待其进程 ID 与 pid 相等的⼦进程。

status: 输出型参数

WIFEXITED(status): 若为正常终⽌⼦进程返回的状态,则为真。(查看进程 是否是正常退出) WEXITSTATUS(status): 若 WIFEXITED ⾮零,提取⼦进程退出码。(查看进程 的退出码)

options: 默认为 0 ,表⽰阻塞等待

WNOHANG: 若 pid 指定的⼦进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束, 则返回该⼦进程的 ID 。

注意事项

如果⼦进程已经退出,调⽤wait/waitpid时,wait/waitpid会⽴即返回,并且释放资源,获得⼦ 进程退出信息。

如果在任意时刻调⽤wait/waitpid,⼦进程存在且正常运⾏,则进程可能阻塞。

如果不存在该⼦进程,则⽴即出错返回。

‍

获取⼦进程status

wait和waitpid,都有⼀个status参数,该参数是⼀个输出型参数,由操作系统填充。

如果传递NULL,表⽰不关⼼⼦进程的退出状态信息。

否则,操作系统会根据该参数,将⼦进程的退出信息反馈给⽗进程。

status不能简单的当作整形来看待,可以当作位图来看待。

‍

测试代码

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(void)
{
    pid_t pid;
    if ((pid = fork()) == -1)
    {
        perror("fork");
        exit(1);
    }

    if (pid == 0)
    {
        sleep(20);
        exit(10);
    }

    else
    {
        int st;
        int ret = wait(&st);

        if (ret > 0 && (st & 0X7F) == 0)
        {
            // 正常退出
            printf("child exit code:%d\n", (st >> 8) & 0XFF);
        }
        else if (ret > 0)
        {
            // 异常退出
            printf("sig code : %d\n", st & 0X7F);
        }
    }
}

阻塞与⾮阻塞等待

阻塞等待⽅式:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        printf("%s fork error\n", __FUNCTION__);
        return 1;
    }
    else if (pid == 0)
    {
        // child
        printf("child is run, pid is : %d\n", getpid());
        sleep(5);
        exit(257);
    }
    else
    {
        int status = 0;
        pid_t ret = waitpid(-1, &status, 0); // 阻塞式等待,等待5s
        printf("this is test for wait\n");
        if (WIFEXITED(status) && ret == pid)
        {
            printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));
        }
        else
        {
            printf("wait child failed, return.\n");
            return 1;
        }
    }
    return 0;
}

⾮阻塞等待⽅式

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

typedef void (*handler_t)(); // 函数指针类型

std::vector<handler_t> handlers; // 函数指针数组

void fun_one()
{
    printf("这是⼀个临时任务1\n");
}

void fun_two()
{
    printf("这是⼀个临时任务2\n");
}

void Load()
{
    handlers.push_back(fun_one);
    handlers.push_back(fun_two);
}

void handler()
{
    if (handlers.empty())
        Load();

    for (auto iter : handlers)
        iter();
}
int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        printf("%s fork error\n", __FUNCTION__);
        return 1;
    }
    else if (pid == 0)
    {
        // child
        printf("child is run, pid is : %d\n", getpid());
        sleep(5);
        exit(1);
    }
    else
    {
        int status = 0;
        pid_t ret = 0;
        do
        {
            ret = waitpid(-1, &status, WNOHANG); // ⾮阻塞式等待
            if (ret == 0)
            {
                printf("child is running\n");
            }
            handler();
        } while (ret == 0);

        if (WIFEXITED(status) && ret == pid)
        {
            printf("wait child 5s success, child return code is :%d.\n",
                   WEXITSTATUS(status));
        }
        else
        {
            printf("wait child failed, return.\n");
            return 1;
        }
    }
    return 0;
}

‍

avatar

胧

RECOMMENDED

线程控制

2026-07-21 01:47:21

线程池——单例模式

2026-07-27 22:36:45

线程封装

2026-07-23 13:49:45

Table of Contents