Linux进程创建


前言

进程控制:进程创建/进程终止/进程等待/程序替换

一、进程创建

1.pid_t fork(void);

–创建一个进程-----父子进程数据独有,代码共享
在这里插入图片描述

写时拷贝技术:
子进程复制了父进程,一开始与父进程指向同一块物理内存;因此看起来父子进程完全相同;但是进程之间应该具有独立性;意味着当这块物理内存中数据即将发生改变时会重新给子进程开辟物理空间,将数据拷贝过来,因为子进程应该有自己的数据
代码共享:因为代码段是只读的
进程创建这采用写时拷贝技术,目的是为了提高进程的创建效率
按理说每个进程都应该有自己独立的内存空间,但是若是创建子进程时直接开辟空间拷贝数据,将会比较缓慢
子进程创建pcb的时候,就会创建页表,虚拟地址空间…然后从父进程pcb中去拷贝这些数据
fork创建的子进程,父子进程时可以同时运行的

2.pid_t vfork(void)

创建一个子进程,并且阻塞父进程,直到子进程exit退出或者程序替换之后,父进程才会运行。vfork创建子进程效率比较高,因为vfork创建子进程之后父子进程共用同一个虚拟地址空间
在这里插入图片描述
在这里插入图片描述

共用同一个虚拟地址空间,意味着共用代码段,数据段,这样如果父子进程同时运行会造成栈 混乱,因此必须子进程先运行,父进程阻塞,直到子进程退出,所有函数出栈
vfork创建的子进程是不能使用main中return退出,因为main中return退出,会释放进程资源

早期使用vfork是因为vfork创建子进程效率高,但是fork实现了写时拷贝之后,创建效率变高了,并且使用灵活了,现在很少使用vfork

为什么要创建一个子进程呢?
创建子进程大多数情况下并不是为了让子进程干跟自己一样的活,而是让子进程去调度另一个程序运行

给大家举个生动例子:
小明和小黑去捅马蜂窝,到马蜂窝树下,小明说小黑你去捅,我给你望风~~
这就是说,有一个任务,父进程可以干,但是怕有风险,万一数据处理出错崩溃了,因此创建一个子进程,让子进程去干。
进程创建的流程:在内核中调用clone接口创建pcb,从父进程-pcb中拷贝数据过来,-写时拷贝技术

二、进程终止

退出一个进程:

1. main函数中return

退出进程时会刷新缓冲区

2. void exit(int status);

—库函数,退出调用进程,将status作为返回值返回给父进程 退出时也会刷新缓冲区

3.void _exit(int status);

—系统调用接口 退出调用进程,将status返回给父进程 直接释放资源,并没有刷新缓冲区
库函数和系统调用接口有什么关系??库函数封装了系统调用接口
exit和return又有什么区别? return之后在main函数中的时候才会退出程序,然而进程exit是在任意位置调用都会退出调用进程。
在这里插入图片描述

以上几种进程退出方式,都属于进程的正常退出—但是会根据返回值向父进程表示为什么退出了
但是进程退出也有可能有异常退出–程序崩溃–程序没有运行完毕突然因为某种错误退出了

三、进程等待

1.pid_t wait(int *status);

—阻塞等待任意一个子进程退出,获取子进程的返回值放到status指向的空间中;并且释放子进程资源;返回退出的子进程pid
阻塞:为了完成某个功能发起一个调用,若当前不具备完成功能的条件;则调用会一直等待
非阻塞:为了完成某个功能发起一个调用,若当前不具备完成功能的条件,则立即报错返回
在这里插入图片描述
在这里插入图片描述

2. pid_t waitpid(pid_t pid,int *status,int options);

pid:若等于-1,则表示等待任意一个子进程退出,若是>0,则表示等待指定的子进程退出
status:输出型参数,传入一个int空间的首地址,获取退出的子进程返回值
options:选项参数,0–则表示默认阻塞等待;WNOHANG–将waitpid设置为非阻塞–没有子进程已经退出的话就立即报错返回
返回值:若等待到了子进程退出则返回子进程的pid;若有子进程,但是没有退出则返回0;出错返回-1;
wait(status)==waitpid(-1,status,0)

当用WNOHANG的时候,配合循环,否则可能会产生僵尸进程
举例:我去幼儿园接孩子回家,到了幼儿园孩子还没有放学,我就去打麻将,这时正好儿子放学了,导致儿子没人接。
非阻塞相较于阻塞操作,对cpu利用更加充分,因为不再一直等待了,但是必须循环进行操作

