fork函数详解

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

文章目录

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多进程调试

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6