• fork函数


    在这里插入图片描述

    二.fork函数

    2.1函数原型

    fork()函数在 C 语言中的原型如下:

    #include 
    
    pid_t fork(void);
    
    • 1
    • 2
    • 3

    其中pid_t是一个整型数据类型,用于表示进程ID。fork()函数返回值是一个pid_t类型的值,具体含义如下:

    • 如果调用fork()的进程是父进程,则返回子进程的进程ID(PID)。
    • 如果调用fork()的进程是子进程,则返回0。
    • 如果出现错误,fork()返回-1。

    通过检查fork()函数的返回值,可以判断当前代码是在父进程中还是在子进程中,并根据不同的返回值执行不同的代码逻辑。

    需要注意的是,fork()函数需要包含头文件才能进行调用。此外,在使用fork()函数时,应格外注意处理可能出现的错误情况。例如,当系统资源耗尽时,fork()可能会返回-1,表示创建子进程失败。

    2.2fork函数功能

    fork()函数的功能是创建一个新的子进程,该子进程是调用进程的几乎完全副本。具体功能和特点如下:

    1. 进程复制:fork()函数在调用进程中创建一个新的子进程,该子进程几乎完全复制了父进程的所有内容,包括代码、全局变量、堆、栈、文件描述符等。子进程是父进程的副本。

    2. 独立执行:fork()函数调用后,父进程和子进程分别继续执行,彼此之间的执行是相互独立的。它们有各自的内存空间和资源管理。

    3. 返回值区分:根据fork()函数的返回值,可以在父进程和子进程中执行不同的代码路径。在父进程中,fork()返回子进程的进程ID(PID),在子进程中,fork()返回0。可以根据返回值进行条件判断,以实现不同的代码逻辑。

    4. 进程间共享和隔离:父进程和子进程之间共享某些资源,如打开的文件描述符。这种共享机制可以用于进程间通信和共享状态。但同时,子进程是父进程的副本,它们之间的修改不会相互影响,各自拥有独立的虚拟内存空间。

    通过使用fork()函数,可以实现以下功能:

    • 创建并发执行的多个进程,用于处理并行任务或任务分割。
    • 实现简单的进程间通信和共享资源。
    • 实现守护进程等特殊的进程模式。
    • 创建进程树,用于实现复杂的进程关系和层次结构。

    需要注意的是,在实际使用fork()函数时,需要注意处理可能出现的错误情况,如资源耗尽或其他系统限制。

    2.3fork函数特性

    fork()函数具有以下几个特性:

    1. 创建子进程:fork()函数用于创建一个与父进程几乎完全相同的子进程。子进程从fork()函数的返回处开始执行,而父进程继续执行fork()之后的代码。

    2. 独立的执行环境:父进程和子进程在fork()后分别独立地执行,彼此之间的执行是相互独立的。它们有各自的内存空间和资源管理,所以它们的状态互不干扰。

    3. 返回值区分:根据fork()函数的返回值,可以在父进程和子进程中执行不同的代码逻辑。在父进程中,fork()返回子进程的进程ID(PID),在子进程中,fork()返回0。通过判断返回值,可以实现父子进程的不同分支逻辑。

    4. 共享和隔离的资源:父进程和子进程之间通过fork()函数共享某些资源,如打开的文件描述符。这意味着它们可以共享一些数据和状态。但同时,子进程是父进程的副本,它们之间的修改不会相互影响,各自拥有独立的虚拟内存空间。

    5. 进程树的形成:通过反复调用fork()函数,可以创建更多的子进程,从而形成进程树结构。子进程可以再次调用fork()创建更多的子进程,形成更复杂的进程关系和层次结构。

    需要注意的是,在使用fork()函数创建子进程时,应当避免资源泄漏和竞争条件等问题,并妥善处理可能出现的错误情况。同时,对于父子进程之间的通信和同步,可以使用其他机制,如管道、共享内存、信号等。

    2.4fork案例

    下面是一个使用fork()函数创建子进程的简单示例:

    #include 
    #include 
    
    int main() {
        pid_t pid;
    
        pid = fork();
    
        if (pid == -1) {
            // 创建子进程失败
            perror("fork");
            return 1;
        } else if (pid == 0) {
            // 子进程代码
            printf("这是子进程(PID:%d)\n", getpid());
            printf("子进程结束\n");
        } else {
            // 父进程代码
            printf("这是父进程(PID:%d),创建了子进程(PID:%d)\n", getpid(), pid);
            printf("父进程结束\n");
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行上述代码后,输出的结果可能如下:

    这是父进程(PID:1234),创建了子进程(PID:1235)
    父进程结束
    这是子进程(PID:1235)
    子进程结束
    
    • 1
    • 2
    • 3
    • 4

    在这个案例中,程序首先调用了fork()函数,在父进程中会得到一个非负的子进程PID,而在子进程中则会得到0。之后,根据返回值不同,在父进程和子进程中分别输出不同的信息。最后,在每个进程中都输出进程结束的信息。

    这个案例展示了fork()函数的基本用法,创建了一个父进程和子进程,它们具有相同的代码和执行流程,但是可以根据进程的不同,执行不同的代码路径。

  • 相关阅读:
    LVS+keepalived——高可用集群
    详细讲解RNN+LSTM+Tree_LSTM(Tree-Long Short Term Memory)基于树状长短期记忆网络
    【CMake】add_dependencies 命令
    【技术指南资料】编码器与正交译码器
    Java:JVM基础
    AM@无穷小和无穷大
    浅析kubernetes中client-go structure01
    如何制定测试团队度量体系
    Pytorch入门实战(8):小样本学习实现图片分类(Few-shot Learning, Meta Learning)
    Redis 的6种回收策略(淘汰策略)详解
  • 原文地址:https://blog.csdn.net/m0_73731708/article/details/133048392