【Linux】进程控制
重谈进程创建
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程 。
进程调用fork,当控制转移到内核中的fork代码后,内核做:
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程
- 添加子进程(pcb)到系统进程列表当中
- fork返回,开始调度器调度
执行到 fork 语句时,进入操作系统内核空间执行fork:
创建进程:
写时拷贝
os如何知道父子进程共享数据块(父子进程对应数据,代码和数据中的数据)需要写时拷贝?
父进程创建子进程后,父进程可读的部分权限不变,默认把父进程对应读写的权限都改为只读;子进程也是只读。
此刻父子任意一个进行写入,进行权限审核时,如果OS识别历史上这块区域可以被使用只是被暂时设置为只读状态,此刻触发缺页中断,写时拷贝,重新开辟空间,拷贝数据, 把父子进程对应页表中的只读标签去掉,此刻就可以进行访问了。 (原先权限就是读的不会有这种情况)。
写时拷贝是父进程创建子进程时,让父进程对数据区的页表的权限改成只读权限,看后续谁先触发写入的错误,来判断谁要发生写时拷贝。
该理论只适用于数据,不适用于代码。
fork常规用法
-
一个父进程希望复制自己,使父子进程同时执行不同的代码段(if else)。例如,父进程等待客户端请求,生成子进程来处理请求。
-
一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。
fork调用失败的原因
- 系统中有太多的进程
- 实际用户的进程数超过了限制
这种情况下,系统可能会杀进程
创建多个进程
exit 退出进程
脚本:
while :; do ps ajx | head -1 && ps axj | grep myproc | grep -v grep; echo "---------------------"; sleep 1; done
父子进程谁先运行由调度器决定(我们不可知),取决于进程创建成功之后,放入运行队列的位置,谁先被放,谁先调度:
进程终止
进程退出情况
- 代码运行完毕,结果正确(本文重要)–> 不关心为什么正确
- 代码运行完毕,结果不正确(本文重要)–> 关心为什么不正确?
- 代码异常终止( 进程等待讲一部分,信号将一部分)
return 0;
进程的退出码,表征进程的运行结果是否正确 0–>success,0 会被父进程(bash)拿到;非 0 表示错的。
进程中,谁会关心我运行的情况?
一般而言,是该进程的父进程关心
当代码运行完毕后,结果不正确,可以通过 return 的不同的返回值数字,表征不同的出错原因 —— 退出码
结论1:main函数的返回值,本质表示:进程运行完成时,是否正确的结果,如果不是可以用不同的数字表示不同的出错原因。
echo $? 第一次接收是进程返回的退出码,后来是 0 ,为什么?
注:$? 表示命令行中最近一个程序运行时的退出码
当 echo 过一次后,之后最近一次程序就是 echo 命令,echo 命令执行正确,所以退出码为 0
在不进行打印的代码中,比如向网络中写入时,进程运行完之后,并没有返回任何信息,不能知道结果,此时只能用退出码。
退出码是计算机看的,错误信息是人看的。
strerror 会带回错误码对应的错误信息:
例如 ls 有问题,打印错误码:
系统提供的错误码和错误信息,描述是互相关心的。
可以自己弄一套。
父进程为什么要关心子进程的运行情况?
因为用户需要知道出错原因,需要父进程把退出信息转交给用户,让用户决策下一阶段的执行任务。
回顾王婆的例子:
从这里就可以知道,王婆是父进程,村长是用户,实习生是子进程,子进程回来会带回来执行的结果,如果错误,会有错误的原因。村长需要知道叫王婆办事办的怎么样,王婆会把实习生工作的结果给村长汇报一下。一般是要让实习生把结果反馈给王婆或者村长的,除非有特殊情况王婆不关心,此刻退出码随便写。
总结:
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
正确与否会通过进程的退出码来标识,即 main 函数的返回值。
errno c 提供的全局变量,保存的是最近一次的错误码
异常时,退出码还有意义?
代码异常,本质可能就是代码没跑完;可能根本没有执行到 return 语句,所以退出码没有意义;但是如果刚把 return 执行完呢?main函数是被调用的,return 在语言层面上程序结束了,但是并不一定进程退出了。说到底,我怎么知道 return 有没有被执行?再者说,return 执行异常的情况,退出码我们敢用吗? 我们并不能确定在哪里异常,随时随地可能发生,异常时所以退出码没有意义。此刻不关心退出码。
所以先关心进程退出是否异常,再看结果是否正确(再关心退出码)
进程出异常,本质是我们的进程收到了对应的信号,异常会触发某种硬件错误(野指针,/0),这些硬件问题都会被 OS 识别,向进程发信号完成
浮点数报错:SIGFPE
程序异常后,如何证明 OS 将异常转换成信号,发送给进程,让进程退出的?
当写一个循环的代码:
如果向它发送信号 8 即浮点数报错:
证明结论:进程出异常,本质是我们的进程收到了对应的信号
所以如果父进程要关心子进程是否退出,只需要确定父进程退出时是否收到过信号,然后再查对应的结果,如果错误,得到错误原因。
进程常见退出方式
正常终止:
- main 函数返回
- exit
- _exit
exit:引起一个进程终止
exit 的参数是当前进程对应的退出码
return 在 main 函数表示退出,在其他函数表示函数返回;exit 在任何函数中都是退出
_exit 为系统调用
_exit 和 exit 有什么区别?
int main()
{
printf("you can see me!\n");
exit(11); // _exit(11);
}
此刻没区别
int main()
{
printf("you can see me!");
exit(11); // _exit(11);
}
_exit 不能打印出缓冲区中的数据。
_exit 在 OS 内部会直接终止进程;exit 终止进程之前会冲刷缓冲区,关闭流,所以 exit 可以让我们看到刷新的结果
_exit 是系统调用,认为程序调用接口,直接在进程场面上终止进程了;exit 会将应用层中的东西刷新,再调用 _exit
两者是调用和被调用的关系。
故 printf 一定是先把数据写入缓冲区中,合适的时候,再进行刷新。那么这个缓冲区绝对不在哪里?
决定不在内核里!因为如果在内核中,_exit 终止的时候,会把内核中缓冲区的数据也做一下刷新,因为 OS 不做浪费资源的事情,如果不刷新,为什么要维护缓冲区把数据保存起来呢?所以一定不在内核中。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!