当前位置: 首页 > article >正文

Linux 创建进程 fork()、vfork() 与进程管理

Linux 创建进程 fork、vfork、进程管理

    • 一、Linux的0号、1号、2号进程
    • 二、Linux的进程标识
    • 三、fork() 函数
      • 1、基本概念
      • 2、函数特点
      • 3、用法以及应用场景
        • (1)父子进程执行不同的代码
        • (2)进程执行另一个程序
      • 4、工作原理
    • 四、vfork() 函数
      • 1、基本概念
      • 2、函数特点
      • 3、用法以及应用场景
      • 4、工作原理
    • 五、fork 与 vfork 的比较
    • 六、父子进程共享文件的场景
      • 示例:父子进程共享文件描述符

在这里插入图片描述

在Linux操作系统中,进程是资源分配的基本单位。进程的创建是系统正常运行的重要组成部分。通过理解进程创建的机制,开发者能够更高效地控制系统的资源和多任务处理

一、Linux的0号、1号、2号进程

在Linux操作系统中,进程以树状结构组织。最重要的进程包括0号、1号和2号进程,它们具有特殊的意义:

  • 0号进程(系统进程):也被称为swapperidle进程,是所有进程的祖先,负责初始化系统的基本工作。
  • 1号进程(systemd):是系统的初始化进程,负责系统的启动、硬件初始化以及进程管理。它是所有其他进程的父进程。
  • 2号进程(kthreadd):是内核线程的管理者,负责内核级线程的创建、管理和调度。

这些进程在系统启动时就已经存在,确保了系统的正常运行。

二、Linux的进程标识

每个进程在Linux系统中都有一个唯一的进程标识符(PID)。进程标识符用于标识和管理进程。除了PID外,还有一些相关的进程标识符:

  • PPID(父进程ID):指向父进程的PID。
  • PGID(进程组ID):用于管理进程组,多个进程可以组成一个进程组。
  • SID(会话ID):代表一组进程的会话标识,通常与终端的会话关联。

在Linux中,进程的创建通常使用fork()系统调用来实现。

三、fork() 函数

1、基本概念

fork()是Unix/Linux系统中用于创建新进程的一个系统调用。调用fork()时,当前进程(父进程)会复制一份新的进程(子进程)。子进程将继承父进程的大部分资源(如文件描述符、环境变量等)。

2、函数特点

  • 进程复制fork()会复制父进程的虚拟地址空间,但父子进程的实际物理地址不同。
  • 内存副本:父进程的堆栈空间和数据空间会被复制到子进程中,父子进程的数据并不共享。
  • 顺序不确定fork()调用后,父子进程的执行顺序是不可预知的。父进程可能先执行,也可能子进程先执行。

3、用法以及应用场景

(1)父子进程执行不同的代码

在调用fork()后,父子进程各自从fork()返回的位置继续执行代码。父进程和子进程通常执行不同的操作。

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

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        // 错误处理
        perror("fork failed");
        return 1;
    }

    if (pid == 0) {
        // 子进程执行
        printf("This is the child process. PID: %d\n", getpid());
    } else {
        // 父进程执行
        printf("This is the parent process. PID: %d\n", getpid());
    }

    return 0;
}

运行该代码时,父子进程会并行执行,打印各自的进程ID。

(2)进程执行另一个程序

父进程可以通过fork()创建子进程,然后使用exec()系列函数来让子进程执行不同的程序。这在处理多任务时非常常见。

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

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork failed");
        return 1;
    }

    if (pid == 0) {
        // 子进程执行新的程序
        execlp("/bin/ls", "ls", "-l", NULL);
        // 如果 execlp 调用成功,下面的代码不会执行
        perror("execlp failed");
    } else {
        printf("Parent process is running. PID: %d\n", getpid());
    }

    return 0;
}

在此示例中,子进程通过execlp()调用执行ls命令。

4、工作原理

fork()系统调用的工作原理基于复制父进程的虚拟内存空间并为子进程创建一个新的进程。虽然父子进程的虚拟地址空间相同,但它们是不同的物理地址。

