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

Linux应用层开发--进程处理

ps -ef    可以看父进程和子进程

1、 进程处理相关系统调用


system 函数底层调用

system() 函数会间接调用以下几个系统调用来执行命令:

  • fork():创建子进程

  • execve():在子进程中执行命令

  • waitpid():等待子进程结束


main 函数声明(C 标准)

1️ 无参数形式

int main(void);
int main();
  • 两者等价,表示 main 不接受命令行参数。

  • 推荐使用 int main(void)(尤其从 C99 开始),避免歧义。

有参数形式

int main(int argc, char *argv[]);
  • argc:参数数量(包含程序名本身)

  • argv:字符串数组,保存所有命令行参数

    • argv[0]:程序名称

    • argv[1] ~ argv[argc-1]:用户输入的命令行参数


🔧 2、 fork 函数详解

相关头文件

#include <sys/types.h>
#include <unistd.h>

类型说明:pid_t

  • pid_tint 的别名

  • 定义过程:

    typedef int __pid_t;
    typedef __pid_t pid_t;
    

fork()

pid_t fork(void);
  • 功能:复制当前进程,创建一个子进程

  • 返回值说明

    • 父进程中:返回子进程的 PID

    • 子进程中:返回 0

    • 创建失败:返回 -1


getpid()

pid_t getpid(void);
  • 功能:返回当前进程的 PID

  • 不会失败,一定有返回值


getppid()

pid_t getppid(void);
  • 功能:返回当前进程的父进程 PID

  • 不会失败


小结

  • fork() 是进程创建的基础,父子进程共享代码段但拥有独立的数据段。

  • getpid()getppid() 是用于确认进程身份的工具函数。

  • 每次 fork() 之后,父子进程会并发执行接下来的代码,可以根据 fork() 的返回值判断当前是哪个进程。

二、 文件描述符的引用计数和 close()


基础理解

在 Linux 中,文件描述符(FD) 本质上是一个引用,指向内核中的 struct file 数据结构。当一个进程执行 fork()子进程会复制父进程的所有文件描述符,也就是复制引用,但不是复制文件本身。

每个 struct file 结构体中有一个字段 f_count,表示有多少文件描述符在使用它(也就是引用计数)。


sleep 函数介绍


#include <unistd.h>  unsigned int sleep(unsigned int seconds);
  • 使进程休眠指定秒数

  • 若过程中收到信号,可能提前醒来

  • 返回值

    • 正常结束:返回 0

    • 被打断:返回剩余秒数


原理解析

为什么子进程 close(fd) 后父进程还能 write()

  • fork() 后父子进程共享相同的 struct file 结构体

  • fd 是文件描述符,它在进程内部是一个整型值

  • 每个 fd 对应一个 struct file(底层实际对象)

struct file 中的 f_count 是引用计数:

  • 初始打开:引用计数 = 1

  • fork() 后:引用计数 = 2(因为父子进程都有一个 fd

  • 子进程 close(fd):引用计数减为 1,struct file 还在

  • 父进程继续使用 write() 没问题

只有当最后一个引用释放,引用计数为 0struct file 才会被销毁。


小结

操作行为
open()返回 fd,并创建 struct file,引用计数 = 1
fork()子进程复制 fd,引用计数 = 2
close(fd)减少引用计数,不一定销毁 struct file
write(fd)只要引用计数 > 0,文件可继续使用

🔐 避免重复关闭同一个 fd:每个进程只需关闭自己持有的文件描述符,不要重复关闭。

三、 execve 与进程替换

1)execve 单独使用测试

背景
  • exec 系列函数用于在当前进程中执行另一个程序,替换原进程的正文段和数据段。

  • 不会创建新进程,调用成功后,原进程空间会被新程序完全替换,PID 不变。

  • 常用函数:execve() 是底层实现函数,最通用。

原理说明
int execve(const char *pathname, char *const argv[], char *const envp[]);
  • pathname:程序的完整路径

  • argv[]:参数数组,第一个元素是程序名,最后一个必须是 NULL

  • envp[]:环境变量数组,可为 NULL

实验流程
  1. 编写 erlou.c,作为目标执行程序(输出传参内容)

  2. 编写 execve_test.c,调用 execve 跳转执行 erlou

关键逻辑
char *args[] = {"/完整路径/erlou", "banzhang", NULL};
char *envs[] = {"PATH=...省略...", NULL};
execve(args[0], args, envs);
  • 若参数不足,目标程序内部会打印“参数不够,上不了二楼”

  • 一旦 execve() 成功,后续代码不再执行(控制权已转移)


2)fork + execve 联合使用

场景模拟
  • 老学员在一楼学习(父进程)

  • 通过 fork() 创建新学员进程

  • 子进程使用 execve() 跳转至二楼(执行另一个程序)

  • 父进程保持原样继续学习

关键逻辑
pid_t pid = fork();
if (pid == 0) {char *args[] = {"/完整路径/erlou", "ergou", NULL};execve(args[0], args, envs);
}
  • fork() 创建子进程

  • 子进程替换自己,进入新程序

  • 父进程输出提示,不受影响


四、 waitpid:父进程回收子进程

为什么需要 wait/waitpid?

  • 若父进程不回收子进程,子进程会变成僵尸进程

  • 僵尸进程虽已结束,但其资源(如 PCB)未被释放

  • 会被系统进程(init 或 systemd)回收,但应主动处理

