进程的创建、回收和终止

版权声明:未经同意,严禁转载 https://blog.csdn.net/pengchengliu/article/details/80501591
一、进程的创建:       //内核里面只有fork 和exec两种可以创建进程。其他方式都是使用的这两种方式,如system()封装了exec。
system()
fork()
exec()
popen()

二、进程的回收:
孤儿进程
僵尸进程
wait()
三、进程的终止:
main函数的自热返回;
调用exit函数;
调用_exit函数;
调用abort函数;
接收能导致进程终止的信号Ctrl +c SSIGINT  ctrl+\ SIGQUIT

----------------------------------------------------------------------------------------------------------------------------------------------------------------
一、进程创建     
1.system函数:不常用
#include <stdlib.h>
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 <unistd.h>
#include <stdio.h>
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 <sys/types.h>         
 #include <sys/wait.h>         
 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文件





展开阅读全文

没有更多推荐了,返回首页