UNIX FIFO:如何允许只有一个写入者/读取者对FIFO进行使用?

3
我写了两个程序:第一个是“写入器”,它创建一个FIFO并将数据写入其中。第二个程序是“读取器”,在后台运行并查找FIFO中的数据。一旦有数据,读取器就会将其读出。
例如,如果我启动了两个写入器和两个读取器,他们都可以写入/读取相同的FIFO。如何限制第3个和第4个读取器/写入器使用FIFO,并只允许一个写入器和一个读取器使用FIFO?
我的代码:
FIFO写入器:
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <fcntl.h>

#define BUFFERSIZE 50
#define CHMOD 0777



int main(int argc, char **argv)     
{  
   char outbuf[BUFFERSIZE]; // outbuffer
   int fifo, j, anzahl;
   // fifo - pipe file deskriptor, j - counter, anzahl - Parameter.

   if(argc!=2)               // Check if parameter is ok
   {                            
       printf("Ungültiger Parameter! Bsp.: ./fifow 10\n");  
       return 1;
   }

   anzahl=atoi(argv[1]);        // convert paramter to integer


   mkfifo("namedpipe4", CHMOD);         // make FIFO "namedpipe4"
   fifo = open("namedpipe4",O_WRONLY);      // open FIFO
   //
   for(j=0;j<anzahl;j++)
     {   

         printf("Writer PID: %d writes record nr. %6d\n", getpid(), j+1);
         sprintf(outbuf, "Writer PID: %d writes record nr. %6d\n", getpid(), j+1); 
         write(fifo, outbuf, BUFFERSIZE);       
         remove("namedpipe4");  // removing the fifo
         sleep(1);              // Wait 1 sec
     }

   close(fifo);  // 

   exit(0);

}

FIFO读取器:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>

#define BUFFERSIZE 50

int main(void)      
{  

   char inbuf[BUFFERSIZE];  // inbuffer
   int fifo, var;

   printf("\n Waiting for a Pipe....\n");

   while((fifo = open("namedpipe4",O_RDONLY)) == -1) // while "there is no such pipe"
   {
    remove("namedpipe4");
    sleep(1);
   }    


   while((var = read(fifo, inbuf, BUFFERSIZE)) > 0) // while "i can read"
   {    
     printf("Reader PID: %d reads  record: %s\n", getpid(), inbuf);
     sleep(1);
   }



   close(fifo);     // 

   printf("\n EOF..\n");

   exit(0);


}
2个回答

4
鉴于您在另一篇答案中发布的代码,这里是一个修改后的版本,它修复了您遇到的问题。请查看注释以获取详细信息,但简言之:
  • 写入程序会检查mkfifo的返回值,以查看是否已经有其他写入程序创建了管道。
  • 读取程序打开管道后使用flock获取独占的advisory lock,以避免第二个读取程序在第一个读取程序删除管道之前打开管道的竞争条件。

写入程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>   /* needed for mkfifo */
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>

#define BUFFERSIZE 50
#define CHMOD 0777

int
main (int argc, char **argv)     
{  
    char outbuf[BUFFERSIZE];
    int fifo, j, anzahl;

    if (argc != 2)
    {                            
        printf("Ungültiger Parameter! Bsp.: ./fifow 10\n");  
        return 1;
    }

    anzahl=atoi(argv[1]);

    /* mkfifo fails if the file already exists, which means there's a
     * writer waiting for a reader.  This assures that only one writer
     * will write to the pipe, since it only opens the pipe if it was
     * the one who created it.
     */
    if (mkfifo("namedpipe4", CHMOD) == -1)
    {
        printf("namedpipe4 already exists\n");
        return 1;
    }

    fifo = open("namedpipe4", O_WRONLY);

    for (j = 0; j < anzahl; j++)
    {   
        printf("Writer PID: %d writes record nr. %6d\n", getpid(), j + 1);
        sprintf(outbuf, "Writer PID: %d writes record nr. %6d\n", getpid(), j + 1); 
        write(fifo, outbuf, BUFFERSIZE);       
        remove("namedpipe4");
        sleep(1);
    }

    close(fifo);

    exit(0);
}

读者:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>   /* for flock */
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>

#define BUFFERSIZE 50

int
main (int argc, char **argv)      
{  
    char inbuf[BUFFERSIZE];
    int fifo, var;

    printf("\n Waiting for a Pipe....\n");

    /* There are *two* ways the open can fail: the pipe doesn't exist
     * yet, *or* it succeeded, but a different writer already opened
     * it but didn't yet remove it.
     */
    while (1)
    {
        while ((fifo = open("namedpipe4", O_RDONLY)) == -1)
        {
            /* Since you didn't specify O_CREAT in the call to open, there
             * is no way that namedpipe4 would have been created by the
             * reader.  If there *is* now a namedpipe4, a remove here
             * would delete the one the writer created!
             */
            sleep(1);
        }    

        /* Get an exclusive lock on the file, failing if we can't get
         * it immediately.  Only one reader will succeed.
         */
        if (flock (fifo, LOCK_EX | LOCK_NB) == 0)
            break;

        /* We lost the race to another reader.  Give up and wait for
         * the next writer.
         */
        close (fifo);
    }

    /* We are definitely the only reader.
     */

    /* *Here* we delete the pipe, now that we've locked it and thus
     * know that we "own" the pipe.  If we delete before locking,
     * there's a race where after we opened the pipe, a different
     * reader also opened, deleted, and locked the file, and a new
     * writer created a new pipe; in that case, we'd be deleting the
     * wrong pipe.
     */
    remove("namedpipe4");

    while ((var = read(fifo, inbuf, BUFFERSIZE)) > 0)
    {    
        printf("Reader PID: %d reads  record: %s\n", getpid(), inbuf);
        /* No need to sleep; we'll consume input as it becomes
         * available.
         */
    }

    close(fifo);
    printf("\n EOF..\n");
    exit(0);
}

1
使用pipe(2)创建FIFO,并且只在从父进程分叉出适当的进程时,为每个FIFO端点提供文件描述符。(或者让读取器调用pipe(2)并分叉写入器,反之亦然。)由于FIFO永远不会存在于文件系统中,因此任何其他进程都无法访问它。
如果必须使用命名FIFO,请在读取器和写入器打开它后删除FIFO。底层FIFO仍将存在,只要读取器和写入器将其保持打开状态,但是没有新进程将能够打开它。但是,在您删除它之前,可能会出现第二个读取器或写入器打开FIFO的竞争条件。

非常感谢,保罗!看起来它运行正常。我必须使用命名管道(FIFO),所以我只是在读取程序中的write()语句后面添加了一个remove("namedpipe")。现在,如果我启动2个写入程序和2个读取程序,只有1个写入程序可以写入,只有1个读取程序可以读取。 - Max Krug
一个以上的写入者可以访问该文件,但只有一个读取者可以访问。 - Max Krug
如果在读取器打开FIFO后删除它,就没有任何阻止另一个写入器使用相同名称创建新的FIFO的方法。如果您仍然遇到问题,请发布代码,以防还有其他问题发生。 - Paul Kuliniewicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接