概念
进程状态转换
信号:
让进程在后台运行;
但能够将输出的内容到终端,这个时候可以去输入指令;
运行的程序的父进程都是当前的终端;
进程创建;
如果成功;在父进程里返回子进程的子进程号;返回0实在子进程中返回的;失败返回-1;
查看是那个进程执行的for循环:
父进程的进程号:87650;子进程进程号:87651 ;父进程的父进程是当前终端:87186
首先,父进程运行for :i=0--->子进程运行for : i=0-->,,,,
父进程子进程交替运行;
父子进程虚拟地址空间情况
用户区数据一样;pid不同;
代码段返回值是全部变量-在用户区栈空间;父进程返回子进程的pid,子进程返回0;
子进程会复制父进程的内容;两者在不同的内存空间,但是内存空间中内容不同,如果修改各自内存空间中的内容,互不影响;
事实上:读时共享,写时拷贝:
因为子进程直接拷贝父进程的内存到新的内存空间浪费资源;所以在只读的时候是共享原先的内存,当数据发生修改时拷贝新的内存空间;
父子进程关系及GDB多进程调试
默认GDB追踪父进程;子进程直接运行完;
改为GDB追踪子进程:
默认GDB调试父进程;并且默认其他程序脱离调试;这样父进程调试时,子进程运行完;
设置默认其他程序不脱离--off :
问题:GDB版本8.1.0进行多进程调试可能会有问题;
在7.11.1上测试:
*表示当前正在调试的进程--切换调试的进程:
再continue:
使某个进程脱离GDB调试:
exec函数族
:一系列功能相同或者相似的函数
在调用exec函数族时,执行a.out 可执行程序-这个时候原先进程中用户区会被a.out代替;原来的用户区数据就没有了-然后运行a.out中的代码;即父进程ID啥的都没变(内核区),但是进程中的内容变了.
前6个都是标准C库的函数,最后一个才是Linux标准函数;
execl :只有调用失败时会有返回值为-1,并设置errno
调用成功没有返回值;
一般会创建一个子进程,在子进程中运行exec;
第一个子进程中的printf不会执行,因为在子进程中执行exec;
第二个for循环是子进程和父进程共享的代码;但是只有父进程会执行,因为子进程的用户区已经被代替了;
可以输出pid查看;
子进程没有输出,只输出了父进程的pid;子进程输出了可执行程序中的内容;并且是分开执行的;
还可以执行一个shell命令:
在execlp中不用写绝对路劲-写文件名;
execlp会从环境变量path中去找ps找得到执行,找不到不执行,返回-1和errno;
不需要传具体路劲;具体从自己定义的环境中去找
进程退出、孤儿进程、僵尸进程
父进程要负责回收子进程的资源;
status是退出的状态,可以在父进程中通过参数查看子进程因为什么原因退出;
exit标准C库函数_exit函数是Linux函数;
执行了exit,return 0就不会调用了;
_exit
区别exit会在退出系统函数之前会刷新IO缓冲,所以会输出后面的word;
由于hello后面有换行,所以会输出word后没有换行,数据此时还在缓冲区内,由于_exit()函数不刷新缓冲区;就不会输出word;
父进程死了,就没有进程来回收子进程的资源;
所以内核会将子进程的父进程设置为init;帮忙善后;
sleep1秒;这个时候父进程已经运行结束;子进程变成孤儿进程;
这个时候子进程的父进程变成了1; 子进程还在运行状态;但此时终端也是阻塞的状态;
进程运行时切换到后台,进程运行结束后就切换到前台;但是子进程还没死还可以输出;
子进程和父进程内核区数据共享,子进程输出内容还在当前终端;
孤儿进程没有危害;
--》父进程必须要有义务回收子进程的资源;
僵尸进程:子进程死了,父进程没回收资源(没有调用wait())就变成了僵尸进程;一直占用进程号
例如:子进程结束,父进程一直在运行且不调用wait();子进程就变成了状态Z(zombie);
通过:ps aux 查看
kill -9也不能杀死;
杀死父进程;僵尸进程就杀死了;
wait():
等待子进程状态的改变;包含子进程状态变化的信息;状态的改变:子进程终止,被一个信号挂起,或者被信号终止;
这种情况下:wait()允许系统释放资源和子进程相关的;
如果没有提供wait(_)就变成僵尸进程;
调用wait()函数的进程会被挂起阻塞,直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒。如果没有子进程了或者子进程都结了;函数会立刻返回-1;
这样不是产生5个子进程;因为子进程里面也会产生子进程.....
当pid==0;即当前是子进程;就不再继续运行for循环;也就不会产生孙子进程;
如果
如果进程正常退出,就会return 0;
int ret = wait(&s);
传入int 类型参数的地址;wait函数会将进程状态传入参数;
WIFEXITED(s);//是不是正常退出;
WIFSIGNALED(s);//是不是异常终止;(被信号干掉了)
exit(1):退出状态码为1;
使用kill杀死
默认:等待,当没有子进程结束就阻塞;如果有子进程结束就调用一下;没有子进程、所有子进程都结束了,返回-1;
参数:(pid):
>0:回收指定pid的进程;
=0:回收当前进程组的所有子进程;
=-1 :回收所有子进程;(相当于wait())
<-1:回收某个进程组的组id的绝对值;回收指定进程组的子进程;