Linux之匿名管道和命名管道詳解
匿名管道
kubectl get pod -A | grep mysql
上面命令行里的「|」豎線就是一個管道,在命令行(如 Linux Shell 或 Windows CMD/PowerShell)中,管道操作符 | 的作用是將 前一個命令的標(biāo)準(zhǔn)輸出(stdout) 傳遞給 后一個命令的標(biāo)準(zhǔn)輸入(stdin)。但它默認(rèn) 不會傳遞標(biāo)準(zhǔn)錯誤(stderr),這是它的核心行為特點(diǎn)。
1. 管道|的基本行為
command1 | command2
command1的stdout→ 作為command2的stdin。command1的stderr→ 直接打印到終端,不會傳遞給command2。
示例 1(stdout 被管道傳遞)
# ls 成功時,stdout(文件列表)會傳遞給 grep ls /usr/bin | grep "python"
示例 2(stderr 未被管道傳遞)
# 如果目錄不存在,錯誤信息會直接顯示,不會傳遞給 grep ls /nonexistent_dir | grep "error"
輸出:
ls: cannot access '/nonexistent_dir': No such file or directory
(錯誤信息直接顯示,grep 不會處理它)
2. 為什么|不處理 stderr
- 設(shè)計初衷:
|的職責(zé)是傳遞 正常輸出,錯誤信息通常需要直接反饋給用戶。 - 分離數(shù)據(jù)流:stdout(正常輸出)和 stderr(錯誤信息)是獨(dú)立的流,
|默認(rèn)只操作 stdout。
3. 原理
int pipe(int fd[2])
這里表示創(chuàng)建一個匿名管道,并返回了兩個描述符,一個是管道的讀取端描述符,另一個是管道的寫入端描述符fd。注意,這個匿名管道是特殊的文件,只存在于內(nèi)存,不存于文件系統(tǒng)中。

所謂的管道,就是內(nèi)核里面的一串緩存。從管道的一段寫入的數(shù)據(jù),實(shí)際上是緩存在內(nèi)核中的,另一端讀取,也就是從內(nèi)核中讀取這段數(shù)據(jù)。另外,管道傳輸?shù)臄?shù)據(jù)是無格式的流且大小受限。
管道只能一端寫入,另一端讀出,所以上面這種模式容易造成混亂,因?yàn)楦高M(jìn)程和子進(jìn)程都可以同時寫入,也都可以讀出。那么,為了避免這種情況,通常的做法是:
- 父進(jìn)程關(guān)閉讀取的 fd[o],只保留寫入的 fd[1];
- 子進(jìn)程關(guān)閉寫入的fd[1],只保留讀取的 fd[o];

在shell 里面執(zhí)行 A | B 命令的時候,A 進(jìn)程和 B 進(jìn)程都是shell 創(chuàng)建出來的子進(jìn)程,A 和 B 之間不存在父子關(guān)系,它倆的父進(jìn)程都是 shell。

