问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C语言如何创建子进程

创作时间:
作者:
@小白创作中心

C语言如何创建子进程

引用
1
来源
1.
https://docs.pingcode.com/baike/1219313

在C语言中创建子进程是多线程编程中的一个重要概念,它允许一个进程创建另一个进程来执行不同的任务。本文将详细介绍C语言中创建子进程的几种方法,包括fork()、vfork()和posix_spawn(),并介绍子进程的管理方法和实际应用场景。

C语言创建子进程的方法包括使用fork()系统调用、使用vfork()系统调用、以及使用posix_spawn()函数。其中,fork()是最常用的方法,它会创建一个新的进程,该进程是调用进程(父进程)的副本。vfork()类似于fork(),但它会创建一个更轻量级的子进程,适用于某些特定场景。posix_spawn()是POSIX标准提供的创建新进程的函数,它在某些情况下可以替代fork()。下面我们将详细讲解fork()的使用方法。

一、fork()系统调用

1. 什么是fork()?

fork()是UNIX系统中创建进程的基本方法。它会复制当前进程的所有资源,包括文件描述符、内存空间等,生成一个新的进程,称为子进程。

2. fork()的工作原理

调用fork()时,操作系统会创建一个新的进程,该进程是调用进程的副本。新的进程拥有与父进程相同的代码段、数据段、堆、栈等,但它们是独立的实体,修改子进程的内存不会影响父进程。fork()的返回值可以用来区分父进程和子进程。在父进程中,fork()返回子进程的PID,而在子进程中,fork()返回0。

3. fork()的使用方法

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

int main() {
    pid_t pid;
    pid = fork();
    if (pid < 0) {
        // fork failed
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // Child process
        printf("This is the child process, PID: %d\n", getpid());
    } else {
        // Parent process
        printf("This is the parent process, PID: %d\n", getpid());
        printf("Child process PID: %d\n", pid);
    }
    return 0;
}

二、vfork()系统调用

1. 什么是vfork()?

vfork()是fork()的一种变体,它在创建子进程时不会复制父进程的地址空间,而是让子进程共享父进程的地址空间,直到子进程调用exec()或exit()。

2. vfork()的工作原理

vfork()创建的子进程在调用exec()或exit()之前与父进程共享地址空间,这意味着子进程对内存的修改会影响父进程。因此,在子进程调用exec()或exit()之前,父进程会被挂起,直到子进程完成。

3. vfork()的使用方法

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

int main() {
    pid_t pid;
    pid = vfork();
    if (pid < 0) {
        // vfork failed
        fprintf(stderr, "vfork failed\n");
        return 1;
    } else if (pid == 0) {
        // Child process
        printf("This is the child process, PID: %d\n", getpid());
        _exit(0);  // Use _exit() instead of exit() to avoid flushing stdio buffers
    } else {
        // Parent process
        printf("This is the parent process, PID: %d\n", getpid());
        printf("Child process PID: %d\n", pid);
    }
    return 0;
}

三、posix_spawn()函数

1. 什么是posix_spawn()?

posix_spawn()是POSIX标准提供的创建新进程的函数。与fork()和vfork()相比,posix_spawn()更加灵活和高效,适用于某些特定场景。

2. posix_spawn()的工作原理

posix_spawn()结合了fork()和exec()的功能,在一个步骤中创建新进程并执行新的程序。它提供了更丰富的选项,可以指定新进程的属性,如文件描述符、环境变量等。

3. posix_spawn()的使用方法

#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;

int main() {
    pid_t pid;
    char *argv[] = {"/bin/ls", NULL};
    int status = posix_spawn(&pid, "/bin/ls", NULL, NULL, argv, environ);
    if (status == 0) {
        printf("Child process PID: %d\n", pid);
        if (waitpid(pid, &status, 0) != -1) {
            printf("Child process exited with status: %d\n", WEXITSTATUS(status));
        } else {
            perror("waitpid");
        }
    } else {
        perror("posix_spawn");
    }
    return 0;
}

