Linux下进程的相关操作

本文深入讲解进程创建、终止、等待及替换的关键技术,包括vfork、fork、wait、waitpid和exec函数族的使用,以及如何避免僵尸进程,是理解操作系统进程管理的必备指南。

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

进程创建

创建进程的函数接口:

pid_t vfork();			// 创建一个子进程,并且阻塞父进程,避免调用栈混乱(vfork创建出的子进程与父进程共用虚拟地址空间)
pid_t fork(void);		// 通过复制父进程创建子进程;父子进程数据独有,代码共享
  • 代码共享:主要因为代码是只读的,不能改变的
  • 返回值:对父进程来说 返回值是子进程的pid;对子进程来说,返回值是0(可以通过返回值对父子进程进行代码分流,让其各自执行不同的代码段)

写时拷贝技术:子进程创建出来后,各个虚拟地址在物理内存中的指向与父进程完全相同,这是为了提高子进程的创建效率(否则要为子进程开辟物理内存,拷贝数据、更新页表,整个过程比较慢,并且有可能子进程根本不会用这些数据),若某一块物理内存中的数据即将发生改变(某个进程要对这块内存进行写操作),则给子进程相应的数据开辟物理内存,将数据拷贝过来

进程终止(退出进程)

进程退出的场景:正常退出且结果符合预期/正常退出且结果不符合预期/异常退出

进程正常退出的方式

  • 在main函数中return以退出进程:return退出进程时会刷新缓冲区
  • 调用系统调用接口void _exit(int status);退出调用进程:任意位置都能调用,不限于main函数;status为退出返回值,退出进程时不会刷新缓冲区,直接释放资源
  • 调用库函数void exit(int status);退出调用进程:任意位置都能调用,不限于main函数;status为退出返回值,退出进程时会刷新缓冲区

异常退出:程序没有运行完毕,而是在运行中因为某些异常直接退出了,这时进程的返回值是不能作为评判结果的,因此在获取返回值之前,应该先判断进程是否是正常退出的

进程等待

概念:等待子进程退出,获取退出子进程返回值,释放子进程资源,避免产生僵尸进程

  • 僵尸进程:处于僵死状态的进程。退出了但是资源没有完全释放的进程;子进程先于父进程退出,父进程没有关注子进程的退出状态
  • 如何避免僵尸进程的产生:使用进程等待

进程等待接口

pid_t wait(int *status);	// 阻塞等待任意一个子进程的退出,通过status获取子进程的退出返回值,返回退出子进程的pid

pid_t waitpid(pid_t pid,int *status,int options);		// 等待指定进程的退出,也可等待任意子进程退出
// 参数:
// pid:如果值为-1表示等待任意一个子进程退出,值>0表示等待指定子进程退出
// status:用于获取子进程的退出返回值
// options:值为0表示阻塞等待子进程退出;值为WNOHANG时,表示将waitpid设置为非阻塞
waitpid(-1,status,0); /*等价于*/ wait(&status);
// 阻塞:为了完成一个功能发起一个调用,若当前不具备完成功能的条件,则一直等待
// 非阻塞:为了完成一个功能,发起调用,若当前不具备完成功能的条件,则立即报错返回
// 非阻塞可以在不满足条件期间做其它事情,对资源的利用率更高,但是通常需要循环判断操作
  • wait/waitpid不仅仅是处理刚退出子进程(只处理一个),只要在调用wait的时候,有子进程退出,wait都回去获取返回值,释放资源
  • waitpid的返回值:-1表示出错;0表示没有子进程退出;>0表示有子进程退出,退出子进程的pid就是返回值

返回值的获取

  • 子进程的返回值实际上只用了一个字节进行保存(进程的返回值最好不要大于256,否则就会进位),通过wait/waitpid函数中的status获取返回值时,status有4个字节(32位),其中高16位未使用;低16位中的高8位存放返回值,低7位中存放进程异常退出的信号值,中间一位是core dump,返回值放在低16位中的高8位。返回值的获取:(status>>8)&0xff

    • core dump:核心转储。程序异常退出时,保存程序的运行信息到一个core文件中,使用这个core文件,便于时候调试;默认关闭
  • 如何判断进程是否正常退出:status低7位中存放的是进程异常退出的信号值,因此若status中低7位为0则表示正常退出,否则表示异常退出(status&0x7f)

    • WIFEXITED(status)判断进程是否正常退出(调用exit/_exit/main函数中的return)
    • WEXITSTATUS(status)获取子进程退出返回值

进程替换

替换一个进程正在运行的程序,让当前进程pcb调度运行管理另一个新的程序

  • 将一个新的程序加载到内存中,修改当前进程pcb对应的页表信息、初始化虚拟地址空间,让这个页表映射到新的程序上,这样就实现了不改变pcb(id都不改变),但是运行的程序为新的程序

如何替换?exec函数族:

int execl(const char* path,const char* arg, ...);	/*例*/execl("ls","ls","-l","-a",NULL)
int execlp(const char* file,const char* arg, ...);
int execle(const char* path,const char* arg, ...,char* const envp[]);
int execv(const char* path,char* const argv[]);	/*例*/argv[]={"ls","ls","-l","-a",NULL};execv("ls",argv);
int execvp(const char* file,char* const argv[]); 
int execve(const char* path,char* const argv[], char* const envp[]);

程序替换首先要告诉shell新的程序在哪里、叫什么名字、程序的运行参数是什么、环境变量有什么

  • l和v的区别:程序运行参数赋予方式不同。l逐个通过函数不定参数赋予,v通过字符串指针数组赋予
  • 有没有p的区别:新的程序文件是否需要指定路径在哪里。带有p就可以不需要指定路径,shell会默认去PATH环境变量指定的路径下查找
  • 有没有e的区别:运行新程序时是否重新初始化环境变量。若没有e,则默认使用父进程已有的环境变量,若有e,则表示自定义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值