news 2026/8/17 3:08:04

进程创建-fork和system函数使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进程创建-fork和system函数使用

进程创建-fork和system函数使用

文章目录

  • 进程创建-fork和system函数使用
  • 1、system()函数
  • 2、fork()函数
  • 3、继承关系:
  • 4、一些FAQ

一般情况下我们可以打开终端,直接执行./demo等命令执行一个程序,此时程序以进程的形式运行,大概率程序的父进程是执行此命令对应的shell进程。

实际应用中大概是在程序中完成进程的创建,简单常用的可以用system命令调用,比如system(“./demo &”) 或者使用fork创建一个子进程,本文介绍这两种方法的区别和使用注意事项。

1、system()函数

主程序在代码中执行system函数相当于利用shell新开一个独立的进程,此时shell的父进程为原始程序,system中执行的命令是新开一个进程,此时该命令的父进程为shell,相对主程序来说,system中执行的进程变成了孙子进程,如下:

主进程(PID=1000)
└── sh(PID=1001) # system启动的shell
└── sleep(PID=1002) # shell执行的命令

另外需要注意的是,sytem中使用&和不使用&存在区别,不使用&会阻塞主程序,直到system调用的程序运行结束才会执行主程序的代码。如果system调用的程序是一直运行的,需要加&符号后台运行,此时system会立即返回,不会阻塞主进程,但由于system返回了、会导致后台进程成为孤儿进程。

#include <stdlib.h> #include <stdio.h> int main() { printf("父进程开始\n"); // 情况1: 阻塞执行 printf("=== 阻塞执行 ===\n"); int ret1 = system("sleep 3 && echo '阻塞命令完成'"); printf("system返回: %d\n", ret1); // 情况2: 非阻塞执行(使用&) printf("\n=== 非阻塞执行 ===\n"); int ret2 = system("sleep 3 && echo '非阻塞命令完成' &"); printf("system立即返回: %d\n", ret2); printf("父进程继续执行...\n"); sleep(5); // 等待后台进程 return 0; }

2、fork()函数

fork函数是在主程序运行过程中直接创建子进程,需要关注的是fork函数是一次调用,两次返回,fork()的返回值有三种情况,它用来区分父进程和子进程,这种设计使得父进程和子进程可以在同一个代码中执行不同的逻辑路径,具体先执行父进程还是子进程受到操作系统调度影响,是非阻塞的

#include <unistd.h> #include <stdio.h> #include <sys/types.h> int main() { printf("准备调用fork()...\n"); pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "fork失败\n"); return 1; } else if (pid == 0) { // 子进程 printf("这是子进程:\n"); printf(" fork()返回值: %d\n", pid); // 应该是0 printf(" 自己的PID: %d\n", getpid()); printf(" 父进程PID: %d\n", getppid()); // 子进程可以执行不同的任务 sleep(1); // 模拟工作 printf("子进程结束\n"); return 10; // 子进程退出码 } else { // 父进程 printf("这是父进程:\n"); printf(" fork()返回值: %d (这是子进程的PID)\n", pid); printf(" 自己的PID: %d\n", getpid()); printf("父进程结束\n"); } return 0; }

程序的返回为:

准备调用fork()... 这是父进程: fork()返回值: 3683 (这是子进程的PID) 自己的PID: 3682 父进程结束 这是子进程: fork()返回值: 0 自己的PID: 3683 父进程PID: 3682 图解说明: 调用 fork() ↓ ┌───────────────┐ │ 创建子进程副本 │ └───────────────┘ ↓ 父进程继续执行 子进程开始执行 ↓ ↓ 返回子进程的PID 返回 0 ↓ ↓ 执行 pid > 0 分支 执行 pid == 0 分支 ↓ ↓ 各自独立运行,互不影响

fork函数在创建进程之后可以配合exec()的各种函数,即在返回的pid=0的分支调用exec函数执行自己新开的程序命令。

#include <unistd.h> // 函数原型 int execl(const char *path, const char *arg, ... /* (char *) NULL */); int execlp(const char *file, const char *arg, ... /* (char *) NULL */); int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[], char *const envp[]); l (list):参数以列表形式传递 v (vector):参数以数组形式传递 p (PATH):在PATH环境变量中查找程序 e (environment):可以指定新的环境变量

具体的函数用法可以网上搜,举两个常用简单例子:

//// execl pid_t pid = fork(); if (pid == 0) { // 子进程 execl("./codebin", "codebin", "code.cfg", NULL); // 第一个参数需要时=为程序的执行路径 // 如果exec失败,才会执行到这里 perror("execl失败"); exit(1); } //// execv pid_t pid = fork(); if (pid == 0) { // 子进程 char *args[] = {"./codebin", "code.cfg", NULL}; execv("./codebin", args); perror("execv失败"); exit(1);

3、继承关系:

system和fork在创建进程的时候会继承父进程的很多属性,例如:

1.环境变量:当前进程的所有环境变量都会被继承
2.当前工作目录:子进程会继承父进程的工作目录
3.进程组和会话:通常在同一会话和进程组中
4.信号处理:某些信号处理方式会继承
5.文件描述符:打开的文件描述符会继承(标准输入/输出/错误除外,会被重定向)等

demo例子:

