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

016 进程控制 —— 进程创建

🦄 个人主页: 小米里的大麦-CSDN博客
🎏 所属专栏: Linux_小米里的大麦的博客-CSDN博客
🎁 GitHub主页: 小米里的大麦的 GitHub
⚙️ 操作环境: Visual Studio 2022

在这里插入图片描述

文章目录

    • 进程控制 —— 进程创建
      • 一、`fork()` 函数基础
        • 1. `fork()` 的作用
        • 2. 写时拷贝(Copy-On-Write, COW)
      • 二、代码示例
        • 示例 1:基础 `fork()` 使用
        • 示例 2:循环创建多个子进程
      • 三、`fork()` 常见问题
        • 1. `fork()` 失败的原因
        • 2. 避免子进程成为僵尸
        • 3. 父子进程共享的资源
      • 四、进阶用法
        • 1. 父子进程分工
        • 2. 链式创建进程
        • 2. 链式创建进程
    • 共勉

进程控制 —— 进程创建

一、fork() 函数基础

1. fork() 的作用
  • 创建子进程:通过复制父进程的地址空间生成一个新进程。
  • 调用一次,返回两次
    • 父进程返回子进程的 PID(即 > 0 or 正数)。
    • 子进程返回 0。
    • 失败返回 -1。
pid_t fork(void);
2. 写时拷贝(Copy-On-Write, COW)

image-20250405192403735

  • 机制fork 时不会立刻复制父进程的所有内存页。fork() 后,父子进程 共享物理内存(共享内存页(只读)),直到一方尝试修改数据时,内核才复制该内存页。

    • 修改时触发“页错误”
    • 操作系统才会为该进程分配新的物理页,完成“真正拷贝”
  • 优点

    • 提升效率: 减少 fork() 的开销(避免立即复制全部内存)。
    • 节省内存开销: 节省物理内存(共享未修改的页)。

[!NOTE]

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图:

image-20250714185539037


二、代码示例

示例 1:基础 fork() 使用
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define N 5
int main()
{printf("pid:%d before!\n", getpid());  // fork 调用前,打印一次fork();                                // 创建子进程printf("pid:%d after!\n", getpid());   // 父子进程都会执行这一句return 0;
}

运行结果

image-20250405184713829

说明:
pid:31612 before!      # 父进程打印
pid:31612 after!       # 父进程打印
pid:31613 after!       # 子进程打印(PID不同)

关键点

image-20250714185622229

  • fork() 前的代码仅父进程执行,之后的代码父子进程均执行。
  • 父子进程的 printf 输出顺序不确定,由调度器决定。

示例 2:循环创建多个子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>              // 定义 pid_t 类型
#include <unistd.h>                 // 定义 fork() 函数
#define N 5
void runChild()
{int cnt = 10;while (cnt){printf("我是子进程:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}
}int main()
{int i = 0;					    // 避免 C99 不支持报错for (i = 0; i < N; i++){pid_t pid = fork();			// 创建子进程if (pid == 0)               // 子进程进入{runChild();             // 子进程执行任务exit(0);                // 子进程退出,防止继续 fork}else if (pid < 0)           // fork失败{perror("fork");exit(1);}// 父进程继续循环}return 0;
}

运行结果

我是子进程:1761, ppid:1760
我是子进程:1762, ppid:1760
我是子进程:1763, ppid:1760 
我是子进程:1764, ppid:1760
我是子进程:1765, ppid:1760
(每个子进程完成10次输出后)

关键点

  1. 子进程立即退出循环:通过 if (pid == 0) 确保子进程执行 runChild() 后调用 exit(0),避免子进程继续 for 循环。
  2. 父进程管理子进程:父进程在循环中创建所有子进程后退出。但是父进程没有调用 wait 回收子进程,子进程结束后将变成僵尸进程。
  3. 并发执行:所有子进程同时运行,输出顺序交错(由调度器决定)。

三、fork() 常见问题

1. fork() 失败的原因
  • 系统限制:进程数超过 RLIMIT_NPROC 限制。
  • 内存不足:无法复制页表或分配 PID。
  • 资源耗尽:如内核进程表满。

如果 fork() 返回 -1,通常是以下原因:

原因描述
进程数超出限制系统有最大进程数限制(ulimit -u
内存不足无法分配页表或必要资源
权限问题某些系统限制普通用户创建大量进程
系统负载过高为了保护系统稳定性,内核可能拒绝 fork

建议加上错误处理:

if (pid < 0)
{perror("fork failed");exit(1);
}
2. 避免子进程成为僵尸
  • 父进程需调用 wait()waitpid() 回收子进程资源。
  • 或忽略 SIGCHLD 信号:
    signal(SIGCHLD, SIG_IGN);  // 自动回收子进程
    
3. 父子进程共享的资源
  • 共享
    • 文件描述符(打开的文件)。
    • 信号处理函数(但信号掩码独立)。
  • 独立
    • 内存数据(因 COW 机制)。
    • 进程 ID、父进程 ID。

四、进阶用法

1. 父子进程分工
pid_t pid = fork();
if (pid == 0)
{execvp("ls", (char* []) { "ls", "-l", NULL });  // 子进程执行任务
}
else
{wait(NULL);                                     // 父进程等待子进程
}
2. 链式创建进程
pid_t pid = fork();
if (pid == 0)
{execvp("ls", (char* []) { "ls", "-l", NULL });  // 子进程执行任务
}
else
{wait(NULL);                                     // 父进程等待子进程
}
2. 链式创建进程
for (int i = 0; i < N; i++)
{if (fork() == 0){printf("Child %d\n", i);exit(0);}
}

共勉

在这里插入图片描述
在这里插入图片描述

http://www.lryc.cn/news/588348.html

相关文章:

  • ShenYu实战、问题记录
  • Spring Boot 自带的 JavaMail 集成
  • 文心一言 4.5 开源深度剖析:中文霸主登场,开源引擎重塑大模型生态
  • 分布式光伏并网中出现的电能质量问题,如何监测与治理?
  • 时序预测 | Pytorch实现CNN-LSTM-KAN电力负荷时间序列预测模型
  • MongoDB从入门到精通
  • [Nagios Core] 事件调度 | 检查执行 | 插件与进程
  • 【Linux】Linux 操作系统 - 28 , 进程间通信(四) -- IPC 资源的管理方式_信号量_临界区等基本概念介绍
  • Excel常用快捷键与功能整理
  • 《恋与深空》中黑白羽毛是谁的代表物?
  • 【前端】【分析】前端功能库二次封装:组件与 Hook 方式的区别与好处分析
  • 体验RAG GitHub/wow-rag
  • 国内MCP服务器搜索引擎有哪些?MCP导航站平台推荐
  • 基于cornerstone3D的dicom影像浏览器 第一章,新建vite项目,node版本22
  • 了解 Java 泛型:简明指南
  • yolo8+声纹识别(实时字幕)
  • ArkTs实现骰子布局
  • Pandas-特征工程详解
  • WinUI3开发_Combobox实现未展开时是图标下拉菜单带图标+文字
  • Java-ThreadLocal
  • Apache-web服务器环境搭建
  • 机器学习(ML)、深度学习(DL)、强化学习(RL):人工智能的三驾马车
  • 基于Snoic的音频对口型数字人
  • PyTorch 数据加载全攻略:从自定义数据集到模型训练
  • 7月14日作业
  • 选择一个系统作为主数据源的优势与考量
  • 【数据结构】基于顺序表的通讯录实现
  • Hello, Tauri!
  • The Network Link Layer: WSNs 泛洪和DSR动态源路由协议
  • Python:打造你的HTTP应用帝国