前言:前面我们了解了进程的创建fork()、进程号的获取,现在我们基序了解一下进程的相关操作:进程的终止、等待以及特殊进程。
1. 进程的终止
进程终止即结束一个进程!
进程终止的方法有以下几种:
1)进程的标准终止方法:exit(); 在当前进程中任何地方调用该函数,进程终止!
结束进程时,会刷新缓存区! 缓存区中数据会正常起作用!
2)return; 用来结束一个函数的!放在主函数中可以结束进程!
结束进程时,会刷新缓存区!
3)_exit(); 功能、用法和exit();相同!
结束进程时,直接结束,不刷新缓存区!
4)abort(); 结束一个进程! 在进程中任何地方调用都会结束当前进程!
结束进程时,会刷新缓存区!
5)给进程发信号! kill -9 PID
直接发信号,杀死PID进程,不会刷新缓存区!
exit(0);(这是最常用的一个方法,和入口main()相呼应)
函数原型:void exit(int status);
函数功能:终止一个进程!该函数是进程的标准出口!
形参列表:status:返回给父进程当前进程的终止状态!
返回值:void
void abort(void);
函数功能:结束一个进程!结束时刷新缓存区!
本质是一个信号!
2. 进程的等待
进程等待主要用途是:父进程等待子进程结束之后,回收释放子进程空间,然后才能结束!
进程的等待主要是为了防止孤儿进程产生!
进程等待函数:wait();
主要用于父进程等待子进程!
wait();
头文件:#include <sys/types.h>
#include <sys/wait.h>
函数原型:pid_t wait(int *status);
函数功能:父进程调用该函数之后,会进入睡眠状态! 挂起等待!!!
等到任意一个子进程结束,wait返回等到的子进程进程号。父进程结束!
形参列表:status:接收子进程返回的进程结束状态
返回值:
成功:等待到的子进程进程号
失败:-1
总结:
wait函数只能父进程等待子进程!
wait函数只能等待任意一个子进程结束返回!
wait函数可以和exit结合使用,接收子进程的结束状态!
ps -aux : STAT
运行态:R/R+
就绪态:R/R+
等待态:S/S+
waitpid();
函数原型:pid_t waitpid(pid_t pid, int *status, int options);
函数功能:等待某一个进程结束,返回等待到的进程PID。
形参列表:
pid:进程号
< -1 :等待 进程组ID等于pid绝对值的这些子进程中的任意一个
= -1 :等待任意一个子进程 == wait();
= 0 :等待 进程组ID等于当前调用进程PID号的子进程中的任意一个进程!
> 0 :指定等待进程号为PID的子进程!
status:接收等待到的子进程的返回值
options:操作方式
0:默认操作方式
WNOHANG:非阻塞等待
返回值:
成功:等待到的进程PID
WNOHANG:没有子进程终止返回0
失败:-1
进程组:默认情况下,同一组进程指的是,同一个父进程创建出来的子进程、子进程的子进程...
默认情况下,同一个进程组的这些进程的进程组ID就是父进程PID!
3. 特殊的进程(了解)
3.1 孤儿进程: 父进程已经结束,子进程仍然在运行!
孤儿进程会交由1号进程托管,如果进程中有死循环,则会一直运行下去!
孤儿进程是对系统有危害,尽量避免出现,解决方法:父进程调用wait()函数!
守护进程:对系统的运行起到支持保护作用的进程!特点是系统启动守护进程运行,直到系统关机,守护进程终止!
3.2 僵尸进程:子进程结束,父进程还一直在运行! 子进程就变为僵尸进程!
僵尸进程尽量避免! 把工作量比较大的进程做为子进程!或者调用wait();
补充:
ctrl + z :把当前进程放入到后台执行
fg : 把后台程序重新放回终端执行
4. exec族函数
exec函数:进程替换!
system函数:进程调用!
system();
函数功能:在一个程序内部调用另一个可执行程序!
调用的时候,被调用可执行程序会在内存中开辟一个新的进程空间!
实现形式是函数形式!
exec函数族(了解就行)
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
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 execl(const char *path, const char *arg, ...);
函数功能:进程空间替换! 进程替换过程中进程号不变!!!
形参列表:
path:带路径的可执行程序 <要替换为的可执行程序>
const char *arg, ...:可变参 <可执行程序的传参列表>
返回值:失败:-1(只有失败才有返回值)
例:execl(“/bin/ls”, “声明”, “-l”, “-a”, “--color=auto”, NULL);
功能等于:ls -a -l --color=auto
int execv(const char *path, char *const argv[]);
函数功能:进程空间替换! 进程替换过程中进程号不变!!!
形参列表:
path:带路径的可执行程序 <要替换为的可执行程序>
argv:替换可执行程序的传参 <可执行程序的传参列表>
返回值: 失败:-1
例:char* buf[] = {“声明”, “-l”, “-a”, “--color=auto”, NULL};
等效于:execv(“/bin/ls”, buf);
int execlp(const char *file, const char *arg, ...);
函数功能:进程空间替换! 进程替换过程中进程号不变!!!
形参列表:
file:可执行程序(默认路径可自动识别) <要替换为的可执行程序>
const char *arg, ...:可变参 <可执行程序的传参列表>
返回值:失败:-1
例:execl(“ls”, “声明”, “-l”, “-a”, “--color=auto”, NULL);
等效于:ls -a -l --color=auto
5. 进程创建--vfork
fork(); vfork();
vfork();
函数原型: pid_t vfork(void)
函数功能:创建子进程!
父子进程共有父进程空间,子进程运行结束,父进程才能开始试运行!
子进程结束必须加exit(),否则一直运行!
当子进程调用exec主函数,子进程会分配自己的进程空间!
形参列表:无
返回值:
成功:
返回给父进程:子进程PID
返回给子进程:0
失败:-1
注意:
vfork一般都是和exec族函数结合使用!
保证exec之前不会产生孤儿进程!
节省内存空间!
进程号:PID
从小到大连续分配!
进程结束,进程号释放,但是释放的进程号不会重新分配!
只要系统没有重启,进程号就不会二次分配!