笔记二:进程间的通信(fork、孤儿进程,僵死进程等)
       以下是以前学习《unix环境高级编程》时的一些笔记和测试代码,好久没看过了,没有再次验证,存在错误的话,希望见谅,分享下主要是!!!
 
 ps     查看系统中的进程   ps–axj
 
 A与B在用户空间是不能通信的,相当于封闭的房子,也没有窗户,所以在用户空间是无法通信的
 
 二:进程控制相关函数
 
 1.fork()函数
 
 功能:创建一个子进程
 
 参数:没有参数
 
 返回值:成功,返回二个值;0 --- 子进程中  >0(子进程的PID号) ---- 父进程中
 
 出错,返回一个值-1
 
 成功时,内核为何会返还二个值?
 
 用户空间的进程,可以认为是一个封闭的房子(在用户空间),内核是怎样对用户空间这么多进程进行管理,怎样识别,是通过PID。
 
 fork() 调用成功,内核则在用户空间创建一个进程(房子),这个房子和之前的进程代码段,数据段等都一样(除了PID之外)
 
以下两段fork函数简单的使用
 
 
以下两段fork函数简单的使用
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "stdlib.h"
int main()
{
  pid_t pid;
  pid=fork();
  printf("hello linux\n");
  usleep(200);  //200微秒
  printf("11111111111\n");
  while(1);
  return 0;
}#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "stdlib.h"
int main()
{
  pid_t pid;
  pid=fork();
  if(pid >0)
  {
    printf("parent process run\n");
  }
  if(pid ==0)
  {
    printf("child process run\n");
  }
  while(1);
  return 0;
}
 getpid(); 获取本进程的PID号(返回值)
 
 getppid();获取本进程父进程PID号(返回值)
 
 exit():   进程退出函数
什么是僵尸进程?什么是孤儿进程?
 
 
什么是僵尸进程?什么是孤儿进程?
   子进程退出,父进程没有退出,此时子进程处于Z(zobile)(僵尸) 状态,处于此状态的进程仍要耗费系统资源。子进程自己不能回收资源,只有父亲负责为子进程回收资源。父进程退出,子进程没有退出,此时子进程变为孤儿进程,会被1号进程收养,即init进程为其父进程。
 
 例子1:
 
/*僵尸进程    查看进程pid状态等消息    可以终端使用命令ps aux或者ps axj*/
#include "signal.h"
#include "sys/types.h"
#include "stdio.h"
int main()
{
    pid_t pid;
    pid = fork();
    if(pid < 0)
    {
        printf("创建进程失败\n");
        return -1;
    }
    /*对于父进程首先是睡眠(8秒)S状态,8秒后进入R(运行)状态
      对于子进程就变成了T状态;
     */
    if(pid > 0)
    {
        sleep(8);//睡眠状态;
         /*如果设置的waitpid设置为waitpid(pid,NULL,0)为阻塞,那么将不会产生僵尸进程,父进程会为其回收*/
        if(waitpid(pid,NULL,WNOHANG) == 0)//等待子进程结束;可以设置为非阻塞(WNOHANG);
        {
            kill(pid,9);//杀死子进程,父进程没有结束,子进程将变成Z状态,因为父进程没有给子进程回收资源,子进程将变为僵尸进程;
        }
        while(1);
    }
    if(pid == 0)
    {
        printf("raise function before\n");
        raise(SIGTSTP);//暂停程序,相当于ctrl+z;  T状态
        printf("raise function after\n");
        exit(0);
    }
}
 如果我们要求:子进程退出,父进程没有退出,同时也不要子进程处于Z(zobile) 状态,而是完全结束,怎么办?
 
 2.exit()--------库函数   进程的退出函数
 
 头文件:#include “stdlib.h”
 
 参数:  int 退出状态----- 假设:0 为正常退出,-1为非正常退出
 
 exit中的参数退出状态主要作用是传递给父进程,父进程根据这些状态可判定子进程现在处于什么情况。父进程怎样接收到这个状态呢?是通过wait 或waitpid
 
 exit()------系统调用函数----unistd.h