#include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> int main() { // 环境变量 - 会被继承 setenv("MY_VAR", "parent_value", 1); // 文件描述符 - 会被继承 int fd = open("test.txt", O_CREAT | O_WRONLY, 0644); write(fd, "来自父进程\n", 12); // 进程组、会话、工作目录等 - 会被继承 printf("父进程PID: %d, 工作目录: %s\n", getpid(), getcwd(NULL, 0)); pid_t pid = fork(); if (pid == 0) { // 子进程 printf("子进程PID: %d\n", getpid()); printf("环境变量MY_VAR: %s\n", getenv("MY_VAR")); // 可以访问父进程打开的文件 write(fd, "来自子进程\n", 12); close(fd); // 改变环境变量(只影响子进程) setenv("MY_VAR", "child_value", 1); // 执行新程序 char *argv[] = {"./other_program", NULL}; char *envp[] = {"MY_VAR=exec_value", "PATH=/bin", NULL}; // 不同的exec变体提供不同的控制 // execv("./other_program", argv); // 继承所有环境变量 // execve("./other_program", argv, envp); // 指定新环境变量 // execl("/bin/ls", "ls", "-l", NULL); // 列表参数 exit(0); } else { // 父进程的环境变量不变 printf("父进程中的MY_VAR: %s\n", getenv("MY_VAR")); close(fd); wait(NULL); } return 0; }

4、一些FAQ

继承有时候可能存在问题,比如子进程如果一直占用某些文件描述符,即使父继承显示关闭了,需要卸载驱动或者其它不允许设备被占用的操作都容易出现问题,这种情况下不需要继承的fd设置FD_CLOEXEC,会在调用exec函数的时候自动关闭

问题1:子进程可以继承父进程打开的文件描述符吗?

答:可以

  • 子进程获得父进程文件描述符表的副本
  • 相同的fd编号指向相同的文件表项

问题2:会因为父进程占用了而打不开吗?

答:不会

  • 每个进程有自己的文件描述符表
  • 多个进程可以同时打开同一个文件
  • 限制通常来自系统级(如打开文件总数限制)

问题3:子进程继承后,父进程关闭,子进程还在占用吗?

答:是的

  • 每个fd有独立的引用计数
  • 父进程关闭只减少自己那份的引用计数
  • 子进程的引用仍然存在

问题4:此时其它进程能占用吗?

答:能

  • 其他进程可以正常打开同一个文件
  • 文件是否真正被"占用"取决于文件锁

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 23:56:55

HoRain云--JavaScript导航神器:玩转WindowLocation

&#x1f3ac; HoRain 云小助手&#xff1a;个人主页 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站&#xff0c;性价比超高&#xff0c;大内存超划算&#xff01;忍不住分享一下给大家。点击跳转到网站。 目录 ⛳️ 推荐 …

作者头像 李华
网站建设 2026/8/17 0:57:08

【面板数据】工业机器人进出口数据集(2012.1-2025.10)

我国工业机器人产量第一&#xff0c;应用广泛。产业规模全球领先&#xff1a;我国已成为全球最大机器人生产国&#xff0c;产量实现跨越式增长&#xff0c;从2015年的3.3万套跃升至2024年的55.6万套&#xff1b;应用深度与广度显著提升&#xff1a;工业机器人已覆盖国民经济71个…

作者头像 李华
网站建设 2026/8/16 0:57:04

vue3 新建文件store自动导入

store下新增个index.js用来做自动导入&#xff08;pinia使用可参考之前这篇文章 //使用pinia来管理全局状态 import { createPinia } from pinia // 自动导入所有 store 文件 const modulesFiles import.meta.glob(./modules/*.js, { eager: true }) const stores {}for (co…

作者头像 李华
网站建设 2026/8/16 12:20:20

资金管理平台的核心业务场景中,凡是涉及资金权属变动、资金形态转换、资金成本 / 收益确认的操作,都会触发会计核算需求。这些场景的核算结果需同步至财务系统(如 SAP FI 模块),确保资金流与账务流的

资金管理平台的核心业务场景中&#xff0c;凡是涉及资金权属变动、资金形态转换、资金成本 / 收益确认的操作&#xff0c;都会触发会计核算需求。这些场景的核算结果需同步至财务系统&#xff08;如 SAP FI 模块&#xff09;&#xff0c;确保资金流与账务流的一致性。结合软件外…

作者头像 李华
网站建设 2026/8/16 7:53:21

5.5 信息论在机器学习中的应用:正则化、特征选择与模型比较

5.5 信息论在机器学习中的应用:正则化、特征选择与模型比较 信息论不仅为理解和量化信息提供了坚实的数学基础,其核心概念——熵、互信息和Kullback-Leibler散度——更在机器学习的算法设计、理论分析和实际应用中扮演着至关重要的角色。这些概念超越了其通信理论的起源,成…

作者头像 李华
网站建设 2026/8/16 7:51:19

一文搞懂 Function Calling、MCP、A2A 和 Skills

之前我们已经单独介绍了MCP、Fuction Calling、A2A乃至&#xff08;Claude&#xff09;Skills。 但是很多粉丝依旧觉得有些懵逼&#xff0c;我想了想原因&#xff0c;大概是单点知识不具备连贯性&#xff0c;要把他们完全搞懂&#xff0c;可能还是要从全局出发、从目的出发。 追…

作者头像 李华