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标准库


相关文章
|
8天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
61 13
|
3月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
160 6
|
3月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
170 3
|
3月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
3月前
|
编译器
【收藏】内核级利用通用Hook函数方法检测进程
【收藏】内核级利用通用Hook函数方法检测进程
|
3月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
4月前
|
Linux PHP
Linux CentOS 宝塔 Suhosin禁用php5.6版本eval函数详细图文教程
【8月更文挑战第27天】本文介绍两种禁用PHP执行的方法:使用`PHP_diseval_extension`禁用和通过`suhosin`禁用。由于`suhosin`不支持PHP8,仅适用于PHP7及以下版本,若服务器安装了PHP5.6,则需对应安装`suhosin-0.9.38`版本。文章提供了详细的安装步骤,并强调了宝塔环境下与普通环境下的PHP路径差异。安装完成后,在`php.ini`中添加`suhosin.so`扩展并设置`executor.disable_eval = on`以禁用执行功能。最后通过测试代码验证是否成功禁用,并重启`php-fpm`服务生效。
62 2
|
4月前
|
Shell Linux C语言
Linux0.11 execve函数(六)
Linux0.11 execve函数(六)
90 1
|
4月前
|
Linux
Linux0.11 文件打开open函数(五)
Linux0.11 文件打开open函数(五)
56 0
|
4月前
|
存储 Linux 调度
Linux 0.11 fork 函数(二)
Linux 0.11 fork 函数(二)
54 0