所以就可以改良成这个样子:
结论:
进程退出会变成僵尸,之后将自己的推出结果放入PCB,wait/waitpid是系统调用,有资格去读取PCB中的资源。
阻塞与非阻塞
阻塞
父进程一直在等子进程结束回收资源。
非阻塞
父进程一段时间过来看一下子进程是否结束,如果没结束可以做其他事情,这个叫轮询方式。
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
NO1就父进程是不是询问子进程是否退出。
如果在询问之后不子进程没有准备完毕,父进程则可以做一些其他的事情。
至于非阻塞和阻塞谁更好,这个要看实际场景。
进程替换
之前说过创建子进程的目的是让子进程去帮忙“做事”,可是为什么要去让子进程帮忙做事呢?
首先说目的:
1.想让子进程执行父进程磁盘代码其中的一部分。
2.想让子进程执行一个全新的程序。
替换原理
一个可执行程序被首先被加载到内存中,然后执行代码,然后代码中有操作让本程序执行一个新程序,这个时候就会将指定执行的程序的代码和数据覆盖掉原本的代码和数据,在整个过程中并没有产生新的进程,这就是为什么每次都要去创建一个子进程来去执行新程序。
替换函数
执行系统命令
这些函数的作用是将指定的程序加载到内存当中,让指定的进程执行。
int execl(const char *path, const char *arg, …);
第一个参数是说如何找到程序,第二个参数是我们在命令行解释器怎么调用该程序就怎么写,最后用NULL结尾。
这几个函数统一的是exec,这个函数最后一个l 意思是 list 将参数一个一个传入exec*
这里执行完execl之后,后边打印process就不会执行,因为整个程序的代码和数据已经被覆盖掉了。
并且这类函数返回值只有-1,表示错误。
因为成功之后接下来的代码是不会执行的,所以返回一个正确的值进行判断也毫无意义。
int execlp(const char *file, const char *arg, …);
结尾是p的第一个参数不用去指定路径了,他会在环境变量PATH,进行可执行程序的查找
int execv(const char *path, const char *argv[], …);
v是vector的意思,第二个参数是让我们把所有可执行参数放入数组中传过去。
int execvp(const char *file, const char *argv[], …);
这个就不演示了,这两个参数上面都说过。
上面的只是在执行系统命令,那么想执行自己写的程序该怎么办呢?
执行自己写的程序
首先来说一下makefile这个文件:
先创建一个.c文件
如果我想让test.c去调用process.c,首先要生成这两个可执行程序,但是makefile只会默认的生成第一个可执行程序,后面的就不会再去执行,所以我们要这样写:
因为是调用程序,所以不管是什么语言的程序都可调用。
int execle(const char *path, const char *arg, …,char *const envp[]);
最后一个参数是自定义环境变量的意思。
现在的自定义环境变量还没定义,所以为空。