首頁  >  文章  >  系統教程  >  linux系統中的有名管道(FIFO)

linux系統中的有名管道(FIFO)

WBOY
WBOY轉載
2024-02-15 13:40:03535瀏覽

Linux系統是一種支援多任務並發執行的作業系統,它可以同時運行多個進程,從而提高系統的使用率和效率。但是,如果這些進程之間需要進行資料交換和協作,就需要使用一些進程間通訊(IPC)的方式,例如訊號、訊息佇列、共享記憶體、信號量等。其中,有名管道(FIFO)是一種比較簡單而強大的IPC方式,它可以讓兩個或多個進程透過一個檔案來進行資料傳輸,而無需關心檔案的內容和格式。本文將介紹linux系統中的有名管道(FIFO)的方法,包括有名管道的建立、開啟、讀寫、關閉和刪除等方面。

linux系統中的有名管道(FIFO)

#無名管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的進程間通信,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。 FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中。這樣,即使與FIFO的創建進程不存在親緣關係的進程,只要可以存取該路徑,就能夠彼此透過FIFO相互通信(能夠存取該路徑的進程以及FIFO的創建進程之間),因此,透過FIFO不相關的進程也能交換資料。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀取總是從開始處返回數據,對它們的寫則把數據添加到末尾。它們不支援諸如lseek()等檔案定位操作。
管道的緩衝區是有限的(管道製存在於記憶體中,在管道建立時,為緩衝區分配一個頁面大小)
管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好資料的格式,例如多少個位元組算作一個訊息(或命令、或記錄)等等

FIFO往往都是多個寫進程,一個讀進程。

FIFO的開啟規則:

  1. 如果目前開啟操作是為讀取而開啟FIFO時,若已經有對應程序為寫而開啟該FIFO,則目前開啟作業將會成功傳回;否則,可能阻塞直到有對應行程為寫而開啟該FIFO(目前開啟作業設定了阻塞標誌);或者,成功返回(目前開啟操作沒有設定阻塞標誌)。
  2. 如果目前開啟操作是為寫入而開啟FIFO時,如果已經有對應程序為讀取而開啟該FIFO,則目前開啟作業將成功傳回;否則,可能阻塞直到有對應行程為讀取而開啟該FIFO(目前開啟作業設定了阻塞標誌);或者,返回ENXIO錯誤(目前開啟操作沒有設定阻塞標誌)。

總之就是一句話,一旦設定了阻塞標誌,調用mkfifo建立好之後,那麼管道的兩端讀寫必須分別打開,有任何一方未打開,則在調用open的時候就阻塞。

從FIFO讀取資料:

約定:如果一個行程為了從FIFO讀取資料而阻塞開啟FIFO,那麼稱該行程內的讀取操作為設定了阻塞標誌的讀取操作。 (意思是我現在要打開一個有名管道來讀資料!)

如果有進程寫打開FIFO,且當前FIFO內沒有資料(可以理解為管道的兩端都建立好了,但是寫端還沒開始寫資料!)

  1. 則對於設定了阻塞標誌的讀取操作來說,將一直阻塞(就是block住了,等待資料。它並不消耗CPU資源,這種行程的同步方式對CPU而言是非常有效率的。)
  2. 對於沒有設定阻塞標誌讀取操作來說則回傳-1,目前errno值為EAGAIN,提醒以後再試。

對於設定了阻塞標誌的讀取操作說(見上面的約定)
造成阻塞的原因有兩種

  1. FIFO内有数据,但有其它进程在读这些数据
  2. FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。

读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样,此时,读操作返回0。

注:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。

向FIFO中写入数据:

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

对于设置了阻塞标志的写操作:

  1. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。(PIPE_BUF ==>> /usr/include/linux/limits.h)
  2. 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:

  1. 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
  2. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;

简单描述下上面设置了阻塞标志的逻辑
设置了阻塞标志

if (buf_to_write then
 if ( buf_to_write > system_buf_left ) //保证写入的原子性,要么一次性把buf_to_write全都写完,要么一个字节都不写!
 then
  block ;
  until ( buf_to_write else
  write ;
 fi
else
 write ; //不管怎样,就是不断写,知道把缓冲区写满了才阻塞
fi

管道写端 pipe_read.c

/pipe_read.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF

int main()
{
int pipe_fd;
int res;

int open_mode = O_RDONLY;
char buffer[BUFFER_SIZE + 1];
int bytes = 0;

memset(buffer, '\0', sizeof(buffer));

printf("Process %d opeining FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd);

if (pipe_fd != -1)
{
do{
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes += res;
printf("%d\n",bytes);
}while(res > 0);
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}

printf("Process %d finished, %d bytes read\n", getpid(), bytes);
exit(EXIT_SUCCESS);
}

管道读端 pipe_write.c

//pipe_write.c

#include   
#include   
#include   
#include   
#include   
#include   
#include   
  
#define FIFO_NAME "/tmp/my_fifo"  
#define BUFFER_SIZE PIPE_BUF  
#define TEN_MEG (1024 * 100)  
  
int main()  
{  
    int pipe_fd;  
    int res;  
    int open_mode = O_WRONLY;  
  
    int bytes = 0;  
    char buffer[BUFFER_SIZE + 1];  
  
    if (access(FIFO_NAME, F_OK) == -1)  
    {  
        res = mkfifo(FIFO_NAME, 0777);  
        if (res != 0)  
        {  
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);  
            exit(EXIT_FAILURE);  
        }  
    }  
  
    printf("Process %d opening FIFO O_WRONLY\n", getpid());  
    pipe_fd = open(FIFO_NAME, open_mode);  
    printf("Process %d result %d\n", getpid(), pipe_fd);  
  
   //sleep(20);
    if (pipe_fd != -1)  
    {  
        while (bytes if (res == -1)  
            {  
                fprintf(stderr, "Write error on pipe\n");  
                exit(EXIT_FAILURE);  
            }  
            bytes += res;  
        printf("%d\n",bytes);
        }  
        close(pipe_fd);  
    }  
    else  
    {  
        exit(EXIT_FAILURE);  
    }  
  
    printf("Process %d finish\n", getpid());  
    exit(EXIT_SUCCESS);  
}

本文介绍了linux系統中的有名管道(FIFO)的方法,包括有名管道的创建、打开、读写、关闭和删除等方面。通过了解和掌握这些知识,我们可以更好地使用有名管道(FIFO)来实现进程间通信,提高系统的性能和可靠性。当然,linux系統中的有名管道(FIFO)还有很多其他的特性和用法,需要我们不断地学习和探索。希望本文能给你带来一些启发和帮助。

以上是linux系統中的有名管道(FIFO)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:lxlinux.net。如有侵權,請聯絡admin@php.cn刪除