命名管道
簡介
- 匿名管道,它的通信范圍是存在父子關(guān)系的進(jìn)程。因?yàn)楣艿罌]有實(shí)體,也就是沒有管道文件,只能通過fork來復(fù)制父進(jìn)程 fd文件描述符,來達(dá)到通信的目的。
- 對于命名管道,它可以在不相關(guān)的進(jìn)程間也能相互通信。因?yàn)槊罟艿?提前創(chuàng)建了一個類型為管道的設(shè)備文件,在進(jìn)程里只要使用這個設(shè)備文件,就可以相互通信。
- 不管是匿名管道還是命名管道,進(jìn)程寫入的數(shù)據(jù)都是緩存在內(nèi)核中,另一個進(jìn)程讀取數(shù)據(jù)時候自然也是從內(nèi)核中獲取,同時通信數(shù)據(jù)都遵循先進(jìn)先出原則,不支持Iseek之類的文件定位操作。
使用
int mkfifo(const char *pathname, mode_t mode);
創(chuàng)建 FIFO
使用 mkfifo 命令或 mkfifo() 函數(shù)創(chuàng)建:
mkfifo /tmp/myfifo # 創(chuàng)建一個名為 /tmp/myfifo 的 FIFO
pathname:FIFO 的文件路徑(如/tmp/myfifo)。mode:權(quán)限(如0644,表示用戶可讀寫,其他人只讀)。
FIFO 的特點(diǎn)
半雙工通信
- 同一時間只能 讀 或 寫,不能同時讀寫(類似單行道)。
- 示例:
# 終端1:寫入數(shù)據(jù) echo "Hello" > /tmp/myfifo # 終端2:讀取數(shù)據(jù) cat /tmp/myfifo # 輸出 "Hello"
阻塞機(jī)制
- 如果沒有進(jìn)程在 讀,寫操作會 卡住,直到有進(jìn)程來讀。
- 如果沒有進(jìn)程在 寫,讀操作會 卡住,直到有進(jìn)程來寫。
數(shù)據(jù)是流式的
- 數(shù)據(jù)像水流一樣,讀完后會消失,不能像普通文件那樣隨意跳轉(zhuǎn)(
lseek無效)。
如何使用 FIFO?
(1)命令行測試
# 終端1:監(jiān)聽 FIFO(讀) cat /tmp/myfifo # 終端2:發(fā)送數(shù)據(jù)(寫) echo "Hello FIFO" > /tmp/myfifo
終端1 會顯示 Hello FIFO。
(2)C 語言示例
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
mkfifo("/tmp/myfifo", 0644); // 創(chuàng)建 FIFO
int fd = open("/tmp/myfifo", O_WRONLY); // 以寫方式打開
write(fd, "Hello", 6); // 寫入數(shù)據(jù)
close(fd);
return 0;
}
常見問題
Q1: FIFO 和普通文件有什么區(qū)別?
- FIFO 是 內(nèi)存中的管道,數(shù)據(jù)讀完就沒了;普通文件會持久化存儲。
Q2: FIFO 和匿名管道(|)有什么區(qū)別?
- 匿名管道只能用于 父子進(jìn)程,F(xiàn)IFO 可用于 任意進(jìn)程。
Q3: 如果 FIFO 已經(jīng)存在,再創(chuàng)建會怎樣?
mkfifo會失敗,并提示File exists。
原理
1. 普通文件 vs. 命名管道(FIFO)
普通文件(如 a.txt):
- 數(shù)據(jù)存儲在 磁盤 上,打開時會加載到內(nèi)存。
- 修改后需要 寫回磁盤(刷盤)。
- 多個進(jìn)程打開同一文件時,數(shù)據(jù)可能不同步(除非加鎖)。
命名管道(FIFO):
- 本質(zhì)是一個 內(nèi)存緩沖區(qū),只是偽裝成文件(磁盤上只有文件名,沒有實(shí)際數(shù)據(jù)塊)。
- 數(shù)據(jù) 不寫入磁盤,直接在進(jìn)程間流動。
- 專為 進(jìn)程間通信(IPC) 設(shè)計,速度快。
2. 命名管道的工作原理
(1)創(chuàng)建 FIFO
當(dāng)執(zhí)行 mkfifo /tmp/myfifo 時:
- 磁盤上創(chuàng)建一個 空文件(只有 inode,沒有數(shù)據(jù)塊)。
- 操作系統(tǒng)內(nèi)核維護(hù)一個 內(nèi)存緩沖區(qū)(用于存儲進(jìn)程間傳遞的數(shù)據(jù))。
(2)進(jìn)程 A 寫入數(shù)據(jù)
echo "Hello" > /tmp/myfifo
- 進(jìn)程 A 打開 FIFO 以寫方式(
O_WRONLY)。 - 數(shù)據(jù)
"Hello"被寫入內(nèi)核的 內(nèi)存緩沖區(qū)(不刷盤!)。 - 如果 沒有進(jìn)程在讀取,進(jìn)程 A 會 阻塞(卡住),直到有進(jìn)程來讀。
(3)進(jìn)程 B 讀取數(shù)據(jù)
cat /tmp/myfifo
- 進(jìn)程 B 打開 FIFO 以讀方式(
O_RDONLY)。 - 從內(nèi)核緩沖區(qū)讀取
"Hello",數(shù)據(jù)被消費(fèi)(緩沖區(qū)清空)。 - 如果 沒有進(jìn)程在寫入,進(jìn)程 B 會 阻塞,直到有進(jìn)程寫入。
3. 關(guān)鍵點(diǎn)解析
(1)文件描述符與內(nèi)核結(jié)構(gòu)
每個進(jìn)程打開 FIFO 時,內(nèi)核會 復(fù)用同一個 struct file(通過引用計數(shù) ref 管理)。
- 第一次打開時創(chuàng)建
struct file,ref=1。 - 第二個進(jìn)程打開時,
ref=2(指向同一個內(nèi)存緩沖區(qū))。 - 關(guān)閉時
ref--,ref=0時內(nèi)核才釋放資源。
(2)為什么數(shù)據(jù)不刷盤?
- FIFO 的 設(shè)計目的 是進(jìn)程間通信,數(shù)據(jù)不需要持久化。
- 刷盤會 拖慢速度,且毫無意義(通信完數(shù)據(jù)即可丟棄)。
(3)半雙工通信
- 同一時間只能 單向流動(讀或?qū)懀?/li>
- 如果需要雙向通信,需創(chuàng)建 兩個 FIFO(一個負(fù)責(zé) A→B,一個負(fù)責(zé) B→A)。
4. 類比理解
把 FIFO 想象成 一條水管:
寫入端:進(jìn)程 A 往水管里倒水(數(shù)據(jù))。
讀取端:進(jìn)程 B 從水管接水(數(shù)據(jù))。
特性:
- 水管沒有儲水功能(數(shù)據(jù)不持久化)。
- 如果沒人接水,倒水的人會等待(阻塞寫入)。
- 如果沒人倒水,接水的人會等待(阻塞讀?。?。
總結(jié)
| 特性 | 命名管道(FIFO) | 普通文件 |
|---|---|---|
| 存儲位置 | 內(nèi)存緩沖區(qū) | 磁盤 |
| 刷盤 | 永不刷盤 | 定期刷盤 |
| 多進(jìn)程訪問 | 共享同一緩沖區(qū) | 可能數(shù)據(jù)不同步 |
| 阻塞行為 | 讀/寫會阻塞 | 無阻塞 |
| 用途 | 進(jìn)程間通信 | 數(shù)據(jù)存儲 |
簡單來說:
FIFO 是 披著文件外衣的內(nèi)存管道,讓進(jìn)程可以通過文件路徑名通信,數(shù)據(jù) 不落盤,速度極快!
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Linux環(huán)境下采用壓縮包方式安裝JDK 13的方法
JDK(Java Development Kit)是Sun公司(后被Oracle收購)推出的面向?qū)ο蟪绦蛟O(shè)計語言的開發(fā)工具包,擁有這個工具包之后我們就可以使用Java語言進(jìn)行程序設(shè)計和開發(fā)。這篇文章主要介紹了在Linux環(huán)境下采用壓縮包方式安裝JDK 13,需要的朋友可以參考下2019-10-10
Linux有限狀態(tài)機(jī)FSM的理解與實(shí)現(xiàn)
這篇文章主要幫助大家理解與實(shí)現(xiàn)Linux有限狀態(tài)機(jī)FSM,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
crontab執(zhí)行時間與系統(tǒng)時間不一致問題解決
這篇文章主要給大家介紹了關(guān)于crontab執(zhí)行時間與系統(tǒng)時間不一致問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
ubuntu20.04 LTS系統(tǒng)默認(rèn)源sources.list文件的修改
這篇文章主要介紹了ubuntu20.04 LTS系統(tǒng)默認(rèn)源sources.list文件的修改,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Linux系統(tǒng)設(shè)置開機(jī)自動運(yùn)行腳本的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Linux系統(tǒng)設(shè)置開機(jī)自動運(yùn)行腳本的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Linux系統(tǒng)具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
使用 Apache Web 服務(wù)器配置兩個或多個站點(diǎn)的方法
這篇文章主要介紹了使用 Apache Web 服務(wù)器配置多個站點(diǎn)的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2018-10-10
Linux安裝NodeJs并配合Nginx實(shí)現(xiàn)反向代理
本篇文章主要介紹了Linux安裝NodeJs并配合Nginx實(shí)現(xiàn)反向代理,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-11-11

