linux進程間通信快速入門【一】:管道編程

介紹

	管道本質上就是一個文件,前面的進程以寫方式打開文件,后面的進程以讀方式打開。這樣前面寫完后面讀,于是就實現了通信。雖然實現形態上是文件,但是管道本身并不占用磁盤或者其他外部存儲的空間。在Linux的實現上,它占用的是內存空間。所以,Linux上的管道就是一個操作方式為文件的內存緩沖區。管道分類:匿名管道、命名管道

命令行中使用

1、mkfifo或mknod命令來創建一個命名管道

[root@VM-90-225-centos ~]# mkfifo pipe
[root@VM-90-225-centos ~]# ls -l pipe
prw-r--r-- 1 root root 0 Feb 28 21:02 pipe

我們現在讓一個進程寫這個管道文件:

 echo 12345 > pipe

此時這個寫操作會阻塞,因為管道另一端沒有人讀。此時如果有進程讀這個管道,那么這個寫操作的阻塞才會解除:

[root@VM-90-225-centos ~]# cat pipe 
12345

當我們cat完這個文件之后,另一端的echo命令也返回了.
Linux系統無論對于命名管道和匿名管道,底層都用的是同一種文件系統的操作行為,這種文件系統叫pipefs,可以通過下面命令查看是否具有這個系統:

[root@VM-90-225-centos ~]# cat /proc/filesystems |grep pipefs
nodev   pipefs
nodev   rpc_pipefs

系統編程中使用

匿名管道和命名管道分別叫做PIPE和FIFO,創建匿名管道的系統調用是pipe(),而創建命名管道的函數是mkfifo()。
匿名管道:

#include <unistd.h>
int pipe(int pipefd[2]);

這個方法將會創建出兩個文件描述符:
pipefd[0]是讀方式打開,作為管道的讀描述符。pipefd[1]是寫方式打開,作為管道的寫描述符。從管道寫端寫入的數據會被內核緩存直到有人從另一端讀取為止。

pipe示例一:簡單的寫入+讀出

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>#define STRING "hello world!"int main()
{int pipefd[2];char buf[BUFSIZ];// 創建一組管道if (pipe(pipefd) == -1) {perror("pipe()");exit(1);}// 從[1]寫入STRINGif (write(pipefd[1], STRING, strlen(STRING)) < 0) {perror("write()");exit(1);}// 從[0]讀出,結果存到bufif (read(pipefd[0], buf, BUFSIZ) < 0) {perror("write()");exit(1);}// 打印讀出來的結果printf("%s\n", buf);exit(0);
}

程序創建了一個管道,并且對管道寫了一個字符串之后從管道讀取,并打印在標準輸出上。
當然這不屬于進程間通信,實際情況中我們不會在單個進程中使用管道。
進程在pipe創建完管道之后,往往都要fork產生子進程。下面的demo是父子兩個進程使用一個管道可以完成半雙工通信。此時,父進程可以通過fd[1]給子進程發消息,子進程通過fd[0]讀。子進程也可以通過fd[1]給父進程發消息,父進程用fd[0]讀。

pipe示例二:父子進程半雙工通信

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>#define STRING "hello world!"int main()
{int pipefd[2];pid_t pid;char buf[BUFSIZ];// 創建管道if (pipe(pipefd) == -1) {perror("pipe()");exit(1);}// fork產生子進程pid = fork();if (pid == -1) {perror("fork()");exit(1);}// fork()新進程返回0,舊進程返回新進程的進程ID。if (pid == 0) {/* this is child. */printf("Child pid is: %d\n", getpid());// 子進程會繼承父進程對應的文件描述符// 父進程先pipe創建管道之后,子進程也會得到同一個管道的讀寫文件描述符if (read(pipefd[0], buf, BUFSIZ) < 0) {perror("write()");exit(1);}printf("%s\n", buf);// 清空bufbzero(buf, BUFSIZ);snprintf(buf, BUFSIZ, "Message from child: My pid is: %d", getpid());// 把讀取到的數據重新發送給主進程if (write(pipefd[1], buf, strlen(buf)) < 0) {perror("write()");exit(1);}} else {/* this is parent */printf("Parent pid is: %d\n", getpid());snprintf(buf, BUFSIZ, "Message from parent: My pid is: %d", getpid());// 父進程寫數據到管道if (write(pipefd[1], buf, strlen(buf)) < 0) {perror("write()");exit(1);}// 等待1ssleep(1);// 清空bufbzero(buf, BUFSIZ);// 讀取管道消息到bufif (read(pipefd[0], buf, BUFSIZ) < 0) {perror("write()");exit(1);}printf("%s\n", buf);wait(NULL);}exit(0);
}

