【Linux】fork函数的理解

fork函数总结

先说下fork函数
这里写图片描述

在Unix/Linux中用fork函数创建一个新的进程。进程是由当前已有进程调用fork函数创建,分叉的进程叫子进程,创建者叫父进程。该函数的特点是调用一次,返回两次,一次是在父进程,一次是在子进程。两次返回的区别是子进程的返回值为0,父进程的返回值是新子进程的ID。子进程与父进程继续并发运行。如果父进程继续创建更多的子进程,子进程之间是兄弟关系,同样子进程也可以创建自己的子进程,这样可以建立起定义关系的进程之间的一种层次关系。

1、子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。父子进程共享代码段,但是分别拥有自己的数据段和堆栈段

现在很多现实并不执行一个父进程数据段,栈和堆的完全复制。而是采用写时复制技术。这些区域有父子进程共享,而且内核将他们的访问权限改变为只读的。如果父子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储器系统中的一“页”。

2、一般来说,在fork之后的父进程先执行还是子进程先执行是不确定的。(取决于内核的调度算法)

子进程可以通过getppid函数获得自己父进程的ID,进程可以通过getpid函数获得自己的ID。

3、在linux中存在缓冲区的问题。当写printf函数,但是没有换行的话,它是不会输出的,而是先将要输出的内容存放再缓冲区中,当碰到换行时,将缓冲区中的内容再一起输出。

所以fork后子进程会复制父进程的缓冲区,因此也将待输出内容复制到自己“与父进程独立的缓冲区中”,因此当子进程执行遇到换行,就将缓冲区中的内容全都输出来。

程序1

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

int main(int argc, char *argv[])
{
    int i;
    pid_t pid;
    for(i=0; i<2; i++)
    {
        pid = fork();
        printf("-");
    }
    return 0;
}

先分析有几个进程:4 (丑图,别介意)
这里写图片描述

再分析输出

主进程执行了两次,i=0,i=2,所以输出两个 - -(总计:2个)

first是主进程的子进程,但是i=0时主进程fork 并没有执行printf,所以
缓冲区里面没有东西,first进程执行了两次printf是输出自己的两个 – -。(总计:2个)

second进程是i=1时主进程fork的子进程,此时主进程缓冲区已经有一个-,所以second 拷贝了一个 ,然后再执行一次printf,一共输出两个 - - 。(总计:2个)

third 是first进程的子进程,分析与second一样,输出两个 - -。(总计:2个)

所以一共输出八个 ——-

程序2、

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

int main(int argc, char *argv[])
{
    int i;
    pid_t pid;
    for(i=0; i<2; i++)
    {
        printf("-");
        pid = fork();
    }
    return 0;
}

注意:程序2与程序1不一样,printf在前,fork在后。
同样也是产生4个进程,同样输出八个-。但是原理与程序1不一样。
分析:
主进程与程序1 一样 同样输出两个–。

first进程会输出1个自己的中划线(i=1的时候执行了printf,i=0时没有执行printf,因为fork时从fork执行后开始执行代码 ),还会输出从主进程缓冲区拷贝过来的1个中划线(总计:2个)

second进程没有自己的中划线输出,会输出从主进程缓冲区拷贝过来的2个中划线(总计:2个)

third进程没有自己的中划线输出,会输出从first进程缓冲区拷贝过来的2个中划线 (总计:2个)

程序3

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

int main(int argc, char *argv[])
{
    int i;
    pid_t pid;
    for(i=0; i<2; i++)
    {
        printf("-\n");
        pid = fork();
    }
    return 0;
}

同样是四个进程。
分析:加了换行符,说明不会从缓冲区拷贝
fork执行后,子进程是从该函数之后继续执行代码
主进程:输出两个 - -。
first 进程,输出1个-,i=0时,后面没有代码,i=1时,先执行了printf输出了一个-。
second 是i=1时,主进程创建的子进程,但是此时second执行fork之后的代码,没有printf,所以不会输出。
third 与second 同理。
所以 总共输出3行短杠,一行一个


程序4、

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

int main(int argc, char *argv[])
{
    int i;
    pid_t pid;
    for(i=0; i<2; i++)
    {
        pid = fork();
        printf("-\n");     
    }
    return 0;
}

分析:同样是四个进程。

主进程,输出两行 - -。
first进程,输出两行- -,i=0,i=1分别输出短杠
second 进程,输出一行短杠,i=1时。
third进程,输出一行短杠,i=1,与second同理。
所以一共输出6行短杠。






关于fork函数就先介绍这里,欢迎大家与我交流。

