execl进程功能重载。
execl函数有很多个组成,做的事情都是差不多的。
开发者可以使用它,加载一个进程的用户空间(读取一个现有程序的功能),赋予子进程。
一个进程的核心内容都在用户层。而不是内核层。
如果想让子进程有浏览器进程,那么就可以把浏览器贴到子进程里。
也就是说,直接拿过来用,贴给子进程。
例如将一个父进程processA,没有浏览器功能,但是想给一个,因此使用execl()函数,浏览器会有路径,例如是/bin/browser,那么就利用execl函数,启动加载浏览器程序的用户层,浏览器也有用户层,函数就可以读取浏览器的用户层,然后复制给自己,用的函数是load(覆盖掉进程原有的用户层),此时,我们的processA进程,就可以使用浏览器了。
此时,重载完毕,该进程具备了浏览器特征以及功能。
可以对现成的数据进行重载,就可以直接拿过来了。
用which查看路径.
execl("/bin/browser", "firefox", "https://www.baidu.com", NULL);
参数最后必须传NULL,否则不能执行。
参数:
用which找到浏览器的绝对路径,然后用execl函数在子进程中编写。
- execl("/usr/bin/firefox","firefox","www.baidu.com",NULL);
- exit(0);
因此在执行之后,先父进程进行子进程创建,然后子进程进入else if,实现execl函数,也就是进入了浏览器的百度页面。
因此,子进程就是浏览器。子进程是重载出来的浏览器。
exec是一个覆盖,进行了重载操作之后,原来的内容就彻底消失了。
execl:如果子进程有自定义任务和工作,需要在fork之后,和exec之前完成。也就是pid=0的里面。
正是因为exec之后,子进程就被覆盖了。exec就不能执行原有的功能了。
因此,exit(0)这句话根本没有执行,我们进程的退出是关闭浏览器的操作才退出的。
是exec函数变体。
区别:就是第一个参数并不用放置绝对路径了,因此,直接写进程名字就好了,并没有什么区别。
没有l了,l是每一个参数都是一个字符串。
但是v是数组,都是放入数组里。
- const char* array[]={"firefox","www.baidu.com"};
- execv("/usr/bin/firefox",array);
作用和效果都是一样的。根据用户喜好。
两种的结合。
execvp("firefox", array);
execle(), execve()也是两种新的函数,只是细微的变化。
l:每个参数都是字符串。
v:这种exec接口需要用户将命令参数封装在数组中,传参时传入数组首地址。
p:path,,可以不用填写绝对位置,可以自行查找程序位置。
e:进程创建时,有环境变量,系统有一个环境变量表,进程创建后,系统会拷贝一份环境变量给进程。
e表示的就是自定义环境变量,替换进程默认的环境变量。
正常fork的流程之后,如果查看进程状态,会发现,父进程是./app,但是子进程却是:[app]
丧尸进程:人——病毒致死——结束——攻击性极强(危害)
可以用来描述这个僵尸进程。
进程——异常原因——内存泄漏(危害)
是因为没有及时处理。
这些问题的解决方案就是:回收处理。
例子:两个进程,父子pc。
描述:子进程先于父进程结束,然后父进程未对子进程进行有效的回收操作,子进程变成僵尸进程,导致内存泄漏。
与孤儿进程不同:是父进程先于子进程结束。
子先死,是僵尸,父先死,是孤儿。
exit函数的底层有另一个函数:_EXIT函数,负责释放回收进程资源的。是系统内核kernel负责的,执行并释放。
完全释放用户层,不会有残留。
但是内核空间,并没有回收干净,pcb没有去动。内核层部分回收。
因此,PCB残留,就是内存泄漏。危害是最小的。
pcb不小,有的东西还有额外的内容。
进程创建前提:有可用pcb。
也就是说,你得能先创建出一个pcb,创建之后,才能创建进程。
危害:
回收作用,回收函数。
用来处理pcb。
pid_t zpid=wait(NULL)
wait函数是一个阻塞函数,阻塞回收僵尸进程(的PCB),成功一次回收一个pcb,而不是调用一次全部回收,如果回收1000个就调用1000次。
成功返回僵尸进程pid,失败返回-1,绝大多数不会失败,如果没有子进程调用了wait,就失败了。
头文件
此时执行完wait函数,僵尸进程消失了。
- zpid=wait(NULL);
- if(zpid>0){
- printf("zpid is %d\n",zpid);
- }
wait函数会等待子进程结束之后,再退出。
也就是说是阻塞函数,父进程到wait之后阻塞了,子进程退出之后开始执行父进程。
为什么内核不把pcb回收掉?
pcb让父进程回收是有原因的,例如子意外了,父就需要处理,但是子的遗骸不可能会消失,也就是说,必须父进程回收,父进程可以通过pcb分析出子进程回收的愿意的。
父进程是子进程的直接负责人。内核并没有这个权限。
退出信息存在PCB中。
wait(int* status),验shi信息存入status,NULL就不进行验。
如果父进程需要对子进程进行退出校验,那么wait函数必须使用status参数传出子进程退出信息。
status退出信息,系统会提供一些校验函数:
WIFEXITED(int* status)
exit(0)和return 0都属于正常的退出。正常退出范畴。
if(WIFEXITED(status))
如果是真的,就是正常退出,反之是假的。
获取退出码:WEXITSTATUS(status),一般是上一个是真的,才会调用这个。
返回status的退出码或返回值。
除了正常退出,还会有异常退出,是信号查找。
Linux经典是信号,一般都是用于杀死进程。
WIFSIGNALED(status),判断是否是信号杀死,返回真假。
WTERMSIG(status),返回杀死子进程的信号编号。
- int main(void){
- pid_t pid;
- int i=0;
- for(i;i<2;++i){
- pid=fork();
- if(!pid)break;
- }
- if(pid>0){
- pid_t zpid;
- int status;
- printf("im father, my pid is %d\n,im waiting...",getpid());
- while((zpid=wait(&status))>0){
- //PCB推出校验
- if(WIFEXITED(status))printf("zombie pid %d return value exitcode %d\n",zpid,WEXITSTATUS(status));
- if(WIFSIGNALED(status))printf("zombie pid %d kill signal NO %d\n",zpid,WTERMSIG(status));
- }
- }else if(!pid){
- if(!i){
- printf("im child%d,my pid is %d\n,im sleeping...",i,getpid());
- sleep(2);
- exit(0);//正常推出
- }else{
- //异常推出
- while(1){
- printf("im child%d,my pid is %d\n",i,getpid());
- sleep(2);
- }
- }
- }else{
- perror("call failed\n");
- exit(1);
- }
- return 0;
- }

结果就是,0号子进程通过正常的方式回收并退出。
但是1号子进程一直在执行,我们通过kill -11 zpid进行异常退出。
就返回了异常退出的信息。