打印結果:

Parent pid is: 17697
Child pid is: 17702
Message from parent: My pid is: 17697
Message from child: My pid is: 17702

如果在vscode中debug的話是debug不到if (pid == 0) 的分支的,你只能debug主進程的流程。
從以上的demo中用同一個管道的父子進程可以分時給對方發送消息,我們可以看到對管道讀寫的一些特點:
1、在管道中沒有數據的情況下,對管道的讀操作會阻塞,直到管道內有數據為止。
2、當一次寫的數據量不超過管道容量的時候,對管道的寫操作一般不會阻塞,直接將要寫的數據寫入管道緩沖區即可。
管道實際上就是內核控制的一個內存緩沖區,既然是緩沖區,就有容量上限。我們把管道一次最多可以緩存的數據量大小叫做PIPESIZE。內核在處理管道數據的時候,底層也要調用類似read和write這樣的方法進行數據拷貝,這種內核操作每次可以操作的數據量也是有限的,一般的操作長度為一個page,即默認為4k字節。我們把每次可以操作的數據量長度叫做PIPEBUF。POSIX標準中,對PIPEBUF有長度限制,要求其最小長度不得低于512字節。PIPEBUF的作用是,內核在處理管道的時候,如果每次讀寫操作的數據長度不大于PIPEBUF時,保證其操作是原子的。而PIPESIZE的影響是,大于其長度的寫操作會被阻塞,直到當前管道中的數據被讀取為止。
在Linux 2.6.11之前,PIPESIZE和PIPEBUF實際上是一樣的。在這之后,Linux重新實現了一個管道緩存,并將它與寫操作的PIPEBUF實現成了不同的概念,形成了一個默認長度為65536字節的PIPESIZE,而PIPEBUF只影響相關讀寫操作的原子性。從Linux 2.6.35之后,在fcntl系統調用方法中實現了F_GETPIPE_SZ和F_SETPIPE_SZ操作,來分別查看當前管道容量和設置管道容量。管道容量容量上限可以在/proc/sys/fs/pipe-max-size進行設置。
在實際情境下,半雙工管道管道的兩端都可能有多個進程進行讀寫處理。如果再加上線程,則事情可能變得更復雜。實際上,我們在使用管道的時候,并不推薦這樣來用。管道推薦的使用方法是其單工模式:即只有兩個進程通信,一個進程只寫管道,另一個進程只讀管道。

pipe示例三:父子進程單工通信

