實現c與shell間的雙向通信

實現c與shell間的雙向通信

管道是應用較爲廣泛的進程間通信的手段,一般來講,管道是單向的,一個進程負責向管道里寫內容,另一個進程負責向管道里讀內容。於是我利用了兩個管道來實現雙向通行。

在這裏插入圖片描述

  • 具體實現

    • c_conn_shell.h

      //
      // Created by fengjun on 18-9-22.
      //
      
      #ifndef STUDYNET_C_CONN_SHELL_H
      #define STUDYNET_C_CONN_SHELL_H
      
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <sys/wait.h>
      #include <errno.h>
      #include <fcntl.h>
      
      //存放讀寫通道句柄與子進程(shell程序)之間的關係
      static pid_t	*ccsChildpid = NULL;
      
      //shell命令的路經
      #define	CCS_SHELL "/bin/sh"
      //能存放的最大句柄數
      #define CCS_MAX_FD 100
      
      /**
       * @brief 執行shell命令,並返回shell程序的的讀寫句柄
       * @param cmdstring
       * @return 存有兩個文件描述符的數組,第一個用於從shell進程讀出數據,第二個用於向shell進程寫入數據
       */
      int* c_conn_shell(const char *cmdstring);
      
      /**
       * @brief 關閉c與shell程序之間通信的通道
       * @param fd
       * @return 返回子進程終止時的狀態
       */
      int c_conn_shell_close(int* fd);
      
      #endif //STUDYNET_C_CONN_SHELL_H
      
    • c_conn_shell.h

      //
      // Created by fengjun on 18-9-22.
      //
      
      #include "c_conn_shell.h"
      
      int* c_conn_shell(const char *cmdstring)
      {
          int		pfdRead[2],pfdWrite[2];
          pid_t	pid;
      
          //第一次進入該函數
          if (ccsChildpid == NULL) {
              //爲數組分配內存,並清0
              if ( (ccsChildpid = calloc(CCS_MAX_FD, sizeof(pid_t))) == NULL)
                  return(NULL);
          }
      
          if (pipe(pfdRead) < 0 )
              //pipe()會爲errno賦值
              return(NULL);
          else if(pipe(pfdWrite) <0){
              //關閉pfdRead管道,並返回NULL
              close(pfdRead[0]);
              close(pfdRead[1]);
              return(NULL);
          }
      
          if ( (pid = fork()) < 0)
              //fork()會爲errno賦值
              return(NULL);
          else if (pid == 0) { //子進程
              close(pfdRead[0]);
              if (pfdRead[1] != STDOUT_FILENO) {
              	//重定向輸出
                  dup2(pfdRead[1], STDOUT_FILENO);
                  close(pfdRead[1]);
              }
              close(pfdWrite[1]);
              if (pfdWrite[0] != STDIN_FILENO) {
              	//重定向輸入
                  dup2(pfdWrite[0], STDIN_FILENO);
                  close(pfdWrite[0]);
              }
      
              //關閉在ccsChildpid[]中的所有文件描述符
              for (int i = 0; i < CCS_MAX_FD; i++)
                  if (ccsChildpid[ i ] > 0)
                      close(i);
      
              execl(CCS_SHELL, "sh", "-c", cmdstring, (char *) 0);
              _exit(127);
          }
          //父進程
          int* result;
          if ( (result = calloc(2, sizeof(int))) == NULL)
              return(NULL);
      
          close(pfdRead[1]);
          //記錄文件描述符的父進程
          ccsChildpid[pfdRead[0]] = pid;
          result[0]=pfdRead[0];
      
          close(pfdWrite[0]);
          //記錄文件描述符的父進程
          ccsChildpid[pfdWrite[1]] = pid;
          result[1]=pfdWrite[1];
          return(result);
      }
      
      int c_conn_shell_close(int* fd)
      {
      
          int		stat;
          pid_t	pid;
      
          if (ccsChildpid == NULL)
              //c_conn_shell()還未被調用過
              return(-1);
      
          if ( (pid = ccsChildpid[fd[0]]) == 0 || ccsChildpid[fd[1]] == 0)
              //文件描述符沒有被c_conn_shell()打開
              return(-1);
      
          ccsChildpid[fd[0]] = 0;
          ccsChildpid[fd[1]] = 0;
      
          if (close(fd[0]) == EOF || close(fd[1]) == EOF)
              return(-1);
      
          //由於fd是動態分配的,所以需要釋放內存
          free(fd);
      
          while (waitpid(pid, &stat, 0) < 0)
              //排除ENTER以外的錯誤
              if (errno != EINTR)
                  return(-1);	/*  */
                  
          return(stat);
      }
      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章