管道
管道分為兩種類型:全雙工管道與半雙工管道。半雙工管道即意味著數據只能在一個方向上流動。全雙工管道上的數據可以兩個方向流動。老舊系統上大多不提供全雙工管道,所以為了移植性考慮,應主要使用半雙工管道。通常管道只能在具有公共祖先的兩個進程間使用。
盡管有局限性,但是半雙工管道仍舊是最常用的進程間通信方式。管道通過pipe()函數創建。
int pipe(int fd[2]);
fd返回兩個文件描述符,fd[0]為讀而打開,fd[1]為寫而打開。我們通過一個例子來說明。
int fd[2];
char line[length];
pipe(fd);
if(pid = fork()>0) {
close(fd[0]);
write(fd[1], "hello world!\n", 12);
}//parent
else {
close(fd[1]);
n = read(fd[0], line, length);
printf("%s", line);
}//child
首先我們創建一個半雙工管道,然后通過fork()函數生成子進程。子進程會繼承父進程的所有文件描述符,顯然fd[0]、fd[1]都繼續保存著。然后父進程關閉讀通道,子進程關閉寫通道,父進程向半雙工管道寫入一串字符串,然后子進程讀入后寫入標準輸出。
其實實現全雙工管道也不是一件復雜的事。我們只需要建立兩個半雙工管道即可。
int fd1[2],fd2[2];
char line1[length],line2[length];
pipe(fd1);
pipe(fd2);
if(pid = fork()>0) {
close(fd1[0]);
close(fd2[1]);
write(fd1[1], "hello world!\n", 12);
read(fd2[0],line1,length);
printf("%s", line1);
}//parent
else {
close(fd1[1]);
close(fd2[0]);
n = read(fd1[0], line, length);
write(fd2[1], "hello world!\n", 12);
printf("%s", line2);
}//child
FIFO
FIFO有時被稱為命名管道。未命名的管道只能在兩個有共同祖先的進程之間使用。FIFO是一個半雙工管道。
FIFO由mkfifo()函數創建。
int mkfifo(const char *pathname, mode_t mode);
mode參數指定文件權限位,類似于open函數的第二個參數。而pathname是一個普通的Unix路徑名,代表FIFO的位置。
需要注意的是mkfifo()函數只能創建一個新的管道,而打開一個新的管道只能用open()函數。
#define FIFO1 "tmp/fifo.1"
#define FIFO2 "tmp/fifo.2"
char line1[length],line2[length];
mkfifo(FIFO1, FILE_MODE);
mkfifo(FIFO2, FILE_MODE);
if(pid = fork()>0) {
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_wDONLY, 0);
write(writefd, "hello world!\n", 12);
read(readfd, line1, length);
printf("%s", line1);
}//parent
else {
readfd = open(FIFO2, O_RDONLY, 0);
writefd = open(FIFO1, O_wDONLY, 0);
write(writefd, "hello world!\n", 12);
read(readfd, line2, length);
printf("%s", line2);
}//child
可以看到這和半雙工管道很像,可以把理解成存儲在Unix系統中的管道。