fork函数详解

fork函数

fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。

通过man命令简单查看一下:man fork

在这里插入图片描述

基本概念

  • 每个进程都由进程号来标识,其类型为 pid_t(整型),进程号的范围:0~32767。进程号总是唯一的,但可以重用。当一个进程终止后,其进程号就可以再次使用。

  • 任何进程(除 init 进程)都是由另一个进程创建,该进程称为被创建进程的父进程,对应的进程号称为父进程号(PPID)。

  • 进程组是一个或多个进程的集合。他们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有一个进程组号(PGID)。默认情况下,当前的进程号会当做当前的进程组号

  • 进程号和进程组相关函数

    • pid_t getpid(void);:获取进程ID
    • pid_t getppid(void);:获取进程的父进程ID
    • pid_t getpgid(pid_t pid);:获取进程的组ID

fork()函数用法

pid_t fork(void);
    函数的作用:用于创建子进程。
    包含头文件:
    	#include <sys/types.h>
		#include <unistd.h>
    返回值:
        fork()的返回值会返回两次。一次是在父进程中,一次是在子进程中。
        在父进程中返回创建的子进程的ID,
        在子进程中返回0
        在父进程中返回-1,表示创建子进程失败,并且设置errno

例子详解

先来看一个最简单的例子

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() 
{

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {
        printf("pid : %d\n", pid);
        // 如果大于0,返回的是创建的子进程的进程号,当前是父进程
        printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
    } else if(pid == 0) {
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());

    }
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
        sleep(1);
    }
    return 0;
}

输出结果

(base) user@ubuntu:~/Desktop/OS/NiuKe$ gcc -o test test.c  -std=c99
(base) user@ubuntu:~/Desktop/OS/NiuKe$ ./test 
pid : 3274
i am parent process, pid : 3273, ppid : 2077
i : 0 , pid : 3273
i am child process, pid : 3274, ppid : 3273
i : 0 , pid : 3274
i : 1 , pid : 3273
i : 1 , pid : 3274
i : 2 , pid : 3273
i : 2 , pid : 3274

这个程序比较简单,唯一值得注意的是输出结果的这个语句i : 0 , pid : 3273

原因是fork之后父进程先执行还是子进程先执行不确定,取决于内核所使用的调度算法。

program

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

int main()
{
    int n=2;
    for(int i=0;i<n;i++)
    {
        fork();
        printf("A\n");
    }
    return 0;
}

输出结果

(base) user@ubuntu:~/Desktop/OS/NiuKe$ gcc -o fork fork.c  -std=c99
(base) user@ubuntu:~/Desktop/OS/NiuKe$ ./fork 
A
A
A
A
A
A

代码共打印6个A,创建3个子进程。
在这里插入图片描述
【注意】:‘\n’:会自动刷新缓冲区。即遇到\n会自动刷新缓冲区

program

我们把上面程序的\n删除掉在试一下。

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

int main()
{
    int n=2;
    for(int i=0;i<n;i++)
    {
        fork();
        printf("A");
    }
    return 0;
}

输出结果

(base) user@ubuntu:~/Desktop/OS/NiuKe$ gcc -o fork fork.c  -std=c99
(base) user@ubuntu:~/Desktop/OS/NiuKe$ ./fork 
AAAA(base) user@ubuntu:~/Desktop/OS/NiuKe$ AAAA

代码共打印8个A。

在这里插入图片描述

工作原理

  • Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。
  • 内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间,只有在需要写入的时候才会复制地址空间,从而使各个进程拥有各自的地址空间。即资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享
  • fork之后父子进程共享文件。fork产生的子进程与父进程有相同的文件描述符,指向相同的文件表,引用计数增加,共享文件偏移指针。

GDB 多进程调试

  • 使用 GDB 调试的时候,GDB 默认只能跟踪一个进程,可以在 fork 函数调用之前,通过指令设置 GDB 调试工具跟踪父进程或者是跟踪子进程,默认跟踪父进程

  • 设置调试父进程或者子进程:set follow-fork-mode [parent(默认)| child]

  • 设置调试模式:set detach-on-fork [on | off]
    默认为 on,表示调试当前进程的时候,其它的进程继续运行,如果为 off,调试当前进程的时候,其它进程被 GDB 挂起。

  • 查看调试的进程:info inferiors

  • 切换当前调试的进程:inferior Num

  • 使进程脱离 GDB 调试:detach inferiors Num

参考资料:89-多进程开发:GDB多进程调试

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值