一、进程的创建: //内核里面只有fork 和exec两种可以创建进程。其他方式都是使用的这两种方式,如system()封装了exec。
system()
fork()
exec()
popen()
二、进程的回收:
孤儿进程
僵尸进程
wait()
三、进程的终止:
main函数的自热返回;
调用exit函数;
调用_exit函数;
调用abort函数;
接收能导致进程终止的信号Ctrl +c SSIGINT ctrl+\ SIGQUIT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
一、进程创建
1.system函数:不常用
#include
int main(){
system("ls -l");
//创建了一个ls -l的进程。system("clear")表示清屏。
}
2.fork()
复制出一份。各自是各自的。每个进程都有一个文件表项。
其实就是为了
比如说双胞胎,出生之后完全一样。一个身上有什么东西,另一个人相同的地方就有什么东西。
父进程先返回子进程的pid(),第二次子进程返回0.
例子1:
pid_t pid;
pid=fork();
//fork不需要传递参数。
if(pid==0){
//这里写子进程接下来做的事
printf("%d%d",getpid(),getppid());
while(1);
}else{
//这里写父进程接下来做的事
printf("%d",pid);
}
例子2:
int i=3;
pid_t pid;
pid=fork();
if(pid==0){
i++;
//在这里改变i的值
printf("%d%d",getpid(),getppid());
while(1);
}else{
sleep(10);
//写sleep()是为了确保子进程先运行,执行了i++这条指令。
printf("%d",i);
//打印i,发现i的值还是3.这是因为子进程和父进程各有自己的虚拟内存空间,虽然内存都相同,但是各自是
各自的,创建完了之后,各自就开始操作各自的了。
printf("%d",pid);
}
3.exec系列(工作中用的太少了)
一个进程创建另一个进程,会直接用新进程覆盖原有进程的代码段。
exec系列有6个函数:execl()、execlp()、execle()、execv()、execvp()、execvpe()
例:
add.c
int main(int argc,char *argv[]){
int i=atoi(argv[0]);
int j=atoi(argv[1]);
return i+j;
}
execl.c
#include
#include
int main(){
excel("./add","add","1","2",NULL);
//./可执行文件 argv[0] argv[1] argv[2].......NULL 。参数一定要从argv[0]开始写,最后写NULL
printf("hello\n");
printf("hello\n");及以下的都不会再执行了,因为从excel走进了另一个世界 。
....
}
4.popen
见《进程间通信IPC》中的管道部分
----------------------------------------------------------------------------------------------------------------------------------------------------------------
二、进程回收
孤儿进程:老爹死了你还没死!
如果父进程先于子进程退出,则子进程成为孤儿进程,此时将自动被PID为1的进程(即init)接管。孤儿进程退出后,它的清理工作有祖先进程init自动处理。init清理没那么及时,毕竟不是亲爹。
子进程退出时,应当由父进程回收资源。应当避免父进程退出了,子进程还在的情况。即,
应当避免孤儿进程。所以父进程要等子进程运行完了再退出。
例:孤儿进程
pid_t pid = fork();
if( pid == 0) {
while(1) ;
//父进程退出了,之后子进程还在运行。通过ps -elf 可以看到进程的ppid变成了1.
}else{
exit(0);
}
僵尸进程:你死了没人给你收尸!
如果子进程先退出,系统不会自动清理掉子进程的环境,而必须由父进程调用wait或waitpid函数来完成清理工作,如果父进程不做清理工作,则已经退出的子进程将成为僵尸进程(defunct),在系统中如果存在的僵尸(zombie)进程过多,将会影响系统的性能,所以必须对僵尸进程进行处理。
例:僵尸进程
pid_t pid=fork();
if(pid == 0){
exit(0);
}else{
while(1);
//子进程都已经退出了,但是父进程没有回收。ps-elf 可以看到僵尸进程。
}
避免僵尸进程(wait函数):
#include
#include
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options); //opeions若为WNOHANG,则父进程运行到这,看看有僵尸就回收,没僵尸就直接走
了,往下运行,不等了。
例1:
pid_t pid;
pid=fork();
if(pid == 0){
sleep(5);
exit(0);
}else{
wait(NULL);
//子进程sleep(5)期间,可以看到父进程的状态时阻塞(sleep),等待子进程结束。等子进程结束之后回收资源。
exit(0);
wait(NULL)表示等待所有进程。
}
例2:
pid_t pid;
pid=fork();
if(pid == 0){
sleep(5);
exit(0);
}else{
waitpid(pid,NULL,0);
//等待指定pid的子进程。
exit(0);
}
}
例3:
pid_t pid;
pid=fork();
if(pid == 0){
while(1);
exit(2);
//如果进程正常结束,返回2。如果不正常的话(比如,ctrl+c),返回0.
}else{
int status; //status存放子进程的退出状态,status含有很多信息,其中就包括返回值。
wait(&status);
printf("I am wake\n");
if(
WIFEXITED(status)){
//这个宏用
来获取状态信息。如果进程正常结束,则结果非零。
printf("the exit value=%d\n",
WEXITSTATUS(status));
//这个宏返回子进程的退出码。这个例子中是2.
}else{
printf("the child abort\n");
//如果WIFEXITED(status)==0,说明不是正常退出。
}
exit(0);
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------
进程终止
进程终止有5种方式:
main函数的自热返回;
调用exit函数;
调用_exit函数;
调用abort函数;
接收能导致进程终止的信号Ctrl +c SSIGINT ctrl+\ SIGQUIT
main函数的自然返回
return 一级一级返回。
调用exit函数
exit直接退出进程。
例子:
int main(){
pid_t pid;
pid=fork();
if(pid == 0){
exit(0);
}else{
printf("child pid=%d\n",pid);
exit(0);
}
}
调用_exit函数(几乎不用)
printf("helloworld");
exit(); //会清理缓冲区,所以能正常打印出来。
printf("helloworld");
_exit();
//printf()只是把"helloworld"放进去,没有刷新,就直接退出了。所以没有打印出来。
abort()函数(用的很少)
自己给自己发一个自杀信号。比如,发现传参错误。
kill -l 可以看到信号都有哪些。abort和最后一个发信号可以理解为是一样的,都是发信号。
abort会产生core文件