管道,通常指無名管道,是 UNIX 系統IPC最古老的形式。
1、特點:
- 它是半雙工的(即數據只能在一個方向上流動),具有固定的讀端和寫端。
- 它只能用于具有親緣關系的進程之間的通信(也是父子進程或者兄弟進程之間)。
- 它可以看成是一種特殊的文件,對于它的讀寫也可以使用普通的read、write 等函數。但是它不是普通的文件,并不屬于其他任何文件系統,并且只存在于內存中。
1). 當一個管道建立時,它會創建兩個文件描述符:fd[0]
為讀而打開,fd[1]
為寫而打開。如下圖:?
要關閉管道只需將這兩個文件描述符關閉即可。
2). 單個進程中的管道幾乎沒有任何用處。所以,通常調用 pipe 的進程接著調用 fork,這樣就創建了父進程與子進程之間的 IPC 通道。如下圖所示:
?
1. 測試代碼:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{int pipefd[2];int ret, nwrite, nread;pid_t pid;char writebuf[1024];char readbuf[1024];ret = pipe(pipefd);if (ret == -1){perror("pipe");return -1;}pid = fork();if (pid == -1){perror("fork");return -1;}else if (pid == 0){close(pipefd[1]);while (1){nread = read(pipefd[0], readbuf, sizeof(readbuf));if (nread == -1){perror("read");exit(-1);}printf("read from pipe %s\n", readbuf);if (!strncmp(readbuf, "quit", 4))break;}}if (pid > 0){close(pipefd[0]);while (1){fgets(writebuf, sizeof(writebuf), stdin);nwrite = write(pipefd[1], writebuf, sizeof(writebuf));if (nwrite == -1){perror("write");exit(-1);}if (!strncmp(writebuf, "quit", 4))break;}}
}
輸出結果:
?
有名管道
1. 為何提出有名管道的說法,目的是為了克服無名管道的不足之處:
- 無名管道只能是用于具有親緣關系的進程之間,這就限制了無名管道的使用范圍。
- 有名管道可以使互不相關的兩個進程互相通信,有名管道可以通過路徑名來指出。并在文件系統課件為了這種有名管道,Linux中專門設立了一個專門的特殊文件系統-管道文件,以FIFO的形式存在于文件系統中,這樣,即使與FIFO的創建者不存在親緣關系的進程,只要訪問該路徑,就能彼此通過FIFO相互通信,因此,通過FIFO不相關的進程也能交換數據,但在磁盤只是一個節點,而文件的數據只存在內存緩沖頁面上,與普通管道一樣。
2. 有名管道的創建
有名管道可以從命令行上創建,命令行方法是使用下面這個命令:
$ mkfifo myfifo
有名管道也可以從程序里創建,相關API有:
int mkfifo(cosnt char *path, mode_t mode);
參數:
- 第一個參數是一個普通的路徑名,也就是創建后FIFO名字。
- 第二個參數與打開普通文件的open函數中的mode參數相同(文件的讀寫權限),如果mkfifo的一個參數是一個已經存在路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼會檢查是否返回該錯誤,如果確實返回該錯誤,那么要調用打開FIFO的函數open就可以了。
3. FIFO的open函數打開規則:
O_RDONLY、O_WRONLY和O_NONBLOCK標志共有四種合法的組成方式:
flags = O_RDONLY:open將會調用阻塞,除非有另外一個進程以寫的方式打開用一個FIFO,否則一直等待。
flags = O_WRONLY:open將會調用阻塞,除非有另外一個進程以讀的方式打開同一個FIFO,否則一直等待。
flags =?O_RDONLY | O_NONBLOCK:如果此時沒有其他進程以寫的方式打開FIFO,此時open也會成功返回,此時FIOF被讀打開,而不會返回錯誤。
flag是= O_WRONLY | O_NONBLOCK:立即返回,如果此時沒有其他進程以讀的方式打開,open會失敗打開,此時FIOF沒有被打開,返回-1。
?
?
?程序1:
?
// writefifo.c
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, const char *argv[])
{int fd, nwrite;char buf[1024] = "\0";if (access(argv[1], F_OK) != 0){int ret = mkfifo(argv[1], 0666);if (ret == -1){perror("mkfifo");exit(-1);}}fd = open(argv[1], O_WRONLY);if (fd == -1){perror("open");return -1;}while (1){fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';nwrite = write(fd, buf, strlen(buf));if (nwrite == -1){perror("write error");return -1;}if (!strncmp(buf, "quit", 4))break;}return 0;
}
程序2:
//readfifo.c
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, const char *argv[])
{int ret, fd, nread;char buf[1024] = "\0";if (access(argv[1], F_OK) != 0){ret = mkfifo(argv[1], 0666);if (ret == -1){perror("mkfifo");exit(-1);}}fd = open(argv[1], O_RDONLY);if (fd == -1){perror("open");exit(-1);}while (1){nread = read(fd, buf, sizeof(buf));if (nread == -1){perror("read errro");exit(-1);}printf("read from fifo is %s\n", buf);if (!strncmp(buf, "quit", 4))break;memset(buf, '\0', sizeof(buf));}return 0;
}
輸出結果:?