为什么这里的程序举例说明在LINUX中使用命名管道-FIFO会遇到竞争条件?

时间:2014-02-01 13:07:39

标签: c++ linux posix named-pipes

我有两个程序 - 一个程序写入管道,另一个程序从管道读取。但是,当我运行它们时,错过了这些消息。

编写程序如下 -

执行时的写作过程 -

$ ./pipe.out 
Write to Buffer = 0

执行时的阅读过程 -

$ ./pipe_reader.out 
Pipe already exists
Read from Buffer
0
Read from Buffer
7
Read from Buffer
7
Read from Buffer

你有没有看到这里的消息丢失了?

这是程序,请解释一下这里有什么问题?如果我不这样做,同样的工作 关闭文件描述符。事实上,继续打开它。我相信这会导致其他一些问题。在这里,它起作用,因为fd打开只有10。

计划 - 班级

#include "iostream"
#include "unistd.h"
#include "stdio.h"
#include "fcntl.h"
#include "string.h"
#include <sys/stat.h>
#include <sys/types.h>


using namespace std;


class Named_Pipe
{

private:

int m_fp;
char m_name[256];
int m_rd;
int m_wr;

public:

void open_pipe(char *name);
void close_reader();
void close_writer();
void write_to_pipe(char *msg);
void read_from_pipe(char *msg);
Named_Pipe();

};

Named_Pipe::Named_Pipe()
{

  /** no access to any groups and others **/
  umask(077);

}

void Named_Pipe::open_pipe(char *name)
{ 
    int rc;
    struct stat pipe_exist;

    strcpy(m_name,name);    

    if ( stat(name, &pipe_exist) == 0 ) 
    {

      cout<<"Pipe already exists"<<endl;    
    }

    else
    {

    rc = mkfifo(name, S_IRWXU); 

     if( rc == -1)
     {
      cout<<strerror(rc);   

     }
    }   

}

void Named_Pipe::write_to_pipe(char *msg)
{
    int rc = 0;

    m_wr = open(m_name,O_WRONLY);

    if( m_wr == -1)
    {
      cout<<"Error in opening the descriptor for writing"<<endl;    
    }

    rc = write(m_wr,msg,256);

    if( rc == -1)
    cout<<"Error in writing the message"<<endl;

}

void Named_Pipe::read_from_pipe(char *msg)
{
    int rc = 0;

    m_rd = open(m_name,O_RDONLY);

    if( m_rd == -1)
    {
      cout<<"Error in opening the descriptor for reading"<<endl;    
    }

    rc = read(m_rd,msg,256);

    if( rc == -1)
    cout<<"Error in reading the message"<<endl;

}

void Named_Pipe:: close_reader()
{
   close(m_rd); 

}

void Named_Pipe:: close_writer()
{
   close(m_wr); 

}

现在,编写器管道进程逻辑 -

pipe.cpp

int main(int argc, char *argv[])
{
  Named_Pipe write_process;
  char buffer[256];
  int i = 0;

  write_process.open_pipe("FIRST_FIFO_PROG");

  for( i= 0; i<10; i++)
  {

   strcpy(buffer,"MY FIRST MSG ON PIPES");

   cout<<"Write to Buffer = "<< i<< endl;
   sprintf(buffer,"%d",i);

   write_process.write_to_pipe(buffer);
   write_process.close_writer();


  }




  return 0; 
}

现在,这里的读者管道流程。

int main(int argc, char *argv[])
{
  Named_Pipe read_process;
  char buffer[256];
  int i = 0;

  read_process.open_pipe("FIRST_FIFO_PROG");

  for( i= 0; i<10; i++)
  {

   cout<<"Read from Buffer"<<endl;

   read_process.read_from_pipe(buffer);
   cout<<buffer<<endl;
   read_process.close_reader();   

  }

  return 0; 
}

3 个答案:

答案 0 :(得分:3)

您可以在读取和写入两侧保持打开和关闭FIFO。您只需打开一次,写入(并读取)您的消息,然后关闭FIFO。