以下为exit()和_exit()基本使用
 
 
以下为exit()和_exit()基本使用
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
int main()
{
  printf("hello linux");
 // fflush(stdout);
  //_exit(0);
  exit(0);
  printf("1111111111111");
  return 0;
}
 3  wait()函数
 
 所需头文件:
 #include <sys/types.h>
 
           #include <sys/wait.h>
 
 函数原型:
   pid_t wait(int *status)
 
 函数参数:
   
 status 是一个整型指针,指向的对象用来保存子进程退出时的状态。
 
                        status 若为空,表示忽略子进程退出时的状态
 
                        status 若不为空,表示保存子进程退出时的状态
 
 另外,子进程的结束状态可由Linux中一些特定的宏来测定。
 
 函数返回值:    成功:子进程的进程号  失败:-1
 
 什么时候会阻塞,什么时候不会阻塞?
 
 调用该函数使进程阻塞(睡眠),直到任一个子进程结束或者是该进程接收到了一个信号为止。
 
 如果该进程没有子进程或者其子进程已经结束,wait函数会立即返回,即不会阻塞。
以下是关于wait函数的使用
 
 
以下是关于wait函数的使用
#include "sys/wait.h"
#include "sys/types.h"
#include "stdio.h"
int main()
{
  printf("wait before\n");
  wait(NULL);
  printf("wait after\n");
  return 0;
}#include "sys/types.h"
#include "stdio.h"
#include "unistd.h"
int main()
{
  pid_t pid;
  pid=fork();
  if(pid <0 )
  {
     printf("creat process failure\n");
     return -1;
  }
  if(pid ==0)//child process
  {
   printf("this is child process\n") ;
   while(1);
  }
  if(pid >0) //parent process
  {
    printf("this is parent process\n");
      printf("parent process:wait before\n");
      wait(NULL);
      printf("parent process:wait after\n");
      while(1);
  }
  return 0;
}
    子进程1: 
 
         printf     ------- pid
 
         sleep(10)  -------S                   10秒后   结束
 
         exit(1)
 
    子进程2: 
 
         printf     ------- pid
 
         sleep(20)  -------S                   S(睡眠状态)                    再过10秒   
 
         exit(3)
 
    父进程:
 
         printf---
 
         printf ---wait before
 
         pet=wait------------------S
 
         printf---wait after ret=%d               
 
         pet=wait                              S(睡眠状态)                         R(运行状态)
 
         printf  second wait ret=%d
 
#include "sys/types.h"
#include "sys/wait.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
  pid_t pid;
  int ret;
  pid = fork();
  if(pid <0)
  {
    printf("creat process failure\n");
    return -1;
  }
  if(pid == 0) //child process1
  {
    printf("this is a child process,pid=%d\n",getpid());
    sleep(10);
    exit(1);
  }
  if(pid >0)
  {
    pid_t  pid1;
    pid1=fork();
    if(pid1<0)
    {
      printf("creat child process1 failure\n");
      exit(2);
    }
    if(pid1 ==0)
    {
      printf("this is a child process1 run,pid=%d\n",getpid());
      sleep(20);
      exit(3);
    }
    printf("this is a parent process\n");
    printf("wait before\n");
    ret=wait(NULL);
    printf("wait after,first wait ret=%d\n",ret);
    ret=wait(NULL);
    printf("second wait after,ret=%d\n",ret);
    while(1);
    return 0;
  }
 4. waitpid()   功能比wait 功能更强
 
 可以等待某一个子进程退出,可通过第一个参数来实现
 
 所需头文件:#include <sys/types.h>
 
          #include <sys/wait.h>
 
 函数原型: pid_t waitpid(pid_t pid, int *status, int options)
 
 函数参数: pid    pid>0:只等待进程ID等于pid的子进程,不管已经有其他子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
 
           pid=-1:等待任何一个子进程退出,此时和wait作用一样。
 
           pid=0:等待其组ID等于调用进程的组ID的任一子进程。
 
           pid<-1:等待其组ID等于pid的绝对值的任一子进程。
 
           status   
 同wait
 
           options   WNOHANG:若由pid指定的子进程并不立即可用,则waitpid不阻塞,此时返回值为0
 
          WUNTRACED:若某实现支持作业控制,则由pid指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态。
 
           0:同wait,阻塞父进程,等待子进程退出。
 
 函数返回值:   正常:结束的子进程的进程号
 
          使用选项WNOHANG且没有子进程结束时:0
 
          调用出错:-1
