標籤:http io ar os 使用 sp for 檔案 資料
在unix系統上最早的IPC形式為管道,管道的建立使用pipe函數:
- #include <unistd.h>
- int pipe(int pipefd[2]);
該函數建立一個單向的管道,返回兩個描述符 pipefd[0],和pipefd[1],pipefd[0]用於讀操作,pipefd[1]用於寫操作。該函數一般應用在父子進程(有親緣關係的進 程)之間的通訊,先是一個進程建立管道,再fork出一個子進程,然後父子進程可以通過管道來實現通訊。
管道具有以下特點:
管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;
只能用於父子進程或者兄弟進程之間(具有親緣關係的進程);
單獨構成一種獨立的檔案系統:管道對於管道兩端的進程而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中。
資料的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。
函數pipe一般使用步驟如下:
1.pipe建立管道;
2.fork建立子進程;
3.父子進程分別關閉掉讀和寫(或寫和讀)描述符;
4.讀端在讀描述符上開始讀(或阻塞在讀上等待寫端完成寫),寫端開始寫,完成父子進程通訊過程。
一個簡單的通訊實現(來自linux man手冊的修改)
- #include <sys/wait.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <errno.h>
-
-
- int main(int argc, char *argv[])
- {
- int pipefd[2];
- pid_t cpid;
- char buf[128];
- int readlen;
-
-
- if (argc != 2) {
- fprintf(stderr, "Usage: %s <string>\n", argv[0]);
- return -1;
- }
- if (pipe(pipefd) < 0) {
- fprintf(stderr, "pipe: %s\n", strerror(errno));
- return -1;
- }
- cpid = fork();
- if (cpid < 0) {
- fprintf(stderr, "fork: %s\n", strerror(errno));
- return -1;
- }
- if (0 == cpid) { /* 子進程 */
- close(pipefd[1]); /* 子進程關閉寫端 */
- readlen = read(pipefd[0], buf, 128); //子進程阻塞在讀上,等待父進程寫
- if (readlen < 0) {
- fprintf(stderr, "read: %s\n", strerror(errno));
- return -1;
- }
- write(STDOUT_FILENO, buf, readlen);
- write(STDOUT_FILENO, "\n", 1);
- close(pipefd[0]); //讀完之後關閉讀描述符
- return 0;
- } else { /* 父進程 */
- close(pipefd[0]); /*父進程關閉沒用的讀端 */
- sleep(2);
- write(pipefd[1], argv[1], strlen(argv[1])); //父進程開始寫
- close(pipefd[1]); /* 父進程關閉寫描述符 */
- wait(NULL); /* 父進程等待子進程退出,回收子進程資源 */
- return 0;
- }
- }
運行時將列印命令列輸入參數,列印將在父進程睡眠2秒之後,子進程將阻塞在讀,直到父進程寫完資料,可見管道是有同步機制的,不需要自己添加同步機制。如果希望兩個進程雙向資料轉送,那麼需要建立兩個管道來實現。
管道最大的劣勢就是只能在擁有共同祖先進程的進程之間通訊,在無親緣關係的兩個進程之間沒有辦法使用,不過有名管道FIFO解決了這個問題。FIFO類似 於pipe,也是只能單向傳輸資料,不過和pipe不同的是他可以在無親緣關係的進程之間通訊,它提供一個路徑與之關聯,所以只要能訪問該路徑的進程都可 以建立起通訊,類似於前面的共用記憶體,都提供一個路徑與之關聯。
- #include <sys/types.h>
- #include <sys/stat.h>
- int mkfifo(const char *pathname, mode_t mode);
pathname 為系統路徑名,mode為檔案許可權位,類似open函數第二個參數。
開啟或建立一個新的fifo是先調用mkfifo,當指定的pathname已存在fifo時,mkfifo返回EEXIST錯誤,此時再調用open函數。
下面來使用mkfifo實現一個無親緣關係進程間的雙向通訊,此時需要建立兩個fifo,分別用於讀寫。服務進程迴圈的讀並等待客戶進程寫,之後列印客戶進程傳來資料並向客戶進程返回資料;客戶進程向伺服器寫資料並等待讀取服務進程返回的資料。
server process:
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include "slnipc.h"
- int main(int argc, const char *argv[])
- {
- int rc;
- int wr_fd, rd_fd;
- char sendbuf[128];
- char recvbuf[128];
- rc = mkfifo(SLN_IPC_2SER_PATH, O_CREAT | O_EXCL); //建立服務進程讀的fifo
- if ((rc < 0 ) && (errno != EEXIST)) {
- fprintf(stderr, "mkfifo: %s\n", strerror(errno));
- return -1;
- }
- rc = mkfifo(SLN_IPC_2CLT_PATH, O_CREAT | O_EXCL); //建立服務進程寫的fifo
- if ((rc < 0 ) && (errno != EEXIST)) {
- fprintf(stderr, "mkfifo: %s\n", strerror(errno));
- return -1;
- }
- wr_fd = open(SLN_IPC_2CLT_PATH, O_RDWR, 0);
- if (wr_fd < 0) {
- fprintf(stderr, "open: %s\n", strerror(errno));
- return -1;
- }
- rd_fd = open(SLN_IPC_2SER_PATH, O_RDWR, 0);
- if (rd_fd < 0) {
- fprintf(stderr, "open: %s\n", strerror(errno));
- return -1;
- }
- for (;;) {
- rc = read(rd_fd, recvbuf, sizeof(recvbuf)); //迴圈等待接受客戶進程資料
- if (rc < 0) {
- fprintf(stderr, "read: %s\n", strerror(errno));
- continue;
- }
- printf("server recv: %s\n", recvbuf);
- snprintf(sendbuf, sizeof(sendbuf), "Hello, this is server!\n");
- rc = write(wr_fd, sendbuf, strlen(sendbuf));
- if (rc < 0) {
- fprintf(stderr, "write: %s\n", strerror(errno));
- continue;
- }
- }
- close(wr_fd);
- close(rd_fd);
- return 0;
- }
client process
- #include <stdio.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <string.h>
- #include <errno.h>
- #include "slnipc.h"
- int main(int argc, const char *argv[])
- {
- int rc;
- int rd_fd, wr_fd;
- char recvbuf[128];
- char sendbuf[128];
- if (argc != 2) {
- fprintf(stderr, "Usage: %s <string>\n", argv[0]);
- return -1;
- }
- snprintf(sendbuf, sizeof(sendbuf), "%s", argv[1]);
- wr_fd = open(SLN_IPC_2SER_PATH, O_RDWR, 0);
- if (wr_fd < 0) {
- fprintf(stderr, "open: %s\n", strerror(errno));
- return -1;
- }
- rd_fd = open(SLN_IPC_2CLT_PATH, O_RDWR, 0);
- if (rd_fd < 0) {
- fprintf(stderr, "open: %s\n", strerror(errno));
- return -1;
- }
- rc = write(wr_fd, sendbuf, strlen(sendbuf));
- if (rc < 0) {
- fprintf(stderr, "write: %s\n", strerror(errno));
- return -1;
- }
- rc = read(rd_fd, recvbuf, sizeof(recvbuf));
- if (rc < 0) {
- fprintf(stderr, "write: %s\n", strerror(errno));
- return -1;
- }
- printf("client read: %s\n", recvbuf);
- close(wr_fd);
- close(rd_fd);
- return 0;
- }
伺服器先啟動運行,之後運行用戶端,運行結果
- # ./server
- server recv: hi,this is fifo client
-
-
- # ./client "hi,this is fifo client"
- client read: Hello, this is server!
這裡有一些類似於socket實現處理序間通訊過程,只是fifo的讀寫描述符是兩個,socket的讀寫使用同一個描述符。fifo的出現克服了管道的只 能在有親緣關係的進程之間的通訊。和其他的處理序間通訊一直,fifo傳送的資料也是位元組流,需要自己定義協議格式來解析通訊的資料,可以使用socket 章節介紹的方式來實現的通訊協定。
本節源碼下載:
http://download.csdn.net/detail/gentleliu/8183027
- 相關文章推薦:
- 細說linux IPC(一):基於socket的處理序間通訊(上)
- 細說linux IPC(二):基於socket的處理序間通訊(下)
- 細說linux IPC(三):mmap系統調用共用記憶體
- 本文來自:愛好Linux技術網
- 本文連結:http://www.ahlinux.com/c/9591.html
細說linux IPC(六):pipe和FIFO