這個程序實際上比上一個要簡單,父進程關閉管道的讀端,只寫管道。子進程關閉管道的寫端,只讀管道。
此時兩個進程就只用管道實現了一個單工通信,并且這種狀態下不用考慮多個進程同時對管道寫產生的數據交叉的問題,這是最經典的管道打開方式,也是我們推薦的管道使用方式。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>#define STRING "hello world!"int main()
{int pipefd[2];pid_t pid;char buf[BUFSIZ];if (pipe(pipefd) == -1) {perror("pipe()");exit(1);}pid = fork();if (pid == -1) {perror("fork()");exit(1);}if (pid == 0) {/* this is child. */close(pipefd[1]);printf("Child pid is: %d\n", getpid());// 讀if (read(pipefd[0], buf, BUFSIZ) < 0) {perror("write()");exit(1);}printf("%s\n", buf);} else {/* this is parent */close(pipefd[0]);printf("Parent pid is: %d\n", getpid());snprintf(buf, BUFSIZ, "Message from parent: My pid is: %d", getpid());// 寫if (write(pipefd[1], buf, strlen(buf)) < 0) {perror("write()");exit(1);}wait(NULL);}exit(0);
}

命名管道
命名管道在底層的實現跟匿名管道完全一致,區別只是命名管道會有一個全局可見的文件名以供別人open打開使用。再程序中創建一個命名管道文件的方法有兩種,一種是使用mkfifo函數。另一種是使用mknod系統調用

fifo示例:

client端

/* 這是一個命名管道的實現demo,實現兩個進程間聊天功能* */
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>int main()
{char *file = "./test.fifo";umask(0); //設置umask,僅在當前進程有效。if(mkfifo(file,0663)<0){if(errno == EEXIST){printf("fifo exist\n");}else {perror("mkfifo\n");return -1;}}int fd = open(file,O_WRONLY);if(fd<0){perror("open error");return -1;}printf("open fifo success!!!\n");while(1){printf("input: ");fflush(stdout);char buff[1024]={0};scanf("%s",buff);write(fd,buff,strlen(buff));}return 0;
}

server端:

#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>int main()
{char *file = "./test.fifo";umask(0); //設置umask,僅在當前進程有效。if(mkfifo(file,0663)<0){if(errno == EEXIST){printf("fifo exist\n");}else {perror("mkfifo\n");return -1;}}int fd = open(file,O_RDONLY);if(fd<0){perror("open error");return -1;}printf("open fifo success!!!\n");while(1){char buff[1024] = {0};int ret = read(fd,buff,1024);if(ret>0){printf("peer say:%s\n",buff);}}return 0;
}

然后在子目錄下編譯:

g++ ./server.cpp -o server
g++ ./client.cpp -o client

然后在兩個終端頁面上分別運行:

./server
./client

即可進行單向通信,一般工程中兩個進程建立兩個fifo就可以進行雙向通信了

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376652.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376652.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376652.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

返回長度hdu 1518 square

查了好多資料&#xff0c;發現還是不全&#xff0c;干脆自己整理吧&#xff0c;至少保證在我的做法正確的&#xff0c;以免誤導讀者&#xff0c;也是給自己做個記載吧&#xff01; 題目的意思是比較明顯的&#xff0c;就是當初給你m根木棒&#xff0c;當初讓你判斷利用這些木棒…

POJ 3233 Matrix Power Series 矩陣快速冪 + 二分

題意&#xff1a;求矩陣的次方和 解題思路&#xff1a;最容易想到方法就是兩次二分因為 我們可以把一段 A^1 A^2 .......A^K 變成 A^1 ..A^(K/2) ( A^1 ..A^(K/2))*(A^(k/2)) 當k 為奇數的時候 或者 A^1 ..A^(K/2) ( A^1 ..A^(K/2))*(A^(k/2)) A^K 當K 為偶數的時候…

時間序列進行分析的一些手法以及代碼實現(移動平均、指數平滑、SARIMA模型、時間序列的(非)線性模型)

文章目錄1、移動平均moving average方法weighted average方法2、指數平滑單指數平滑 exponential_smoothing雙指數平滑三指數平滑 Triple exponential smoothing3、平穩性以及時間序列建模SARIMA模型4、時間序列的&#xff08;非&#xff09;線性模型時間序列的滯后值使用線性回…

政權組織形式

神馬國家結構、政權組織形式的 現在我比較明確的是英國的政權組織形式是式君主立憲制、美國是總統制、中國是人民代表大會制。 目前,世界各國采用的國家結構可分為單一制和復合制兩大類。其中&#xff0c;復合制國家結構形式主要包括聯邦制和邦聯制兩種類型。英國、法國、意大利…

三大平衡樹(Treap + Splay + SBT)總結+模板

Treap樹 核心是 利用隨機數的二叉排序樹的各種操作復雜度平均為O(lgn) Treap模板&#xff1a; #include <cstdio> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <cstdlib> #include <cmath…

mysqld進程 ut_delay 占用率過高

采用性能分析工具perf top -p mysqld進程 在測試mysql數據庫時&#xff0c;用perf top如果看到熱點函數是ut_delay或者_raw_spin_lock的話&#xff0c;說明鎖爭用比較嚴重。 ut_delay這是innodb的一個自旋瑣。也就是說&#xff0c;在這里由于鎖等待&#xff0c;innodb不停地在…

TClientDataSet使用要點

TClientDataSet控件繼承自TDataSet&#xff0c;其數據存儲文件格式擴展名為 .cds&#xff0c;是基于文件型數據存儲和操作的控件。該控件封裝了對數據進行操作處理的接口和功能&#xff0c;而本身并不依賴上述幾種數據庫驅動程序&#xff0c;基本上能滿足單機"瘦"數據…

滑動窗口在重構數據集的作用

step1&#xff1a;使用滑動窗口重構數據集 給定時間序列數據集的數字序列&#xff0c;我們可以將數據重構為看起來像監督學習問題。 我們可以通過使用以前的時間步作為輸入變量并使用下一個時間步作為輸出變量來做到這一點。 通過觀察重構后的數據集與原本的時間序列&…

sliverlight - Unhandled Error in Silverlight Application錯誤

使用firebug控制臺輸出錯誤&#xff1a; Unhandled Error in Silverlight Application 查詢“GetFlow_Process”的 Load 操作失敗。遠程服務器返回了錯誤: NotFound。 位于 System.ServiceModel.DomainServices.Client.OperationBase.Complete(Exception error) 位于 System.S…

前向驗證對于模型的更新作用

首先&#xff0c;讓我們看一個小的單變量時間序列數據&#xff0c;我們將用作上下文來理解這三種回測方法&#xff1a;太陽黑子數據集。該數據集描述了剛剛超過 230 年&#xff08;1749-1983 年&#xff09;觀察到的太陽黑子數量的每月計數。 數據集顯示了季節之間差異很大的…

2014年9月21日_隨筆,jdic,ETL,groovy,Nutz好多東西想學

&#xff08;1&#xff09;老媽十一要回老家&#xff0c;才突然發現買票好難啊。有親朋很重要 &#xff08;2&#xff09;這周我做了什么。jdic,ETL,groovy, Nutz好多東西想學。 Nutz開發成員專訪、Nutz優酷視頻(演講)、Nutz 入門教程、 &#xff08;3&#xff09;想改變&#…

PHP-面向對象(八)

1、多態的介紹與優勢 多態性是繼抽象和繼承后&#xff0c;面向對象語言的第三個特征。從字面上理解&#xff0c;多態的意思是“多種形態”&#xff0c;簡單來說&#xff0c;多態是具有表現多種形態的能力的特征&#xff0c;在OO中是指“語言具有根據對象的類型以不同方式處理。…

雙指數平滑中參數對于預測模型的影響

先看看α 在β一致的情況下&#xff0c;α越小&#xff0c;模型越滯后。 再看看β 在α一致的情況下&#xff0c;β越大&#xff0c;模型對于趨勢的預測更敏銳。

SQL 性能不佳的幾個原因

SQL 性能不佳的幾個原因 ?不準確的統計數據?差勁的索引?差勁的查詢設計 ?差勁的執行計劃&#xff0c;通常是由不正確的參數引起的?過度阻塞和死鎖 ?非基于集合的操作?不良數據庫設計 ?過度碎片 ?不能重復使用執行計劃 ?查詢頻繁重編譯 ?不當使用游標 ?數據庫日志的…

分頁查詢

分頁查詢算是比較常用的一個查詢了在DAO層主要是查兩個數據第一個總條數第二個要查詢起始記錄數到查詢的條數當第一次點擊查詢時候(非下一頁時Page類里面預設的就是 index就是0 pageSize是預設值當點擊下一頁的時候 index 和 pageSize帶的就是頁面上面給的值了分頁的頁面一般的…

TypeError: Object of type ‘datetime‘ is not JSON serializable

python中這個錯誤的原因是json.dumps無法對字典中的datetime時間格式數據進行轉化&#xff0c;dumps的原功能是將dict轉化為str格式&#xff0c;不支持轉化時間. 所以請這樣使用&#xff1a; json.dumps(response_data, defaultstr)

oracle問題

ORA-01031: insufficient privileges 用戶沒有權限&#xff0c;給它賦予角色轉載于:https://www.cnblogs.com/50614090/p/3986880.html

me23n去價格

SELECT knumv kposn AS ebelp kschl kbetr kpein kwert INTO CORRESPONDING FIELDS OF TABLE gt_konv FROM konv FOR ALL ENTRIES IN gt_ekpo WHERE knumv gt_ekpo-knumv AND kinak EQ AND kschl IN (PB00,PBXX,P101).轉載于:…

Fix “Windows cannot access the specified device path or file” Error

http://helpdeskgeek.com/help-desk/windows-cannot-access-the-specified-device-path-or-file/ Method 1 – Windows Server 2003 Terminal Services Firstly, if you’re running into this issue on a Windows Server box running Terminal Services, your problem can be …

使用Bootstrap-table創建表單,并且與flask后臺進行數據交互

文章目錄引用css和js使用htmljavascriptflaskmysql參考引用css和js Bootstrap-table為這些文件提供了 CDN 的支持&#xff0c;所以不需要下載.js .css文件就可以直接用了&#xff0c;十分方便 <!-- Latest compiled and minified CSS --> <link rel"stylesheet…