进程创建
fork函数的认识
#include <unistd.h>
pid_t fork(void)
#include <stdio.h> #include <unistd.h>int main() {printf("我是父进程!\n");pid_t id = fork();if(id < 0){printf("创建子进程失败\n");return 1;}else if{//子进程while(1){printf("我是子进程:pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}else{//父进程while(1){printf("我是子进程:pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}return 0; }
请描述一下,fork创建子进程,操作系统都做了什么?
fork创建子进程就是系统里多了一个进程,也就是进程=内核数据结构+进程代码和数据;一般从磁盘中来,也就是C\C++程序加载后的结果;分配新的内存块和内核数据结构给子进程;将父进程部分数据结构内容拷贝到到子进程;添加子进程到系统进程列表当中,fork返回开始调度器调度;
创建子进程给子进程分配对应的内核结构,理论上子进程也要有自己的代码和数据,但是一般子进程没有加载的过程,也就是说子进程没有自己的代码和数据,所以子进程只能使用父进程的代码和数据;
代码:都是不可以被写的,只能读取,所以父子进程共享没有问题;
数据:可能被修改,所以必须分离;
当想要修改数据时,有两种方案
1、创建进程时,就直接拷贝分离,也就是将父进程给子进程拷贝一份,但这样做有一个问题,可能拷贝子进程根本不会用到的数据空间,即使用到了也就是读取;
#include <stdio.h> #include <unistd.h>int main() {char *str = "aaa";char *str1 = "aaa";printf("%p\n", str);printf("%p\n", str1);return 0; }
此时打印出来的结果地址是相同的,编译器编译程序的时候都知道使用这种方式节省空间,所以操作系统在创建子进程的时候,不需要将不会被访问的或者只会读取的数据,拷贝出一份;那么什么样的数据值的被拷贝呢?将来会被父或子进程写入的数据;一般来说,即便是操作系统也无法提前知道哪些空间可能会被写入,并且即便是提前拷贝了可能也不会立刻使用,所以操作系统选择了写时拷贝技术将父子进程的数据进行分离;
2、写时拷贝;
fork之后是所有的代码共享,不是fork之后的代码共享;
原因:首先,当代吗进行汇编后,会有很多行代码,而且每行代码加载到内存后都有对应的地址;其次,因为进程随时可能被中断(可能没有执行完),下次回来,还必须从之前的位置继续运行,就要求CPU必须随时记录下当前进程执行的位置,所以,CPU内有对应的寄存器数据,用来记录当前进程执行的位置,寄存器叫做EIP,也叫做pc(point code)指针,程序计数器;寄存器在CPU内只有一份,但是寄存器的数据,可以有很多份,而这个数据也就是上下文数据,而这个上下文数据,通过写时拷贝给子进程,父子进程各自调度,各自会修改自己的EIP,但是此时子进程已经认为自己的EIP起始值就是fork之后的代码;
因为有写时拷贝,所以父子进程得以彻底分离,完成了进程独立性的技术保证,是一种延时申请技术,可以提高整机内存的使用率;
fork 的常规用法
一个父进程希望子进程复制自己,是父子进程执行同一份代码的不同区域;
父进程和子进程执行完全不同的代码;
一般而言父子进程实际使用时,像现实一样希望子承父业,或者父子从事不同的行业;
fork失败的原因
系统中有太多的进程;(内存不足)
实际用户的进程数超过了限制;(一般普通用户的进程数是有限制的)几百几千进程;
进程终止
进程终止时操作系统做了什么?
释放进程申请的相关数据结构和对应的代码和数据;本质就是释放系统资源;内存或CPU等;
进程终止的常见方式?
代码跑完,结果正确;
代码跑完,结果不正确;(代码中的逻辑有问题)
代码没跑完,程序崩溃了;(信号部分)
main函数的返回值?main返回值的意义是什么?return 0的含义是什么?为什么总是 0 ?
#include <stdio.h>int main() {return 0; }
return后面的返回值并不是总是0的;
main的返回值是进程的退出码;
return 0 表示的是进程的运行结果正确,当运行结果不正确的时候返回值是非0;
使用 echo $? 可以获取最近一次进程返回的退出码;
返回值的意义是返回给上一级进程,用来评判进程执行结果用的;例;
#include <stdio.h> #include <unistd.h>int sum(int top) {int s=0;for(int i=1;i<=top;i++){s+=i;}return s; }int main() {int ret=0;int res=sum(100);if(res!=5050){ret=1;//代码运行结果不正确;}return ret; }
可以通过对返回值做判断来判断自己的程序运行结果正确还是不正确;
如果程序结果正确了就不会关心为什么正确了,但是如果结果不正确,就会关心结果为什么不正确,非0值有无数个,不同的非0值可以表述不同的错误原因;从而给程序在运行结束后在结果不正确时,方便定位错误的原因细节;
退出码除了返回给上一级进程,还有一个就是可以通过退出码快速定位错误原因;
又因为自己定义的退出码系统并不能识别,所以需要用到一个接口,将退出码转化成字符串描述的错误原因; #include <string.h> strerror();
#include <stdio.h> #include <unistd.h> #include <string.h>int sum(int top) {int s=0;for(int i=1;i<=top;i++){s+=i;}return s; }int main() {for(int number=0;number<100;number++){printf("%d: %s\n",number,strerror(number));} }
最终打印出134个退出码和字符串;
我们自己可以使用这些退出码和含义,但是如果想要自己定义也可以自己设计一套退出方案;
用代码如何终止进程?