<think> 首先,用户的问题是关于Linux系统中fork函数返回值的详细信息。我需要基于提供的引用来构建我的回答。 关键引用信息: - 引用[3]:fork函数会返回两个值: - 在父进程中,返回子进程的PID。 - 在子进程中,返回0。 - 如果失败,返回-1。 - 引用[4]:fork()的返回值用于判断代码运行在哪个进程。非零值表示子进程,0表示父进程。 - 引用[5]:fork执行后有两个进程,父进程和子进程有各自的变量和内存地址。 - 引用[1]和[2]提供了背景:fork用于创建子进程,实现并发。 我的回答需要: - 使用中文。 - 结构清晰,帮助用户逐步解决问题。 - 确保回答真实可靠,基于引用。 - 对于行内数学表达式,使用$...$格式(但fork返回值是整数,可能不需要数学表达式)。 - 独立公式使用$$...$$格式(但这里可能没有)。 - 末尾自然地添加引用标识。 - 生成相关问题。 回答结构: 1. **引言**:简要介绍fork函数。 2. **返回值详解**: - 在父进程中的返回值。 - 在子进程中的返回值。 - 失败时的返回值。 - 如何使用返回值区分进程。 3. **代码示例**:展示fork的使用。 4. **总结**。 5. **相关问题**:生成几个相关问题。 确保引用末尾添加标识: - 例如,在相关段落后添加[^1], [^2], 等。 生成相关问题: - 基于fork函数的主题,如错误处理、应用场景等。 - 例如: 1. fork函数失败的原因有哪些? 2. 如何在程序中处理fork的返回值? 3. fork函数在并发编程中的典型应用是什么? 用户的上一个问题可能是关于fork函数,但这里是直接问返回值,所以我要聚焦在返回值上。 </think>### Linux系统中fork函数返回值的详细介绍 在Linux系统编程中,`fork()`函数是一个核心的系统调用,用于创建子进程。`fork()`的返回值是区分父进程和子进程的关键机制,它提供了进程控制的基础。下面我将逐步解释其返回值的行为和原理,确保内容基于可靠的技术参考。 #### 1. **fork()函数的基本行为** - `fork()`调用后,系统会创建一个几乎与父进程完全相同的子进程(包括代码、数据和堆栈)。 - 关键点:`fork()`**只调用一次,但返回两次**: - 在**父进程**中,`fork()`返回新创建子进程的进程标识符(PID)。 - 在**子进程**中,`fork()`返回`0`。 - 如果创建失败(如系统资源不足),`fork()`在所有上下文中返回`-1`。 - 这种设计允许程序通过返回值判断当前代码运行在哪个进程: - 如果返回值是`非零值`(即子进程的PID),则代码在**父进程**中执行。 - 如果返回值是`0`,则代码在**子进程**中执行。 - 这种机制是Linux进程管理的基础[^3][^4]。 #### 2. **返回值详解** - **在父进程中的返回值**: - 父进程获得子进程的PID(一个正整数),例如`1234`。 - 这使父进程能通过`wait()`或`kill()`等函数管理子进程的生命周期。 - **在子进程中的返回值**: - 子进程获得`0`,表示它不是父进程。这避免了子进程错误地尝试创建自己的子进程。 - **失败时的返回值**: - 如果`fork()`失败(如进程数达到系统限制),它返回`-1`,并设置`errno`变量(如`ENOMEM`表示内存不足)。此时,应检查错误并处理。 - **为什么这样设计?** - 返回`0`给子进程简化了进程区分:子进程无需知道父进程的PID,而父进程需要子进程PID来管理它[^4]。 - 这种设计确保了进程间的独立性:父进程和子进程有各自的变量和内存空间,互不影响[^5]。 #### 3. **代码示例** 以下是一个简单的C语言示例,演示如何使用`fork()`的返回值: ```c #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main() { pid_t pid = fork(); // fork()调用 if (pid == -1) { perror("fork失败"); // 错误处理 return 1; } else if (pid > 0) { printf("父进程: 我的PID是%d, 子进程PID是%d\n", getpid(), pid); // 父进程逻辑 } else { printf("子进程: 我的PID是%d, 返回值是0\n", getpid()); // 子进程逻辑 } return 0; } ``` - **运行解释**: - 在父进程中,`pid`存储子进程的PID(如`5678`),打印父进程和子进程的PID。 - 在子进程中,`pid`为`0`,打印子进程自身的PID(通过`getpid()`获取)。 - 如果失败,程序输出错误信息。 - 这个示例展示了如何利用返回值实现并发任务分配[^1][^2]。 #### 4. **总结** `fork()`的返回值是Linux进程编程的核心特性: - 父进程通过PID管理子进程。 - 子进程通过`0`值避免递归创建。 - 返回值`-1`用于错误处理。 理解这一机制对于深入Linux并发编程至关重要,例如在多进程服务器或任务调度中的应用[^1][^5]。 ### 相关问题 1. `fork()`函数失败的原因有哪些?如何诊断和处理这些错误? 2. 在多进程程序中,如何安全地共享数据以避免竞争条件? 3. `fork()`在哪些实际应用场景中最常用?例如,Web服务器或并行计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值