Linux——进程控制1|再谈fork()|fork常规用法和调用失败原因|进程终止|main函数返回值|exit和_exit|相同点|不同点 |总结 |进程等待(上)

简介: 笔记

再谈fork()


1.png

进程调用fork,当控制转移到内核中的fork代码后,内核做:


分配新的内存块和内核数据结构给子进程



将父进程部分数据结构内容拷贝至子进程



添加子进程到系统进程列表当中



fork返回(当return的时候),开始调度器调度


创建子进程,给子进程分配对应的内核结构,这个结构必须子进程自己独有,因为具有独立性,理论上子进程也要有自己的代码和数据,可是一般而言,我们没有加载的过程,也就是说子进程没有自己的代码和数据。所以,子进程只能使用父进程的代码和数据


代码:都是不可被写的,只能读取,父子共享没问题


数据:可能被修改,所以,必须分离。


对于数据而言创建进程的时候,就直接拷贝分,这种情况可能会拷贝一些不会用到的空间,即使用到了,也只能读取

2.png3.png

编译器编译程序的时候,尚且知道节省空间。创建子进程,不需要将不会被访问的,或者只会读取的数据,拷贝一份。将来被父或子进程写入得数据会被拷贝。一般而言即便是OS,也无法提前知道哪些空间可能会被写入。所以操作系统选择了,写时拷贝技术,将父子进程的数据分离。

4.png

fork之后代码共享,共享的是所有的代码


我们的代码汇编之后,会有很多行代码,而且每行代码加载到内存之后,都有对应的地址


当进程没执行完时候,可能被切换,下次回来,还会从之前切换的位置继续运行,所以CPU要记住这个位置,CPU中有对应的寄存器数据来记录这个当前位置

5.png

pc指针存的是当前正在执行代码的下一行代码的地址,寄存器在CPU内只有一份,但寄存器内的数据(进程的上下文数据)可以有多份


父子进程可以各自调度上下文数据,各自会修改EIP,但是已经不重要了,子进程认为自己的EIP起始值,就是fork之后的代码,子进程从after开始跑,不代表fork之前的代码它看不到


fork常规用法和调用失败原因

一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。



一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数


系统中有太多的进程



实际用户的进程数超过了限制


进程终止


1. 进程终止,操作系统都做了什么?


释放进程申请的相关内核数据结构和对应的数据和代码,本质就是释放系统资源


2.进程终止常见的方式有哪些?


a.代码跑完,结果正确           b.代码跑完,结果不正确          c.代码没有跑完,程序崩溃了(下面例子)

6.png

程序崩溃

7.png



main函数返回值

main函数的返回值并不总是0,return 后面跟的数字是进程退出码,进程退出码表示进程是否正确返回,如果返回0则运行结果正确,如果非0,则结果不正确


查看退出码:echo $?

8.png

我们把进程退出码修改为10,第一次查看退出码结果是10,之后查看结果就会是0,这是因为echo $?获取的是最近一个进程执行完毕后的退出码


main函数返回值的意义,返回给上一级进程(如父进程),用来评判该进程执行结果用的,可以忽略。


我们可以通过退出码判断出运行错误的原因,不同的退出码代表着不同的出错信息


strerror可把一个整数转换成字符串

9.png

查看退出码

10.png

我们可以看到0代表成功


11.png12.png



我们可以自己使用这些退出码和含义,我们也可以自己定义退出码和推出含义,如return 10,我们可认为10是File exists


程序崩溃的时候,退出码无意义,一般而言程序还没执行到return就会崩溃,只有main函数里的return是进程退出,其它函数return起返回作用


exit和_exit

相同点

exit

13.png

_exit

14.png



测试exit

15.png

退出码 111,说明exit在代码的任何地方调用都是直接终止进程

16.png

测试_eixt

17.png

说明exit和_exit共同之处,一执行就退出

18.png



不同点

19.png

正常运行

20.png

去掉\n,由于没了\n,就无法刷新缓冲区,也就是说要打印的内容此时存在了输出缓冲区当中,但exit此时也正常运行

21.png

我们换成_exit


22.png


此时没有打印出来,也就是_exit不会刷新缓冲区

23.png

加上\n之后,正常运行


24.png



总结

25.png

exit最后也会调用_exit, 但在调用_exit之前,还做了其他工作

1. 执行用户通过 atexit或on_exit定义的清理函数。

2. 关闭所有打开的流,所有的缓存数据均被写入


3. 调用_exit


26.png

printf不加\n数据保存在“缓冲区”当中,这个缓冲区一定不在操作系统内部,如果是操作系统维护的,缓冲区_eixt也能刷新,该缓冲区只能在库函数,如C标准库


相关文章
|
6天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
24 3
|
1月前
|
Ubuntu Linux
Linux的基础用法
Linux的基础用法
22 6
|
2月前
|
监控 Linux
Linux系统中du命令与df命令的区别与用法
总的来说,`du` 和 `df` 在磁盘管理中互补使用,能够提供全面的磁盘空间使用信息,帮助用户和管理员有效地监控和管理系统资源。
79 3
|
2月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
95 6
|
2月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
140 3
|
2月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
2月前
|
存储 Ubuntu Linux
linux中的find 命令详细用法
本文介绍了如何将 `find` 命令与 `exec` 结合使用,通过具体示例展示了多种应用场景,如显示文件属性、重命名文件、收集文件大小、删除特定文件、执行工具、更改文件所有权和权限、收集 MD5 值等。文章还探讨了 `{} \;` 和 `{} +` 的区别,并演示了如何结合 `grep` 命令进行内容搜索。最后,介绍了如何在一个 `find` 命令中使用多个 `exec` 命令。这为 Linux 用户提供了强大的文件管理和自动化工具。
|
2月前
|
编译器
【收藏】内核级利用通用Hook函数方法检测进程
【收藏】内核级利用通用Hook函数方法检测进程
|
2月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能