waitpid 用法

pid_t waitpid(pid_t pid, int *status, int options);
  • pid:要等待的子进程

    • -1:等待任意子进程(最常用)
  • status:用于存储子进程的退出状态

  • options

    • 0:阻塞等待

    • WNOHANG:非阻塞

    • WUNTRACED:也返回停止的子进程

    • WCONTINUED:也返回继续执行的子进程

实验流程

  • 创建子进程后,子进程执行 ping

  • 父进程使用 waitpid() 等待子进程完成后回收

关键逻辑
pid_t pid = fork();
if (pid == 0) {char *args[] = {"/usr/bin/ping", "-c", "50", "www.atguigu.com", NULL};execve(args[0], args, NULL);
} else {waitpid(pid, &status, 0); // 父进程挂起等待子进程退出
}

总结

技术点关键特性
execve当前进程被新程序完全替换,原代码不再执行
fork + execve创建子进程,再让子进程切换到新程序,实现分离执行流程
waitpid用于等待和回收子进程,防止出现僵尸进程

以下是根据你提供的内容整理的 Obsidian 学习笔记格式,去掉了多余代码,只保留关键部分,突出概念理解、运行要点及注意事项:


Linux进程相关概念与实操笔记(二)

六、 进程树(Process Tree)

概念理解
  • Linux进程通过父子关系组织,形成“进程树”。

  • 每个进程只有一个父进程,但可以有多个子进程。

  • 所有进程最终追溯到:

    • 用户空间:PID=1 的进程,通常为 systemd

    • 内核空间:PID=2 的第一个内核线程

  • 这两个最早的进程,父进程ID为 0,即由内核创建。

示例说明
  • 使用 fork() 创建子进程。

  • 子进程中调用 execve() 执行另一个程序。

  • 父进程阻塞等待,防止程序提前退出,便于测试。

char bye = fgetc(stdin); // 阻塞等待输入,模拟挂起
进程树观察方法
  • ps -ef:查看所有进程

  • pstree:树状展示进程结构

  • pstree -p:附带 PID 的树状结构

  • 父进程终止后,子进程会被 systemd 自动领养(变为孤儿进程)


七、 孤儿进程(Orphan Process)

定义
  • 父进程先于子进程结束,子进程尚未结束时,该子进程称为“孤儿进程”。

  • 孤儿进程将被 init(即 systemd)进程自动收养。

实验说明
  1. 子进程执行 sleep(100) 保持活跃

  2. 父进程不等待子进程结束,直接返回

  3. 使用 ps -ef | grep [pid] 查看子进程状态

  4. 使用 pstree 查看其被 systemd 收养

sleep(100); // 模拟子进程长时间运行
编程注意
  • 避免父进程提前退出导致孤儿进程

  • 可使用 wait()waitpid() 正确回收子进程


总结

概念说明
进程树所有用户进程最终来源于 PID=1 的 systemd
内核线程显示为 [],起始 PID=2,非用户空间线程
孤儿进程父进程已退出、子进程仍存活;最终会被 systemd 领养
观察工具ps -ef, pstree, pstree -p
编程实践建议合理使用 wait/waitpid,避免产生孤儿进程
http://www.dtcms.com/a/330141.html

相关文章:

  • 【完整源码+数据集+部署教程】医学报告图像分割系统源码和数据集:改进yolo11-HGNetV2
  • @Linux进程管理工具 - PM2全面指南
  • 理财 - 基金
  • 【React】use-immer vs 原生 Hook:谁更胜一筹?
  • PromptPilot — AI 自动化任务的下一个环节
  • 云蝠智能 Voice Agent 多模型接入技术架构与实践
  • 微信小程序实现导航至目的地
  • 腾讯位置商业授权微信小程序关键词输入提示
  • python自学笔记7 可视化初步
  • 并发编程(八股)
  • epoll模型解析
  • 数据科学与计算:从基础到实践的全面探索
  • 深度学习(6):参数初始化
  • 动画相关 属性动画+animateToImmediately+ImageAnimator帧动画组件+模态转场
  • 【C++】哈希表的实现
  • EUDR的核心内容,EUDR认证的好处,EUDR意义
  • web开发,在线%射击比赛管理%系统开发demo,基于html,css,jquery,python,django,三层mysql数据库
  • lesson37:MySQL核心技术详解:约束、外键、权限管理与三大范式实践指南
  • SpringBoot工程妙用:不启动容器也能享受Fat Jar的便利
  • CAD 的 C# 开发中,对多段线(封闭多边形)内部的点进行 “一笔连线且不交叉、不出界
  • ECC的原理、背景、工作机制和数学基础
  • 升级Gradle版本后,安卓点击事件使用了SwitchCase的情况下,报错无法使用的解决方案
  • Query通过自注意力机制更新(如Transformer解码器的自回归生成)的理解
  • Unity3D 中纯 Shader 的双色纹理的平铺计算与实现
  • 二次筛法Quadratic Sieve因子分解法----C语言实现
  • [git diff] 对比检查变更 | 提交前复审 | 版本回退
  • SQL 核心操作全解析:从基础查询到关联关系实战
  • Spring Boot项目通过Feign调用三方接口的详细教程
  • 在es中安装kibana
  • 雨量系列篇一:翻斗雨量传感器与压电雨量传感器的区别是什么