进程控制
进程创建
pid_t fork(void)—通过复制父进程创建一个子进程(数据独有,代码共享)
返回值:对于父进程返回值大于0,是子进程的id,对于子进程返回值等于0,返回值小于0错误
写时拷贝技术:子进程复制父进程,一开始两个进程都是同一块物理内存(但是虚拟地址空间以及页表这些信息都是进程独有的)因为进程间要具备独立性,因此当这块物理内存即将发生改变的时候,给子进程重新开辟空间复制数据
pid_t vfork(void):—创建子进程,父子进程共用一个虚拟地址空间(使用的数据都是同一份),父进程调用vfork会阻塞知道子进程exit退出或进行程序替换之后,才会开始运行(避免调用栈混乱,以及数据的相互影响)
进程终止
作用:退出一个进程
方式:
- 1.在main函数中return退出;只能在main函数中,退出进程前会刷新缓冲区
- 2.在任意位置调用exit接口,可以放在任意位置,退出前会刷新缓冲区
- 3.在任意位置调用_exit接口,可以放在任意位置,退出不会刷新缓冲区
进程退出的场景:
正常退出:使用三种退出方式的进程(进程的退出都在控制中)—结果符合预期与结果不符合预期
异常退出:程序运行中突然因为某些异常,导致中途退出(程序崩溃)
进程等待
父进程等待子进程退出,获取子进程的退出返回值,释放推出的子进程资源,避免子进程成为僵尸进程
僵尸进程:子进程先于父进程退出,但是父进程没有关注子进程的退出状态,子进程为了保存自己的退出状态,因此资源无法完全释放
等待方式:
- int wait(int* status);
是一个阻塞接口,处理退出的子进程,如果没有子进程要退出,则会一直等待,直到有子进程退出才会调用返回
阻塞:为了完成一个功能,发起一个调用,,若不具备功能的条件,则一直等待
非阻塞:为了完成一个功能,发起一个调用,,若不具备功能的条件,则调用报错立即返回
返回值:成功返回处理的子进程的pid,失败则返回-1(比如没有子进程) - int waitpid(int pid,int *status,int option):
与wait不同之处:
1.wait等待的是任意一个子进程的退出(wait是一个父进程假设有很多子进程,任意一个退出,都会处理后调用返回)waitpid可以等待指定的子进程,也可以等待任意一个子进程,通过第一个参数确定(第一个参数pid==-1则表示任意)
2.wait是一个阻塞接口(没有子进程退出则一直等待),waitpid可以默认为阻塞,也可以通过第三个参数设置为非阻塞(第三个参数option=0表示默认阻塞,option =WNHANG则表示非阻塞)
返回值:成功则返回退出子进程的pid大于0,若没有子进程退出返回0,出错返回-1.
非阻塞操作通常需要循环处理,得循环判断能否处理,知道成功为止
程序替换
程序替换:替换一个进程正在调度运行的程序;
说白了就是重新加载另一个程序到内存中,然后将现有的pcb内存指针所指向的内存空间指向这个新的程序(更新页表映射信息),现在的pcb跑去调度这个新的程序了
程序替换后,运行完新的程序就会退出,原先的程序在程序替换以后的代码都不会被运行,相当于程序替换之后之前的代码全部被替换为新的代码了
exec函数族:
- int execl(const char* path,const char* arg,…):path-带路径的程序文件名称 arg/…表示程序的运行参数,逐个赋予,最终以NULL结尾
- int execlp(const char* file,const char* arg,…)PATH环境变量指定了一些路径,execlp会去path环境变量指定的路径下查找程序路径
- int execle(const char* path,const char* arg,…,char* const envp[]);
- int execv(const char* path,char* const argv[]);
- int execvp(const char *file,char *const argv[])
- int execve(const char* file,char const argv[],char const envp[])
关于这些函数的区别:
l和v的区别:程序运行参数的赋予方式不同
有没有p的区别:新的程序文件名称是否需要带路径
有没有e的区别:是否自定义环境变量