目录
2.父子进程共享fork之前和fork之后的所有代码,只不过子进程只能执行fork之后的
(1)低16个比特位的次低8比特位(次低8比特位) 是退出码
fork之前父进程独立执行,fork之后, 父子两个执行流分别执行。
那么fork之后,是否只有fork之后的代码是被父子进程共享的? ?
fork之后,父子共享所有的代码,但fork之前的代码也是父子共享的,只不过子进程只能执行fork之后的
子进程执行的后续代码! =共享的所有代码,只不过子进程只能从这里开始执行! !
为什么呢:
CPU中有一个程序计数器叫eip,用途是eip叫做,保存当前正在执行指令的下一条指令!
eip程序计数器会拷贝给子进程,子进程便从该eip所指向的代码处开始执行啦! !
如果我想让子进程拿到fork之前的代码,可以让子进程把CPU中的eip改成main函数入口就可以执行fork之前的代码。

进程=内核的进程数据结构+进程的代码和数据
创建子进程的内核数据结构(struct task_ struct + struct mm_ struct +页表) +代码继承父进程,数据以写时拷贝的方式,来进行共享或者独立!
数据通过写时拷贝保证独立性
代码因为是只读的,不可修改
A.环境变量
B.父进程的文件锁,pending alarms和pending signals
C.当前工作目录
D.进程号
fork函数功能是通过复制父进程,创建一个新的子进程。
根据理解分析,正确选项为A和C选项
C/C++的时候,main函数为什么 return 0; ?
a.return 0,给谁return
b.为何是0?其他值可以吗?
return 0表示进程代码跑完,结果正确
return 非零:结果不正确
在main函数中return代表进程结束。其他非main 函数return 代表函数调用结束
代码跑完,结果正确就没什么好说的就exit(0)/return 0返回码是0;如果代码跑完,结果不正确,那我们最想知道的是失败的原因!
所以:非零标识不同的原因! 比如exit(13)
return X的X叫做进程退出码,表征进程退出的信息,是让父进程读取的! !

echo $? :在bash中,最近一次执行完毕时,对应进程的退出码
解释这里:第一次 echo $? 打印了进程退出码 123 ,第二次 echo $? 打印的是上一次 echo $?的进程退出码,因为上一次 echo $? 执行成功了,所以进程退出码是0,。
一般而言,失败的非零值我该如何设置呢? ?以及默认表达的含义?
可以自定义,也可以用系统定义的sterror。
错误码退出码可以对应不同的错误原因,方便定位问题!
1. 在main函数中return代表进程结束。其他非main 函数return 代表函数调用结束
2.在自己的代码任意地点中main函数/非main函数,调用exit()都叫进程退出,exit(X)中的X是退出码
exit终止进程刷新缓冲区
_exit,是系统调用,直接中止进程,不会有任何刷新操作
终止进程推荐exit或main函数中的return。

会打印: hello bit,即刷新缓冲区
如果是_exit(0),就不会打印任何东西,即不刷新缓冲区
进程 = 内核结构 + 进程代码 和 数据
进程代码 和 数据一定会释放,但是 task/struct && mm_ struct:操作系统可能并不会释放该进程的内核数据结构
因为再次创建对象:1.开辟空间 2.初始化 都要花时间。
linux会维护一张废弃的数据结构链表叫 obj,若释放进程,对应的进程的数据结构会被维护到这个链表中,这个链表没有释放空间,只是被设成无效,需要时就拿,节省开辟时间(这样的链表也称 内核的数据结构缓冲池,操作系统叫:slab分派器)

子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力。
wait()的方案可以解决回收子进程z状态,让子进程进入X十,wait是等待任意一个退出的子进程
wait演示:


pid_ t (int) 的返回值 :
>0:等待子进程成功,返回值就是子进程的pid
<0:等待失败
=0:等待成功,但子进程没有退出
第一个参数 pid:
>0:是几,就代表等待哪一个子进程退出,比如pid=1234, 指定等待
-1:等待任意进程退出(通常是最后退出的那个进程)
第三个参数 option:
0:标识阻塞等待(就是父进程等待子进程死,子进程死后就回收它)
当options被设置为WNOHANG则函数非阻塞(Wait No Hang 夯住了),且当没有子进程退出时,waitpid返回0
int* status:是一个整数,是输出型参数:父进程调用waitpid,可以通过status拿到子进程的退出码(具体过程:子进程退出后变为Z状态—子进程代码释放,只是维护着子进程的进程控制块 task_struct;此时子进程的进程控制块 task_struct中 有两个整形退出码int exit_ code和退出信号 exit_ signal 会被填充,然后父进程会拿到这两个值放入status中,所以输出型参数要先定义然后传参时取地址 waitpid(id, &status, 0),或者传nullptr 是不需要退出码 )
只需要关心改整数的低16个比特位!

证明:让子进程先睡眠5秒,然后退出,退出码设为0。子进程睡眠5秒期间父进程用 wait/waitpid 设成阻塞态,已知返回值:
pid_ t (int) 的返回值 :
>0:等待子进程成功,返回值就是子进程的pid
<0:等待失败 =0:等待成功,但子进程没有退出
利用ret接收返回值,当接收成功时,打印ret(这里的ret就是子进程的pid),
并打印(status>>8) &0xFF ,status>>8是 次低8比特位开始,与上0xFF(8个1),就是退出码。我们会发现status中的退出码确实记录了子进程的退出码

(kill -l 可查退出信号,异常就是因为收到信号,status&0x7F 可打印终止信号)
status&0x7F:status的低8位与上111 1111 ,就可得到终止信号

没有0号信号:

另一个接收


waitpid():
阻塞等待和非阻塞等待
当我们调用某些函数的时候,因为条件不就绪,需要我们阻塞等待,本质:就是当前进
程自己变成阻塞状态,等条件就绪的时候,在被唤醒!(这里的条件不就绪可能是任意的软硬件条件!)
waitpid(-1,&status, WNOHANG);
WNOHANG 父进程为非阻塞等待 (Wait No Hang 夯住了)
返回值:=0,等待成功,但子进程没有退出 ;等待成功返回子进程pid,等待失败返回-1。
waitpid(-1,&status, WNOHANG); 在while循环内每次 waitpid执行一次,就检测一次子进程,如果子进程退出,就等待成功返回子进程pid;如果子进程没有退出,因为是非阻塞等待就等待成功返回0;


- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- typedef void (*handler_t)();
-
- //方法集
- std::vector<handler_t> handlers;
-
- void fun1()
- {
- printf("hello, 我是方法1\n");
- }
- void fun2()
- {
- printf("hello, 我是方法2\n");
- }
-
- void Load()
- {
- //加载方法
- handlers.push_back(fun1);
- handlers.push_back(fun2);
- }
-
- int main()
- {
- pid_t id = fork();
- if(id == 0)
- {
- //子进程
- while(1)
- {
- printf("我是子进程, 我的PID: %d, 我的PPID:%d\n", getpid(), getppid());
- sleep(3);
- }
-
- exit(104);
- }
- else if(id >0)
- {
- //父进程
- // 基于非阻塞的轮询等待方案
- int status = 0;
- while(1)
- {
- pid_t ret = waitpid(-1, &status, WNOHANG);
- if(ret > 0)
- {
- printf("等待成功, %d, exit sig: %d, exit code: %d\n", ret, status&0x7F, (status>>8)&0xFF);
- break;
- }
- else if(ret == 0)
- {
- //等待成功了,但是子进程没有退出
- printf("子进程好了没,奥, 还没,那么我父进程就做其他事情啦...\n");
- if(handlers.empty())
- Load(); 添加任务
- for(auto f : handlers)
- {
- f(); //回调处理对应的任务,即执行任务
- }
- sleep(1);
- }
- else{
- //出错了,暂时不处理
- }
- }
- }