你所看到的并不是一个竞争条件,而是你自己制作的计时问题。在相应的开放调用成功之前,FIFO需要读写器,而FI​​FO可以有多个读写器。

我看到的是以下内容的变体:

  1. 读者&amp;作家开放
  2. 作家写道
  3. 作家关闭
  4. 步骤2&amp; 3循环x次之前......
  5. 读者阅读单个消息
  6. 读取器关闭,其余消息丢失。
  7. 你也可以写一个固定的256字节,你可能想写strlen字节。而且,和往常一样,阅读&amp;写一个循环。

答案 1 :(得分:1)

请不要接受此答案 - 接受Duckanswer


分析

您的类成员函数操作与其名称不匹配。

  • 构造函数只是设置umask()而不执行任何其他操作。它甚至没有将文件描述符设置为已知状态。
  • 如果没有该名称的文件,open_pipe()函数会创建FIFO。
  • write_to_pipe()函数每次调用时都会打开文件并写入管道而不关闭它。
  • read_from_pipe()函数每次调用时都会打开文件,并从管道读取而不关闭它。
  • 无论是否已执行相应的读取或写入,close_reader()close_writer()函数都会关闭文件描述符。
  • 没有析构函数。
  • 从不使用该类的m_fp成员变量。

尽管有一连串的问题,代码表面上应该可以工作 - 除了当所有文件描述符都关闭时FIFO会丢弃任何写入的数据。 您从comment Duck获得了此关键信息。

请注意,如果您的代码在调用close之前执行了多次读取或写入操作,那么您可能会大量泄漏资源。

这是一个重写 - 处理一些设计问题。

pipe.h

#ifndef PIPE_H_INCLUDED
#define PIPE_H_INCLUDED

class Named_Pipe
{
private:
    char m_name[256];
    int m_rd;
    int m_wr;
    bool m_mk;      // FIFO created?
    void mkpipe(char const *name);

public:
    void open_reader(const char *name);
    void open_writer(const char *name);
    void close_reader();
    void close_writer();
    void write_to_pipe(const char *msg);
    int read_from_pipe(char *msg, int maxlen);

    Named_Pipe();
    ~Named_Pipe();
};

#endif // PIPE_H_INCLUDED

pipe.cpp

#include "pipe.h"

#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <unistd.h>

using namespace std;

Named_Pipe::Named_Pipe() : m_rd(-1), m_wr(-1), m_mk(false)
{
    m_name[0] = '\0';
    umask(077);
}

Named_Pipe::~Named_Pipe()
{
    close_reader();
    close_writer();
    unlink(m_name);
}

void Named_Pipe::mkpipe(char const *name)
{
    struct stat pipe_exist;

    strcpy(m_name, name);

    if (stat(name, &pipe_exist) != 0)
    {
        if (mkfifo(name, S_IRWXU) != 0)
        {
            cerr << strerror(errno);
            exit(1);
        }
    }
    m_mk = true;
}

void Named_Pipe::open_reader(char const *name)
{
    if (!m_mk)
        mkpipe(name);
    m_rd = open(m_name, O_RDONLY);
    if (m_rd == -1)
    {
        cerr << "Error in opening " << name << " for reading" << endl;
        exit(1);
    }
}

void Named_Pipe::open_writer(char const *name)
{
    if (!m_mk)
        mkpipe(name);
    m_wr = open(m_name, O_WRONLY);
    if (m_wr == -1)
    {
        cerr << "Error in opening FIFO " << name << " for writing" << endl;
        exit(1);
    }
}

void Named_Pipe::write_to_pipe(char const *msg)
{
    if (m_wr == -1)
    {
        cerr << "Writing to unopened FIFO\n";
        exit(1);
    }
    int len = strlen(msg) + 1;
    if (write(m_wr, msg, len) != len)
    {
        cerr << "Error in writing the message" << endl;
        exit(1);
    }
}

