目录
一、进程创建
写实拷贝:
二、进程终止
三、进程等待
一、进程创建
描述一下,fork创建子进程,操作系统都做了什么?
fork后父子进程是全部包括之前的代码都共享;
fork创建子进程(内核数据结构(OS创建) + 进程代码和数据(一般从磁盘中来,也就是C/C++程序加载之后的结果));
创建子进程,给子进程分配对应的内核结构,必须是子进程自己独有的,因为进程具有独立性,理论上,子进程也要有自己的代码和数据,可是一般而言,我们没有加载的过程,也就是说,子进程没有自己的代码和数据,所以子进程只能使用父进程的代码和数据那么:
代码:都是不可修改的,只能读取,所以父子共享是没有问题的!
数据:可能被修改的,所以必须分离!
对于数据:
创建子进程时,不需要将不会访问的,或者只会读取的数据都拷贝一份,但是为了保证独立性,还是需要一定数据的拷贝,这里只拷贝将来会被父进程或者子进程写入的数据;但是一般而言,即使是操作系统也无法提前知道那些空间可能会被写入的,而且即使是提前知道了,也不能保证立马就使用拷贝的空间。所以OS选择了写实拷贝技术,来进行父子进程数据的分离。
写实拷贝:父子进程共用代码,只有在进行数据写入时才重新内存分布;
为什么选择写实拷贝?
1.用的时候在给你分配空间(高效)
2.操作系统无法在代码执行前预知那些空间会被拷贝
1.我们的代码汇编之后,会有很多行的代码,而且每行代码都加载到内存之后,都有对应的地址;
2.因为进程随时可能被中断(可能并没有执行完),下次回来,还必须保证从之前的位置继续运行,就要求CPU必须随时记录下当前进程执行的位置,所以CPU内有对应的寄存器数据,用来记录当前进程的执行位置!
寄存器在CPU内,只有一份,寄存器内的数据,是可以有多份的 !(进程的上下文)
fork创建子进程的时候,虽然父子进程会各自调度,各种会修改EIP(存储进程代码位置的寄存器),但是已经不重要了,因为子进程已经认为自己的ETI起始值是在fork之后的代码了!!所以子进程会从fork之后的代码开始执行;
写实拷贝:
父子进程代码共享,子进程页表拷贝父进程页表,在子进程或者父进程进行写入操作时,页表发生改变,重新分配内存;
因为有写实拷贝技术的存在,所以,父子进程得以彻底分离,完成了进程独立性的技术保证;
写实拷贝是一种延时申请技术,可以提高整机的内存使用率;
二、进程终止
1.进程终止时,操作系统做了什么?
释放进程中申请的相关内核数据结构和对应数据和代码;(本质就是释放系统资源)
2.进程终止的常见方式?
- a.代码跑完,结果正确
- b.代码跑完,结果不正确
- c.代码没有跑完,程序崩溃了(信号部分,涉及一点点)
main函数返回值的含义是什么?return 0的含义是什么?为什么总是0?
0:退出码;返回上一进程,用来判断进程执行结果的,可以忽略。
命令:echo $? // 获取上一个进程的退出码
程序崩溃了,退出码无意义。一般而言退出码对应的return语句,没有被执行!
3.用代码,如果终止一个进程?
- main函数内的return语句就是终止进程的,return 退出码;
- exit(int status)在任何地方调用,都表示终止进程;(库函数)
- _exit(int status)终止进程;(系统接口直接终止进程,不进行清理函数和缓冲区的刷新)
库函数 vs 系统接口
逐渐底层:语言 -> 库函数 -> 系统接口 -> 操作系统
printf - \n 数据是存放在“缓冲区”的,那么这个”缓冲区“在哪里,谁维护的?
一定不在操作系统内部;如果是操作系统维护的,那么_exit(int statut) 就可以刷新缓冲区;"缓冲区"是C标准库维护的;
三、进程等待
- 子进程退出,父进程不管子进程,子进程就要处于僵尸状态 ---- 导致内存泄漏
- 父进程创建子进程,是让子进程办事的,那么子进程把任务完成得怎么样?父进程需要关心吗?如果需要,如何得知?如果不需要,该怎么处理?
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
僵尸状态的进程,即使是kill -9也无法杀死一个已经死去的进程;
如何等待?等待是什么?
wait:回收僵尸进程的问题
参数:wait(int *status)
让父进程来等待子进程退出,并回收
pid_t ret = wait(NULL);if (ret > 0){printf("等待子进程成功,ret: %d\n", ret); }
waitpid;获取子进程退出结果的问题
参数:waitpid(pid_t pid, int *status, int option)
- pid = -1 所有子进程
- pid > 0 指定的子进程
- status 获取子进程退出结果(退出码)输出型参数(是按照bit位的方式进行划分的,只使用低16位,高8位表示退出码)
- option 默认为0 (阻塞态等待)
进程异常退出,或者崩溃,本质是操作系统将这进程杀掉了 (通过发送信号的方式);
status的低7位bit位,表示进程收到的信号!
程序异常,不仅仅是内部代码的问题,也可能是外部杀掉了你的进程;
- 父进程通过wait/waitpid可以拿到子进程的退出结果,为什么要用wait/waitpid呢?不直接使用全局变量?(不可以,进程具有独立性,写实拷贝)
- 既然进程具有独立性的,进程的退出码不也是进程的数据码?父进程又怎么拿到的呢?wait/waitpid究竟做了什么呢?
僵尸进程:至少要保留进程的PCB信息,task_struct里面保留了任何进程退出时的结果信息!可知wait/waitpid本质是读取了子进程的PCB结构体中的信息;
- wait/waitpid有这个权限吗?
当然有,它是系统调用,操作系统操作!