【问题标题】:Named pipe file descriptor命名管道文件描述符
【发布时间】:2016-07-03 13:02:40
【问题描述】:

目前我正在为 Linux 操作系统制作 C/C++ 程序。 我想使用命名管道在两个程序之间传递 PID(进程 ID)。 管道已创建并在目录中可见。

Get PID 程序说文件描述符返回 3,如果它可以打开管道,它应该返回 0。我做错了什么?

获取 PID

// Several includes
using namespace std;

int main(int argc, char *argv[]) {
  pid_t pid;
  int sig = 22;
  int succesKill;
  int iFIFO;
  char sPID[5] = {0,1,2,3,'\0'};
  iFIFO = open("IDpipe" , O_RDONLY);
  if(iFIFO != 0)
    {
    cerr << "File descriptor does not return 0, but: " <<  iFIFO << endl;
    return EXIT_FAILURE;
    } 
  read(iFIFO, sPID, strlen(sPID));
  cerr << "In sPID now is: " << sPID << endl;
  close(iFIFO);
  pid = atoi(sPID);
  cout << "The PID I will send signals to is: " << pid << "." << endl;
  while(1)
    {
    succesKill = kill(pid, sig);
    cout << "Tried to send signal" << endl;
    sleep(5);
    }
  return EXIT_SUCCESS;
}

发送 PID

// Several includes
using namespace std;

void catch_function(int signo);

volatile sig_atomic_t iAmountSignals = 0;

int main(void) {
    pid_t myPID;
    int iFIFO;
    char sPID[5] = {'l','e','e','g','\0'};
    myPID = getpid();
    sprintf(sPID, "%d",myPID);
    cout << "My PID is: " << sPID << endl;
    iFIFO = open("IDpipe" , O_WRONLY);
    if(iFIFO == -1)
      {
      cerr << "Pipe can't be opened for writing, error: " << errno << endl;
      return EXIT_FAILURE;
      }
    write(iFIFO, sPID, strlen(sPID));
    close(iFIFO);
    if (signal(22, catch_function) == SIG_ERR) {
        cerr << "An error occurred while setting a signal handler." << endl;
        return EXIT_FAILURE;
    }
    cout << "Raising the interactive attention signal." << endl;
    if (raise(22) != 0) {
        cerr << "Error raising the signal." << endl;
        return EXIT_FAILURE;
    }
    while(1)
    {
        cout << "iAmountSignals is: " << iAmountSignals << endl;
        sleep(1);
    }
    cout << "Exit." << endl;
    return EXIT_SUCCESS;
}

void catch_function(int signo) {
    switch(signo) {
    case 22:
        cout << "Caught a signal 22" << endl;
        if(iAmountSignals == 9)
          {iAmountSignals = 0;}
        else
          {++iAmountSignals;}
        break;
    default:
        cerr << "Thats the wrong signal.." << endl;
        break;
    }
}

终端输出

Output

【问题讨论】:

    标签: c++ linux named-pipes


    【解决方案1】:

    open() 返回新创建的文件描述符。它不能返回 0,原因很简单,新进程已经有一个文件描述符 0。那将是标准输入。

    返回值 3 是来自open() 的预期结果,在这种情况下,因为这将是标准输入、输出和错误之后的下一个可用文件描述符。如果open() 无法打开文件描述符,它将返回-1。

    但除此之外,您的代码还存在许多其他错误:

    sprintf(sPID, "%d",myPID);
    
    // ...
    
    write(iFIFO, sPID, strlen(sPID));
    

    如果您的进程 ID 恰好只有 3 位数长(这是可能的),这会将三个字节写入管道。

    如果您的进程 ID 恰好是 5 位数长(这更有可能),这将写入 5 个字节加上 '\0' 字节,总共写入 6 个字节到 5 个字节长的sPID缓冲区,超出数组并导致未定义的行为。

    当然,实际结果是未定义的,但典型的 C++ 实现最终会破坏堆栈中下一个变量的第一个字节,即:

    int iFIFO;
    

    这是你的文件描述符。所以,如果你的运气用完了并且你的新进程获得了一个五位数的进程 ID,并且这是一个 little-endian C++ 实现,没有填充,那么iFIFO 的低位字节被设置为 0,如果代码在没有任何优化的情况下编译,iFIFO 文件描述符被设置为 0。Hillarity 随之而来。

    此外,在管道的另一边:

    char sPID[5] = {0,1,2,3,'\0'};
    
    // ...
    
    read(iFIFO, sPID, strlen(sPID));
    

    因为SPID 的第一个字节总是设置为0,所以这将总是执行read(iFIFO, sPID, 0),并且不读取任何内容。

    之后:

    pid = atoi(sPID);
    

    atoi() 需要一个以 '\0' 结尾的字符串。 read() 只读取它读取的任何内容,它不会'\0' - 终止它最终读取的任何内容。在使用 atoi() 之前,您有责任放置一个 '\0' 来终止读取输入(当然,还要确保读取缓冲区足够大)。

    【讨论】:

      【解决方案2】:

      您的逻辑似乎不正确。

      if(iFIFO != 0)
      

      应该是

      if(iFIFO == -1)
      

      因为open 出错时返回-1。否则返回一个有效的文件描述符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多