int Named_Pipe::read_from_pipe(char *msg, int msglen)
{
    if (m_rd == -1)
    {
        cerr << "Reading from unopened FIFO\n";
        exit(1);
    }
    int rc = read(m_rd, msg, msglen - 1);
    if (rc == -1)
        cerr << "Error in reading the message" << endl;
    else if (rc == 0)
        cerr << "EOF on pipe" << endl;
    else if (msg[rc-1] != '\0')
        msg[rc] = '\0';
    cerr << "Read " << rc << " bytes from FIFO\n";
    return rc;
}

void Named_Pipe::close_reader()
{
    if (m_rd != -1)
    {
        close(m_rd);
        m_rd = -1;
    }
}

void Named_Pipe::close_writer()
{
    if (m_wr != -1)
    {
        close(m_wr);
        m_wr = -1;
    }
}

管reader.cpp

#include "pipe.h"
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    Named_Pipe read_process;
    char buffer[256];
    int i = 0;

    read_process.open_reader("FIRST_FIFO_PROG");

    for (i = 0; i < 10; i++)
    {
        int nbytes = read_process.read_from_pipe(buffer, sizeof(buffer));
        const char *data = buffer;
        int counter = 0;
        while (nbytes > 0)
        {
            int len = strlen(data);
            cout << "Reader" << counter << ": [" << data << "]" << endl;
            nbytes -= len + 1;
            data += len + 1;
        }
    }

    read_process.close_reader();
    cout << "Reader complete\n";

    return 0;
}

管writer.cpp

#include "pipe.h"
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    Named_Pipe write_process;
    char buffer[256];

    write_process.open_writer("FIRST_FIFO_PROG");

    for (int i = 0; i < 10; i++)
    {
        sprintf(buffer, "Message on FIFO %d", i);
        cout << "Write to Buffer = [" << buffer << "]" << endl;
        write_process.write_to_pipe(buffer);
    }

    write_process.close_writer();
    cout << "Writer complete\n";

    return 0;
}

样本输出

示例1:

$ pipe-writer & sleep 1 ; pipe-reader
[1] 9576
Write to Buffer = [Message on FIFO 0]
Write to Buffer = [Message on FIFO 1]
Write to Buffer = [Message on FIFO 2]
Write to Buffer = [Message on FIFO 3]
Write to Buffer = [Message on FIFO 4]
Write to Buffer = [Message on FIFO 5]
Write to Buffer = [Message on FIFO 6]
Write to Buffer = [Message on FIFO 7]
Write to Buffer = [Message on FIFO 8]
Write to Buffer = [Message on FIFO 9]
Writer complete
Read 180 bytes from FIFO
Reader0: [Message on FIFO 0]
Reader1: [Message on FIFO 1]
Reader2: [Message on FIFO 2]
Reader3: [Message on FIFO 3]
Reader4: [Message on FIFO 4]
Reader5: [Message on FIFO 5]
Reader6: [Message on FIFO 6]
Reader7: [Message on FIFO 7]
Reader8: [Message on FIFO 8]
Reader9: [Message on FIFO 9]
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
Reader complete
[1]+  Done                    pipe-writer
$

示例2:

$ pipe-writer & pipe-reader
[1] 9579
Write to Buffer = [Message on FIFO 0]
Write to Buffer = [Message on FIFO 1]
Write to Buffer = [Message on FIFO 2]
Write to Buffer = [Message on FIFO 3]
Write to Buffer = [Message on FIFO 4]
Write to Buffer = [Message on FIFO 5]
Read Write to Buffer = [Message on FIFO 6]
Write to Buffer = [Message on FIFO 7]
Write to Buffer = [Message on FIFO 8]
Write to Buffer = [Message on FIFO 9]
36Writer complete
 bytes from FIFO
Reader0: [Message on FIFO 0]
Reader1: [Message on FIFO 1]
Read 144 bytes from FIFO
Reader0: [Message on FIFO 2]
Reader1: [Message on FIFO 3]
Reader2: [Message on FIFO 4]
Reader3: [Message on FIFO 5]
Reader4: [Message on FIFO 6]
Reader5: [Message on FIFO 7]
Reader6: [Message on FIFO 8]
Reader7: [Message on FIFO 9]
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
Reader complete
[1]+  Done                    pipe-writer
$

