进程创建
fork
我们前面已经认识过fork
函数, 用fork
创建新进程后, 新建立的进程为子进程, 该进程为父进程。fork
给父进程返回的是子进程的pid
, 给子进程返回的是0
, 出错时返回-1
进程调用fork
后, 当控制转移到内核中的fork代码后, 内核做:
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程
- 添加子进程到系统进程列表当中
- fork返回, 开始调度器调度
用fork创建子进程后如何发生写实拷贝
的?
当我们在创建出子进程后, 父进程的页表
中原本对应的数据区, 将会由可读可写变化为只可读
(当然子进程的权限也是只可读)所以当进程需要对数据进行修改时, 就会触发操作系统的权限问题, 但是在审核的时候, 系统会发现你这个区域本来是可写的, 只是暂时变为了只可读, 所以针对这种情况, 操作系统不会当做异常处理, 而是变成操作系统把数据给你拷贝一份, 谁写的就把谁的页表映射改到新开辟的空间, 并且把权限改回可读可写
然后我们就可以正常访问了
为什么要用写实拷贝的方式维护父子进程的数据区
因为我们的子进程可能只会修改父进程的一部分数据,一味的拷贝会造成内存资源的浪费
也会导致效率的降低
(要多拷贝一些内容)
fork的常规用法
1一个父进程希望复制自己, 使父子进程同时执行不同的代码, 例如父1进程等待客户端请求, 生成子进程来处理请求
2一个进程要处理一个不同的程序, 例如子进程从fork返回后, 调用exec函数
用fork创建多个子进程
子进程执行完run后就直接结束掉, 而父进程继续循环创建子进程
这多个进程谁先被执行?
这完全取决于调度器先调度谁, 父子进程以及兄弟进程谁先执行我们不可知
进程终止
为什么main
函数要返回值, 这个值返回给谁了?为什么要返回这个值?
我们return
的其实就是进程的退出码, 表征进程的退出状态。
0 --> success
这个退出码会被该进程的父进程拿到,(在这里就是被bash拿到)
这个退出码会被保存在?
这个变量里
我们可以使用echo $?
查看上一个进程的退出码
在进程中, 谁会关心我的进程结束状态呢?
一般而言, 是我们进程的父进程会关心
main函数的返回值本质表示:
进程完成时是否是正确的结果,如果不是可以用不同的数字,表示不同的出错原因!
strerror查看错误码信息
c语言自带的库函数strerror
能将对应的错误码转化为错误信息
我们验证一下
我们先是用ls
指令查看一个不存在的文件, 然后报错没有该文件
, 我们查表发现该错误对应的错误码为2
查看上一个进程的退出码,发现确实是2
errno是c语言提供的一个全局变量, 我们调用c语言内部的库函数, 如果调用失败了, c语言会将这个默认的全局变量errno
设置成对应的数字表明为什么调用出错
上诉出现的错误代码都是跑完了的, 但是进程的退出还有一种情况, 那就是异常退出
对于异常退出的进程, 他的退出码还有意义吗?
因为我们不清楚异常退出的进程他是否进行了正常的return
, 所以一般是不用他的退出码。
所以我们在关心进程退出的时候, 应该先关心进程是否异常, 再关心进程的退出码
我们知道了异常退出的进程, 他的退出码没有意义, 但是, 我们该如何知道进程是否是异常退出的呢?
进程异常
进程出现异常, 本质上是我们的进程收到的了对应的信号!!
信号
可以看到我们的进程在正常的跑, 并没有任何的错误
但是当我们在给他发送8号信号的时候, 他就发生段错误了。
所以我们父进程想要判断子进程是否是异常退出, 只需要看是否收到了对应的信号, 如果没收到, 那么自然子进程就是正常退出的, 我们此时就可以去查看子进程的退出码了
exit 与 _exit
他们两个都可以直接终止进程,并且返回对应的退出码
他们的区别是
如果我们是有exit()退出进程时, 这段信息是先等一会, 然后再被打印出来
(这个原因我们讲过, 是因为信息是先被保存到了缓存区, 等刷新后才会显示出来)
而如果我们使用_exit()退出进程, 会发现直接没有打印消息了
所以_exit()在退出进程的时候, 是不会将缓存区里的信息刷新出来的。
_exit()是系统的调用接口, 我们用户在调用这个接口后, 就会直接将当前进程终止。
而如果我们调用的是exit()函数, 他会先把我们之前打开的各种文件啊, 流啊什么的都刷新, 然后再去调用_exit()。