四、vfork() 函数

1、基本概念

vfork()函数和fork()类似,也用于创建子进程。但是,vfork()不会复制父进程的虚拟内存,而是让父进程暂时停止执行,直到子进程调用exec()_exit()。这种行为使得vfork()fork()更高效,尤其在子进程即将调用exec()时。

2、函数特点

  • 共享内存空间vfork()创建的子进程共享父进程的地址空间,直到子进程调用exec()_exit()
  • 父进程暂停:父进程会暂停执行,直到子进程完成某些操作(通常是执行新程序或退出)。

3、用法以及应用场景

vfork()通常用于需要子进程快速调用exec()的场景,能够提高效率。

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

int main() {
    pid_t pid = vfork();

    if (pid < 0) {
        perror("vfork failed");
        return 1;
    }

    if (pid == 0) {
        // 子进程执行
        execlp("/bin/ls", "ls", "-l", NULL);
        perror("execlp failed");
    } else {
        printf("Parent process is waiting. PID: %d\n", getpid());
    }

    return 0;
}

4、工作原理

vfork()会让子进程与父进程共享内存空间,父进程在子进程调用exec()_exit()之前会被挂起,从而避免了不必要的内存复制操作。

五、fork 与 vfork 的比较

特性fork()vfork()
内存复制复制父进程的内存子进程和父进程共享内存空间
父进程执行父子进程并行执行父进程暂停,直到子进程执行完毕
性能较低(内存复制开销)较高(减少内存复制,提高效率)
适用场景适用于父子进程都需要独立运行的情况适用于子进程会调用exec()的情况

六、父子进程共享文件的场景

在Linux中,父进程和子进程共享文件描述符,这意味着它们对同一个文件的操作可能相互影响。例如,如果父进程和子进程都操作同一个文件描述符,文件的偏移量可能会改变,从而导致输出混合。为了避免这种情况,必须显式地同步文件操作。

示例:父子进程共享文件描述符

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

int main() {
    int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("File open failed");
        return 1;
    }

    pid_t pid = fork();

    if (pid < 0) {
        perror("fork failed");
        return 1;
    }

    if (pid == 0) {
        // 子进程写入文件
        write(fd, "Hello from child\n", 17);
    } else {
        // 父进程写入文件
        write(fd, "Hello from parent\n", 18);
    }

    close(fd);
    return 0;
}

在这个示例中,父进程和子进程共享fd文件描述符。如果没有适当的同步机制,写入可能会交替进行,从而产生不一致的结果。


http://www.kler.cn/a/539270.html

相关文章:

  • 增量hdfs数据追平
  • Games202 Lecture11 LTC | Disney principled BRDF | NPR
  • 【C++】命名空间
  • 在 Open WebUI+Ollama 上运行 DeepSeek-R1-70B 实现调用
  • 使用 Postman 进行 API 测试:从入门到精通
  • Sentinel——Spring Boot 应用接入 Sentinel 后内存开销增长计算方式
  • python基础入门:3.5实战:词频统计工具
  • 问卷数据分析|SPSS之分类变量描述性统计
  • 深入浅出:SSL证书的作用与重要性
  • 第二十一章:考研的艰难抉择与放弃入学的转折
  • 基于javaweb的SpringBoot+MyBatis毕业设计选题答辩管理系统(源码+文档+部署讲解)
  • PromptSource安装报错
  • 科普书《从一到无穷大》的科普知识推翻百年集论
  • PlantUml常用语法
  • 青少年编程与数学 02-009 Django 5 Web 编程 02课题、开发环境
  • DeepSeek在无人机上应用技术详解
  • leetcode_80删除有序数组中的重复项 II
  • 【算法】快速排序算法的实现:C 和 C++ 版本
  • 信息学奥赛一本通1003
  • 神经网络常见激活函数 6-RReLU函数
  • 每日一题--数组中只出现一次的两个数字
  • Python 入门:文件操作、读写、管理
  • UIAbility 生命周期方法
  • Spring Boot快速开发
  • python migate执行报错
  • 山东大学软件学院人机交互期末复习笔记