现在你可以看到为什么读者主程序中有额外的循环。


我仍然不认为这是一个好的设计。类中应该只有一个文件描述符。应将FIFO的名称传递给构造函数,并指示这是用于读取还是写入。 FIFO的名称在两个程序中复制;应该使用通用名称。我保留了close_xxxxer()函数,但最好让析构函数完成这项工作。您可以将函数名称简化为read()write()。代码错误检查但写入cerr(不是cout)并在出错时退出。请注意,返回代码-1与从errno获得的错误号不同。

答案 2 :(得分:0)

#include "iostream"
#include "unistd.h"
#include "stdio.h"
#include "fcntl.h"
#include "string.h"
#include <sys/stat.h>
#include <sys/types.h>


using namespace std;


class Named_Pipe
{

private:

int m_fp;
char m_name[256];
int m_rd;
int m_wr;

public:

void open_pipe(char *name);
void close_reader();
void close_writer();
void write_to_pipe(char *msg);
void read_from_pipe(char *msg);
Named_Pipe(int read_desc, int write_desc);
~Named_Pipe();

};

Named_Pipe::Named_Pipe(int read_desc, int write_desc):m_rd(read_desc), m_wr(write_desc)
{

  /** no access to any groups and others **/

  umask(077);

}

Named_Pipe::~Named_Pipe()
{
   /** This is to remove the pipe create **/
   unlink(m_name);   
}

void Named_Pipe::open_pipe(char *name)
{ 
    int rc;
    struct stat pipe_exist;

    strcpy(m_name,name);    

    if ( stat(name, &pipe_exist) == 0 ) 
    {

      cout<<"Pipe already exists"<<endl;    
    }

    else
    {

    rc = mkfifo(name, S_IRWXU); 

     if( rc == -1)
     {
      cout<<strerror(rc);   

     }
    }   

}

void Named_Pipe::write_to_pipe(char *msg)
{
    int rc = 0;

    if ( m_wr == -1)
    {

      m_wr = open(m_name,O_WRONLY);

      if( m_wr == -1)
      {
      cout<<"Error in opening the descriptor for writing"<<endl;    
      }

    }

    rc = write(m_wr,msg,256);

    if( rc == -1)
    cout<<"Error in writing the message"<<endl;

}

void Named_Pipe::read_from_pipe(char *msg)
{
    int rc = 0;

    if( m_rd != 0)
    {
     m_rd = open(m_name,O_RDONLY);

     if( m_rd == -1)
     {
      cout<<"Error in opening the descriptor for reading"<<endl;    
     }

    }

    rc = read(m_rd,msg,256);

    if( rc == -1)
    cout<<"Error in reading the message"<<endl;

}

void Named_Pipe:: close_reader()
{
   close(m_rd); 

}

void Named_Pipe:: close_writer()
{
   close(m_wr); 
}

现在,作家和读者分别处理。

int main(int argc, char *argv[])
{
  /* reader and writer , -1 un-initialized and 0 means intialized **/   
  Named_Pipe write_process(0,-1);
  char buffer[256];
  int i = 0;

  write_process.open_pipe("FIRST_FIFO_PROG");

  for( i= 0; i<10; i++)
  {

   sprintf(buffer,"%s %d","MY FIRST MSG ON PIPES",i);
   cout<<"Write Buffer = "<< buffer<< endl;
   write_process.write_to_pipe(buffer);

  }

   write_process.close_writer();

   return 0;    
}

读者流程 -

int main(int argc, char *argv[])
{
  /* reader and writer , -1 un-initialized and 0 means intialized **/   
  Named_Pipe read_process(-1,0);
  char buffer[256];
  int i = 0;

  read_process.open_pipe("FIRST_FIFO_PROG");

  for( i= 0; i<10; i++)
  {

   read_process.read_from_pipe(buffer);
   cout<<"Read Buffer = "<< buffer<< endl;

  }

  read_process.close_reader();   

  return 0; 
}