(。・∀・)ノ゙嗨!你好这里是ky233的主页ky233_-CSDN博客Linux初识进程,C++类和对象中,C++类和对象上篇https://blog.csdn.net/ky233?type=blog
点个关注不迷路⌯'▾'⌯
目录
一、进程创建
1、fork函数的认识
1.请你描述一下,fork创建子进程,操作系统都做了什么?
2.fork之后,父子进程的代码是共享的,那么是只有之后共享的还是全部都是共享的呢?
2.写时拷贝
二、进程终止
1.进程终止时,操作系统做了什么?
2.进程终止的常见方式
1.那么main函数为什么最后都要return 0呢?
2.第三种情况
3.如何用代码终止一个进程
1.什么是一个正确的终止
三、进程等待
1.为什么要有进程等待
2.wait和waitpid
1.wait
2.waitpid
3.父进程是怎么拿到这子进程的退出结果的呢?
由于之前讲过一部分的fork,这里就不再过多的介绍,只是单独说一下面试题
创建子进程,给子进程分配相应的内核数据结构,必须让子进程自己独立,因为进程是具有独立性的,所以理论上子进程要有自己的代码和数据,可是我们没有加载的过程,那么子进程借用使用父进程的代码,而数据是可以被修改的所以必须要父子进程分离
这里要注意,在使用fork的时候,并不是fork之后的代码才被共享,而是所有的代码都会被共享
这是因进程随时都有可能被中断,下次回来必须要从之前的位置继续运行,所以CPU内有对应的寄存器数据,用来记录当前进程执行的位置,又因为,寄存器内的数据是可以有多份的,所以创建的时候,虽然父子进程各自调度,但是因为fork寄存器中的父进程在寄存器中保存的上下文也一同被子进程共享了,所以fork之后子进程就运行的是fork之后的代码了。(也就是说,创建的时候连父进程执行的位置也一起复制了,所以就接着执行了,而不代表着子进程不能访问之前的数据)
不知道大家还记得不记得,fork函数的父子进程的同一个数据,在同一地址,同一时间访问,有两个不一样的值,这个就叫做发生了写时拷贝。
那么为什么要有写实拷贝呢?
是因为创建子进程的时候,不需要将不会访问的或者只读取的数据拷贝,但是OS也不知道什么样的数据会被写入,即便是知道,那么拷贝了就会提前写入吗?也是不一定的,所以操作系统采用了写时拷贝来将父子进程的数据进行分离!这是一种高效的使用内存的方法!!
这张图说的很清楚,在子进程刚创建的时候,我们的父子进程是完全一样的,当子进程要去修改物理内存的内容的时候,会拷贝一份所共享的数据,然后修改页表的指向。
所以为什么要有写实拷贝总结:
这个话题很简单,当然是释放进程申请的相关内核数据结构和对应的数据代码,本质来说就是一种资源的释放。
前两个问题可以一起讨论,
1 #include
2 int main()
3 {
4 printf("hello Linux\n");
5
6 return 10;
7 }
为什么代码最后总是0呢?
但其实main函数的返回值并不总是0,这个是进程的退出码。其中0表示结果正确,非0表示结果不正确。
我们可以通过下面这个命令来获取最近一个进程的退出码:echo $?
这是返回给上一进程用来评判该进程执行结果用的。我们可以对返回值做判断来判别出你写的代码是否正确,如返回值正确,是自己想要的结果就是0,如不是则if直接返回一个非0的数字,非0值有无数个,以此来表示不同错误的原因,从而方便定位错误的原因。
我们可以用strerror来打印常见的错误码
4 int main()
5 {
6 int i = 0;
7 for(i=0;i<150;i++)
8 {
9 printf("%d:%s\n",i,strerror(i));
10
11 }
12 // printf("hello Linux\n");
13
14 return 0;
15 }
程序崩溃的时候,退出码是没有意义的,一般没有执行retuan语句
首先是return语句,在main中就是终止进程,在其他函数里面是返回上级
还有exit代码在任何时候都作为进程终止。
void print()
6 {
7 printf("hello world\n");
8 exit(0);
9 }
10
11 int main()
12 {
13 print();
14 printf("hello Linux\n");
15
16 return 0;
17 }
我们可以看到程序只运行了“hello world”就退出了。
第一点:如果父进程不管子进程,那么子进程就会处于僵尸状态,会导致内存泄漏。
第二点:父进程要看子进程的状态。
总结的来说就是:父进程通过进程等待的方式,回收子进程资源,获取子进程的退出信息。
2.如何等待以及等待是什么?
首先我们可以看到此时子进程的状态是僵尸进程,
那么我们在用上wait之后,父进程就会在等待子进程运行结束并且回收,来避免僵尸进程造成的内存泄漏,注意,wait默认是父进程在阻塞等待!
5 int main()
6 {
7 pid_t id=fork();
8 if(id==0)
9 {
10 int con=5;
11 while(con)
12 {
13 printf("我是子进程,pid:%d,ppid:%d\n",get pid(),getppid());
14 con--;
15 }
16 exit(0);
17 }
18 else{
19 printf("我是父进程\n");
W> 20 pid_t ret = wait(NULL);
21
22 }
23 return 0;
24 }
pid_t waitpid(pid_t pid, int* status, int options);
pid
表示所等待子进程的pid,其中这里的pid有三种状态
status
表示状态,为整型,高16位暂不涉及,低 16
位中,次低 8
位表示退出码,第 7
位表示 core dump
,低 7
位表示终止信号。
options
是选项,可以选择父进程是否需要阻塞等待子进程退出
我们都知道进程具有独立性,进程退出码也是子进程的数据,那么父进程是怎么拿到的呢?
是我们的PCB结构体中保存了任何进程退出是的退出结果信息,本质就是读取了子进程的PCB结构体!而这两个函数是系统调用的,那就是操作系统,是OS把子进程的PCB结构体里的退出信息按照位图的方式放到了status这个数里面!