四、子进程的管理

1. 等待子进程

父进程通常需要等待子进程的结束,以获取子进程的退出状态。这可以通过wait()或waitpid()系统调用实现。

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

int main() {
    pid_t pid;
    pid = fork();
    if (pid < 0) {
        // fork failed
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // Child process
        printf("This is the child process, PID: %d\n", getpid());
        _exit(0);
    } else {
        // Parent process
        printf("This is the parent process, PID: %d\n", getpid());
        printf("Child process PID: %d\n", pid);
        int status;
        waitpid(pid, &status, 0);
        printf("Child process exited with status: %d\n", WEXITSTATUS(status));
    }
    return 0;
}

2. 处理僵尸进程

当子进程结束后,如果父进程没有调用wait()或waitpid(),子进程的进程表项会保留在系统中,形成僵尸进程。为了避免僵尸进程,可以在父进程中处理SIGCHLD信号。

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>

void sigchld_handler(int signum) {
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main() {
    signal(SIGCHLD, sigchld_handler);
    pid_t pid = fork();
    if (pid < 0) {
        // fork failed
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // Child process
        printf("This is the child process, PID: %d\n", getpid());
        _exit(0);
    } else {
        // Parent process
        printf("This is the parent process, PID: %d\n", getpid());
        printf("Child process PID: %d\n", pid);
        // Do some work in the parent process
        sleep(5);
    }
    return 0;
}

五、实践中的应用场景

1. 多进程并行处理

在多核处理器上,使用多个子进程可以充分利用CPU资源,提高程序的执行效率。

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

void do_work(int id) {
    printf("Process %d is doing work\n", id);
    sleep(2);
    printf("Process %d has finished work\n", id);
}

int main() {
    int num_processes = 4;
    pid_t pids[num_processes];
    for (int i = 0; i < num_processes; i++) {
        pid_t pid = fork();
        if (pid < 0) {
            // fork failed
            fprintf(stderr, "Fork failed\n");
            return 1;
        } else if (pid == 0) {
            // Child process
            do_work(i);
            _exit(0);
        } else {
            // Parent process
            pids[i] = pid;
        }
    }
    for (int i = 0; i < num_processes; i++) {
        waitpid(pids[i], NULL, 0);
    }
    printf("All processes have finished\n");
    return 0;
}

2. 创建守护进程

守护进程是一种在后台运行的进程,通常用于执行系统管理任务。创建守护进程时,通常需要使用fork()两次,以确保进程不会占用控制终端。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

void create_daemon() {
    pid_t pid = fork();
    if (pid < 0) {
        // fork failed
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        // Parent process
        exit(EXIT_SUCCESS);
    }
    // Child process becomes session leader
    if (setsid() < 0) {
        exit(EXIT_FAILURE);
    }
    // Ignore signal sent from child to parent process
    signal(SIGCHLD, SIG_IGN);
    pid = fork();
    if (pid < 0) {
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        exit(EXIT_SUCCESS);
    }
    // Set new file permissions
    umask(0);
    // Change the working directory to the root directory
    chdir("/");
    // Close all open file descriptors
    for (int x = sysconf(_SC_OPEN_MAX); x >= 0; x--) {
        close(x);
    }
    // Open log file
    open("/tmp/daemon.log", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
}

int main() {
    create_daemon();
    while (1) {
        sleep(30);
    }
    return 0;
}

六、总结

通过本文的介绍,我们详细了解了在C语言中创建子进程的几种方法,包括fork()、vfork()和posix_spawn()。其中,fork()是最常用的方法,它复制当前进程的所有资源,生成一个新的进程。vfork()是fork()的变体,适用于某些特定场景。posix_spawn()是POSIX标准提供的创建新进程的函数,适用于某些特定场景。我们还介绍了子进程的管理方法,包括等待子进程和处理僵尸进程,并展示了多进程并行处理和创建守护进程的实际应用场景。希望本文能为您在C语言编程中创建和管理子进程提供有价值的参考。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号