例子:
 
 
例子:
#include "sys/types.h"
#include "sys/wait.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
  pid_t pid;
  int ret;
  int status;
  pid = fork();
  if(pid <0)
  {
    printf("creat process failure\n");
    return -1;
  }
  if(pid == 0) //child process1
  {
    printf("this is a child process,pid=%d\n",getpid());
    sleep(10);
    exit(1);
  }
  if(pid >0)
  {
    pid_t  pid1;
    pid1=fork();
    if(pid1<0)
    {
      printf("creat child process1 failure\n");
      exit(2);
    }
    if(pid1 ==0)
    {
      printf("this is a child process1 run,pid=%d\n",getpid());
      sleep(20);
      exit(3);
    }
    printf("this is a parent process\n");
    printf("wait before\n");
    ret=waitpid(pid1,&status,0);
    printf("wait after,first wait ret=%d,status=%d\n",ret,WEXITSTATUS(status));
//    ret=wait(&status);
//    printf("second wait after,ret=%d,status=%d\n",ret,WEXITSTATUS(status));
    while(1);
    return 0;
  }
}
 5  vfork()    ----形式和fork()
 
 vfork() 调用成功,内核则在用户空间创建一个进程,但是不会重新建立一个房子,父子里程共享父进程这个房子,然后子进程先运行,父进程后运行
 
 6.在此之前创建子进程,子进程进行了代码的完全拷贝,虽然我们用返回值的不同使得父子进程执行了二个不同的代码区。但是很多情况下:
 
       父子进程的代码完全不一样,比如 bash 这是父进程,a.out 是子进程,这二段代码没有相似性;或父进程是C代码,子进程是一个shell脚本,等,我们应该怎么处理?
 
        当进程认为自己不能再为系统和用户做出任何贡献了时就可以调用exec函数,让自己执行新的程序,我们应该怎么处理?
伪代码:
 
 
伪代码:
 int main()                           computer 这个可执行程
 
 {                                     
 
 
   printf(“dfdfggf\n”);
 
   i++;
 
   .....
 
   Computer//不是函数,而是另外一个程序
 
 }
 
 可以通过exec 函数族来解决这些问题。
 
 函数的功能:可以调用另外一个程序来运行,会把原来的程序中的代码段,数据段等都替换掉,从这个调用的程序的开始来执行,会保留原进程的PID,其它都不保留。
 
 什么时候要用这个函数呢?
 
       当进程认为自己不能再为系统和用户做出任何贡献了时就可以调用exec函数,让自己执行新的任务。
 
 如果某个进程想同时执行另一个程序,它就可以调用fork函数创建子进程,然后在子进程中调用任何一个exec函数。这样看起来就好像通过执行应用程序而产生了一个新进程一样。
 
 所需头文件:#include <unistd.h>
 
 函数原型:   int execl(const char *path, const char *arg, ...);
 
                    int execv(const char *path, char *const argv[]);
 
                    int execle(const char *path, const char *arg, ..., char *const envp[]);
 
                    int execve(const char *path, char *const argv[], char *const envp[]:xp[]);
 
                    int execlp(const char *file, const char *arg, ...);
 
                    int execvp(const char *file, char *const argv[]);//环境变量加
 
 函数返回值:    -1:出错,成功是0
 
 参数:        第一个参数,前面四个都一样,后面二个一样,path  file
 
                    path 必须要指定所在程序的路径,要不是绝对路径或相对路径
 
                    file  不用指定路径(指定路径也可以),在环境变量PATH中找这个程序
 
 char * 逐个列举
 
 例如这样一个程序
 
 ls  –l  /mnt   ./child/xxx          “./child/xxx”  NULL
 
 怎样传递这个参数呢? 
 
 “ls” “-l” “/mnt”  NULL   
 
 采用字符指针数组的方式传递参数
 
 char *argv[]={“ls” “-l” “/mnt”  NULL }
 
 第一个参数:有二类,char *path—不仅要指定程序的名称也要指定程序的路径 
 
 char *file----指定程序的名称,你这个程序的所在路径一定要在环境变量位PATH中找到
 
 第二参数:  char *arg ……   char *argv[]
execl例子
 
 
execl例子
#include "stdio.h"
#include "unistd.h"
int main()
{
  printf("exec before\n");
  execl("./xxx","./xxx",NULL);
  printf("exec after\n");
  return 0;
}#include "stdio.h"
#include "unistd.h"
int main()
{
  char *buf[]={"./child/xxx",NULL};
  printf("exec before\n");
  execv("./child/xxx",buf);
  printf("exec after\n");
  return 0;
}#include "stdio.h"
#include "unistd.h"
int main()
{
  char *buf[]={"xxx",NULL};
  printf("exec before\n");
  execvp("xxx",buf);
  printf("exec after\n");
  return 0;
}
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号