进程控制

本文围绕进程展开,介绍了进程创建的fork和vfork函数,包括其原型、执行流、返回值等;阐述了进程终止的场景和方法;说明了进程等待的原因及wait、waitpid方法;还讲解了进程程序替换的原理、替换函数,最后提及函数与进程的相似性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程创建

fork函数原型

#include <unistd.h>
pid_t fork(void);//创建子进程

内核执行流

  • 分配新的内存块和内核数据结构给子进程(创建task_struct)
  • 将父进程部分数据结构内容拷贝至子进程(拷贝父进程的task_struct内容)
  • 添加子进程到系统当中(分配新的内核堆栈、新的pid,再将子进程PCB添加到链表)
  • fork返回,开始调度器调度

进程执行流

fork之前父进程独立执行,fork之后父子进程分别执行,谁先执行有调度器决定

fork返回值

  • fork有两个返回值,父进程返回子进程的pid,子进程返回0
  • 一个父进程有多个子进程,一个子进程有一个父进程,fork之后父进程需要知道那个子进程完成任务,所以父进程返回子进程的pid
  • 当fork失败返回-1,PCB是要占内存的,当内存不够或者实际用户进程超过了限制fork会调用失败

fork常规用法

  • 父进程希望复制自己,使父子进程同时执行不同的代码段
  • 一个进程要执行一个不同的程序

vfork和fork

  • vfork也可以创建子进程,vfork创建的父子进程共享地址空间,fork创建的父子进程具有独立的地址空间
  • vfork保证子进程先运行,调用exec或exit之后父进程才能被调度运行,fork父子进程分别执行,执行顺序由调度器决定

进程终止

进程退出场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 异常终止

进程退出方法

查看进程退出码

echo $?

正常终止

  • main函数返回
  • _exit
#include <unistd.h>
void _exit(int status);
//status定义进程终止状态,父进程通过wait获取
//status仅有低8位可被父进程所用
  • exit:先执行用户通过atexit或on_exit定义的清理函数,然后关闭所有打开的流,所有缓存数据均被写入,最后调用_exit
#include <unistd.h>
void exit(int status);

exit与_exit的执行流

  • return:return n同等于exit(n),调用return会将返回当做exit的参数

异常终止

  • ctrl+c:信号终止
  • kill -9 pid:向进程ID为pid的进程发出9号信号

进程等待

为何存在进程等待

  • 子进程退出,父进程不管不顾会造成僵尸进程,僵尸进程会造成内存泄漏,就算是”kill -9“也无法终止进程,因为无法杀死死去的进程
  • 所以父进程需要知道子进程的任务完成如何,父进程可以通过进程等待的方式获取子进程资源和退出信息

进程等待方法

wait

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int* status);
//成功返回被等待进程的pid,失败返回-1

waitpid

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int* status, int options);
//当pid>0时,只等待进程ID为pid的子进程,当pid=0时,等待同一个进程组中的任何子进程
//当pid=-1时,等待任何子进程,当pid<-1时,等待指定进程组的中的任何子进程
//options:WNOHANG非阻塞式,WUNTRACED阻塞式,WUNTRACED涉及到跟踪方面的知识,并且很少用
//当正常返回时waitpid返回收集到的子进程的pid
//当设置了WNOHANG,而调用中waitpid发现没有退出的子进程可收集,返回0
//当调用出错时,返回-1,此时errno设置成相应的值以指示错误所在

wait和waitpid的参数status

  • status是一个输出性型参数,由操作系统填充
  • 如果传递NULL,表示不关心子进程的退出状态信息
  • WITFEXITED(status):查看子进程是否正常退出,若为正常终止子进程返回的状态,则为非零,否则为0
  • WEXITSTATUS(status):查看进程退出码,若WIFEXITED非零,提取子进程退出码,否则这个值毫无意义
  • 上述的WITFEXITED和WEXITSTATUS是宏,用来读取子进程退出状态
  • status不能简单当做整形,可以当位图看待,只研究低16位,当正常终止时,status的8-15位代表退出码,0-6位为0,当异常退出时,0-6为为退出码

注意

  • 如果子进程已退出,调用wait/waitpid时,wait/waitpid会立即返回,并释放资源,获取子进程退出信息
  • 如果子进程存在且正常运行,调用wait/waitpid时,进程可能会阻塞
  • 如果不存在子进程,则立即出错返回

进程程序替换

替换原理

  • 用fork创建子进程后执行的是和父进程相同的程序,也可执行不同的代码分支,一般子进程和父进程做不同的事,否则这个子进程无意义,所以子进程调用exec函数替换子进程的程序和数据
  • 当进程调用exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动列程开始执行
  • 调用exec函数时并不创建新的进程,所以进程的id未改变

替换函数

int execl(const char *path, const char* arg, ...);
int execlp(const char *file, const char* arg, ...);
int execle(const char *path, const char* arg, ..., char* const envp[]);
int execv(const char *path, const char arg[], ...);
int execvp(const char *file, const char arg[], ...);
int execle(const char *path, const char arg[], ..., char* const encp[]);

助记

  • exec+l(list):参数采用列表
  • exec+v(vector):参数采用数组
  • execl/execv+p(path):自动搜索的环境变量
  • execl/execv+e(env):自己维护的环境变量

函数解析

  • 调用成功加载新的程序从启动代码段开始执行,无返回值,调用失败返回-1
  • execve为系统调用其他五个函数都是在execve上包装的

exec函数关系

函数与进程的相似性

  • exec/exit类似于call/return
  • 一个C程序有许多函数,一个函数可以调用另一个函数,同时传递一些参数,每个函数都有它的局部变量,不同函数通过call/return系统进行通信
  • 一个C程序可以fork/exec另一个程序,同时传递一些参数,调用它的进程可以通过wait获取exit的返回值
  • 通过参数和返回值在拥有私有数据的函数间通信时结构化程序设计基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值