fork()函数介绍 原理解析

建议先看看页表是什么   (页表介绍链接)
fork()

在Linux中,创建进程可以使用fork()系统调用。fork()系统调用的作用是在当前进程中创建一个新进程,新进程与当前进程具有相同的内存空间和文件描述符,但拥有独立的进程ID和资源。它是实现多进程并发执行的基础。

fork()系统调用语法如下:

#include  
int fork();

fork()系统调用返回值如下:

  • 如果返回值是0,表示新创建的子进程调用fork()系统调用成功,并返回0。

  • 如果返回值是子进程的进程ID,表示父进程(一般是所在程序的进程)调用fork()系统调用成功,并返回子进程的进程ID。

  • 如果返回值是-1,表示调用`fork()系统调用失败。

  • fork()函数在调用时会返回两次,一次在父进程中返回子进程的PID,一次在子进程中返回0。根据这两个返回值可以区分父进程和子进程,并在不同的进程中执行不同的逻辑。

getpid() getppid()

#include 
#include 
​
int main() {
    pid_t pid = fork(); // 创建子进程
    if (pid == 0) { // 子进程
        printf("I am child process, pid = %d\n", getpid());
    } else if (pid > 0) { // 父进程
        printf("I am parent process, ppid = %d, child pid = %d\n", getppid(), pid);
    } else { // fork()调用失败
        printf("fork() error\n");p
        return 1;
    }
    return 0;
}

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

#include 
#include 
​
int main() {
    pid_t pid = fork();
fork的实现原理(写实拷贝)

fork() 函数的实现原理是写实拷贝:

  1. 创建子进程: 当fork()函数被调用时,操作系统会为新进程分配一个新的进程标识符(PID),创建基本相同的PCB,PCB中的进程地址空间指向的是父进程PCB的进程地址空间结构体地址,创建新的页表,子进程共享父进程页表的一部分页表项。如果设置子进程的权限等等属性,会在页表创建新的页表项,修改其中的权限位。

  2. 更新权限位:父进程的物理地址空间被划分为多个页面(通常是4KB大小),这些页面被标记为“只读”。这意味着新进程(子进程和父进程都)不能直接修改这些页面的内容。而父子进程接下来新建的页面都被标记位“读-写”(RW)

  3. 内存的副本: 无论谁先修改先前的数据,都会遇到只读的问题。这里就会产生异常,会触发页面错误(Page Fault),操作系统会捕获页面错误,并为进程在物理内存中复制一份要修改的页帧,修改其权限和原本页的权限为(RW),然后更新进程的页表,使其指向新的页面。这样子进程和父进程都会有一个独立的、可读写的页面,可以直接修改它们的内容。

  4. 返回值: fork() 函数对于父进程返回新的子进程ID,对于子进程返回0。这种返回值的不同使得父进程和子进程可以根据返回值来执行不同的代码路径。

  5. 执行继续: 在子进程中,程序计数器通常设置为父进程代码的下一条指令,子进程将从这里开始执行。在父进程中,fork() 调用之后的代码将立即继续。(存储在代码段的代码是共享的

  6. 资源管理: 子进程虽然复制了父进程的文件描述符,但实际上它是独立的。子进程需要打开文件以读取或写入时,它会获得自己的文件描述符表项。(通过页表的标志位来进行管理)

  7. 父子关系: 子进程和父进程之间存在一种父子关系,操作系统会维护这种关系,使得父进程可以等待子进程结束,或者子进程可以发送信号给父进程。

在 Linux 操作系统中,fork() 系统调用的实现涉及低级的系统调用和内核操作。具体来说,它调用的是 do_fork() 函数,在内核中完成实际的新进程创建工作。此函数会遍历进程描述符数组,找到合适的位置来插入新进程的描述符,并初始化这个描述符。之后,会根据当前进程的状态创建子进程的内存空间,并设置好相关的寄存器和返回值。

父子进程的运行顺序

取决于操作系统的调度机制(调度器)

如果在编写代码时需要特定的运行顺序,可以使用进程间同步机制,如使用父子进程之间的管道进行通信、使用信号量进行同步等。这样可以确保父子进程按照所需的顺序进行交互和执行。

调度器

调度器是操作系统中负责进程或线程调度管理的组件。它的主要职责是根据一定的调度策略,选择一个进程或线程执行,并控制其执行时间和资源分配。调度器可以基于时间片、优先级、用户态还是内核态等因素进行调度。常见的操作系统调度器有轮转调度算法、优先级调度算法、时间片轮转调度算法等。

你可能感兴趣的:(重要知识点块,c语言,算法,开发语言,linux)