在这里插入图片描述

在这里插入图片描述

wait和waitpid并不是只处理刚退出的进程,而是对已经退出的进程进行处理(不管什么时候退出)
这两个函数,并不是光处理刚刚退出的进程,而是只要是退出的子进程,不管什么时候退出的都会获取返回值释放资源
在这里插入图片描述
(status>>8)&0xff获取返回值
我们现在知道了如何获取一个退出的子进程的返回值,但是,前边进程终止的时候,我们说过,一个进程的退出,并不一定是正常退出,若子进程的退出是异常退出,获取这个返回值将毫无意义,因此获取一个进程返回值的前提是这个进程是正常退出的!!
如何判断子进程是否是正常退出??
在status这个变量中,低8位中保存了一些特殊的数据

低8位中高一位—core dump标志
低7位–程序异常退出信号值
core dump:核心转储–程序异常退出时,则保存程序的运行信息,便于事后调试
信号:通知进程发生了某个事件,中断进程当前的操作,去处理这个事件,而操作系统中的信号的体现实际是一个数字,某个数字表示某个异常事件,程序因为异常退出的时候,则会将这个异常退出的信号值放到低7位
结论:若status低7位为0则表示程序正常退出;否则表示程序异常退出
在这里插入图片描述
操作系统也为我们程序员考虑:(status>>8)&0xff的不方便,为我们提供下面的两个接口来调用
WIFEXITED(status)–用于根据status判断子进程是否正常退出
WEXITSTATUS(status)–从status中取出子进程退出返回值

四、程序替换

1.创建一个子进程目的是什么?

1.干着和父进程相同的事情,分摊压力
2.让子进程干其他事,让子进程背锅

2.如何让子进程干其他的事情?

1.通过fork的返回值就行分流后,子进程的if(fork()==0)(…子进程干的事情… 但是这种方式存在缺陷:程序的耦合度非常强,因为所有功能代码都是在当前程序中编写的—如果想要改变子进程的功能处理流程,需要修改整个程序代码,然后重新进行编译这样一来,我们的程序就会变得非常大。

程序替换:替换一个进程正在调度的程序信息(进程是pcb,负责调度管理一个程序的运行,运行数据和代码都在内存中) 将另一个程序加载到内存中,然后让原有的pcb不在调度原程序,而去调度这个新程序
程序替换:本质来说就是替换一个pcb在内存中对应的代码和数据(加载另一个程序到内存中,然后更新页表信息,初始化虚拟空间)
这个进程pcb将从头重新开始调度新的程序运行

在这里插入图片描述

如何在代码中完成程序替换,让一个进程调度运行另一个进程:
exec函数族–实现进程的程序替换
int execl(const charpath,const chararg,…);
带有路径的新程序名称,就是使用这个程序替换进程正在调度运行的程序
将新程序的运行参数,通过不定参的形式传递进入新的程序,以NULL作为结尾
在这里插入图片描述
在这里插入图片描述

int execlp(const charfile,const chararg,…);
int execle(const char* path,const char *arg,…,char *const envp[]);

int execv(const char* path,charconst argv[]);
举例:
char
new_argv[]={“mytest”,"-l","-p",NULL);
execv("./mytest",new_argv);
在这里插入图片描述

int execvp(const charfile,charconst argv[]);
int execve(const char* path,char* const argv[],char *const envp[]);
在这里插入图片描述
在这里插入图片描述

l和v的区别:在于程序运行参数的赋予方式不同,l通过不定参完成/v通过字符串指针数组进行赋予
有没有p的区别:在于第一个参数执行新程序的时候,是否需要带路径,有p则可以不带路径,默认会去PATH环境变量指定的路径下查找
有没有e的区别:在于这个进程的环境变量是否需要重新初始化;有e则表示初始化,若没有e则表示不需要重新初始化
程序替换之后,当前进程运行完替换后的程序就会退出,并不会又回去运行原先的程序

注意:前五个都是库函数,最后一个execev是系统调用接口
这些函数成功的时候,没有返回值

通过这个execve要明白,一个进程的环境变量实际上是他的父进程赋予的

我们在终端运行一个程序,这个程序的父进程实际上是shell程序
这时候我们理解为什么我们输入一个./main就能运行main程序??

当我们在shell中输入了一个命令,这时候其实shell对这个标准输入进行解析,得到了程序名称,然后创建进程,并且将就子进程的程序替换为当前这个解析出来的程序
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值