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

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

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

命名管道
簡(jiǎn)介
- 匿名管道,它的通信范圍是存在父子關(guān)系的進(jìn)程。因?yàn)楣艿罌](méi)有實(shí)體,也就是沒(méi)有管道文件,只能通過(guò)fork來(lái)復(fù)制父進(jìn)程 fd文件描述符,來(lái)達(dá)到通信的目的。
- 對(duì)于命名管道,它可以在不相關(guān)的進(jìn)程間也能相互通信。因?yàn)槊罟艿?提前創(chuàng)建了一個(gè)類(lèi)型為管道的設(shè)備文件,在進(jìn)程里只要使用這個(gè)設(shè)備文件,就可以相互通信。
- 不管是匿名管道還是命名管道,進(jìn)程寫(xiě)入的數(shù)據(jù)都是緩存在內(nèi)核中,另一個(gè)進(jìn)程讀取數(shù)據(jù)時(shí)候自然也是從內(nèi)核中獲取,同時(shí)通信數(shù)據(jù)都遵循先進(jìn)先出原則,不支持Iseek之類(lèi)的文件定位操作。
使用
int mkfifo(const char *pathname, mode_t mode);
創(chuàng)建 FIFO
使用 mkfifo 命令或 mkfifo() 函數(shù)創(chuàng)建:
mkfifo /tmp/myfifo # 創(chuàng)建一個(gè)名為 /tmp/myfifo 的 FIFO
pathname:FIFO 的文件路徑(如/tmp/myfifo)。mode:權(quán)限(如0644,表示用戶(hù)可讀寫(xiě),其他人只讀)。
FIFO 的特點(diǎn)
半雙工通信
- 同一時(shí)間只能 讀 或 寫(xiě),不能同時(shí)讀寫(xiě)(類(lèi)似單行道)。
- 示例:
# 終端1:寫(xiě)入數(shù)據(jù) echo "Hello" > /tmp/myfifo # 終端2:讀取數(shù)據(jù) cat /tmp/myfifo # 輸出 "Hello"
阻塞機(jī)制
- 如果沒(méi)有進(jìn)程在 讀,寫(xiě)操作會(huì) 卡住,直到有進(jìn)程來(lái)讀。
- 如果沒(méi)有進(jìn)程在 寫(xiě),讀操作會(huì) 卡住,直到有進(jìn)程來(lái)寫(xiě)。
數(shù)據(jù)是流式的
- 數(shù)據(jù)像水流一樣,讀完后會(huì)消失,不能像普通文件那樣隨意跳轉(zhuǎn)(
lseek無(wú)效)。
如何使用 FIFO?
(1)命令行測(cè)試
# 終端1:監(jiān)聽(tīng) FIFO(讀) cat /tmp/myfifo # 終端2:發(fā)送數(shù)據(jù)(寫(xiě)) echo "Hello FIFO" > /tmp/myfifo
終端1 會(huì)顯示 Hello FIFO。
(2)C 語(yǔ)言示例
#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); // 以寫(xiě)方式打開(kāi)
write(fd, "Hello", 6); // 寫(xiě)入數(shù)據(jù)
close(fd);
return 0;
}
常見(jiàn)問(wèn)題
Q1: FIFO 和普通文件有什么區(qū)別?
- FIFO 是 內(nèi)存中的管道,數(shù)據(jù)讀完就沒(méi)了;普通文件會(huì)持久化存儲(chǔ)。
Q2: FIFO 和匿名管道(|)有什么區(qū)別?
- 匿名管道只能用于 父子進(jìn)程,F(xiàn)IFO 可用于 任意進(jìn)程。
Q3: 如果 FIFO 已經(jīng)存在,再創(chuàng)建會(huì)怎樣?
mkfifo會(huì)失敗,并提示File exists。
原理
1. 普通文件 vs. 命名管道(FIFO)
普通文件(如 a.txt):
- 數(shù)據(jù)存儲(chǔ)在 磁盤(pán) 上,打開(kāi)時(shí)會(huì)加載到內(nèi)存。
- 修改后需要 寫(xiě)回磁盤(pán)(刷盤(pán))。
- 多個(gè)進(jìn)程打開(kāi)同一文件時(shí),數(shù)據(jù)可能不同步(除非加鎖)。
命名管道(FIFO):
- 本質(zhì)是一個(gè) 內(nèi)存緩沖區(qū),只是偽裝成文件(磁盤(pán)上只有文件名,沒(méi)有實(shí)際數(shù)據(jù)塊)。
- 數(shù)據(jù) 不寫(xiě)入磁盤(pán),直接在進(jìn)程間流動(dòng)。
- 專(zhuān)為 進(jìn)程間通信(IPC) 設(shè)計(jì),速度快。
2. 命名管道的工作原理
(1)創(chuàng)建 FIFO
當(dāng)執(zhí)行 mkfifo /tmp/myfifo 時(shí):
- 磁盤(pán)上創(chuàng)建一個(gè) 空文件(只有 inode,沒(méi)有數(shù)據(jù)塊)。
- 操作系統(tǒng)內(nèi)核維護(hù)一個(gè) 內(nèi)存緩沖區(qū)(用于存儲(chǔ)進(jìn)程間傳遞的數(shù)據(jù))。
(2)進(jìn)程 A 寫(xiě)入數(shù)據(jù)
echo "Hello" > /tmp/myfifo
- 進(jìn)程 A 打開(kāi) FIFO 以寫(xiě)方式(
O_WRONLY)。 - 數(shù)據(jù)
"Hello"被寫(xiě)入內(nèi)核的 內(nèi)存緩沖區(qū)(不刷盤(pán)?。?。 - 如果 沒(méi)有進(jìn)程在讀取,進(jìn)程 A 會(huì) 阻塞(卡?。?,直到有進(jìn)程來(lái)讀。
(3)進(jìn)程 B 讀取數(shù)據(jù)
cat /tmp/myfifo
- 進(jìn)程 B 打開(kāi) FIFO 以讀方式(
O_RDONLY)。 - 從內(nèi)核緩沖區(qū)讀取
"Hello",數(shù)據(jù)被消費(fèi)(緩沖區(qū)清空)。 - 如果 沒(méi)有進(jìn)程在寫(xiě)入,進(jìn)程 B 會(huì) 阻塞,直到有進(jìn)程寫(xiě)入。
3. 關(guān)鍵點(diǎn)解析
(1)文件描述符與內(nèi)核結(jié)構(gòu)
每個(gè)進(jìn)程打開(kāi) FIFO 時(shí),內(nèi)核會(huì) 復(fù)用同一個(gè) struct file(通過(guò)引用計(jì)數(shù) ref 管理)。
- 第一次打開(kāi)時(shí)創(chuàng)建
struct file,ref=1。 - 第二個(gè)進(jìn)程打開(kāi)時(shí),
ref=2(指向同一個(gè)內(nèi)存緩沖區(qū))。 - 關(guān)閉時(shí)
ref--,ref=0時(shí)內(nèi)核才釋放資源。
(2)為什么數(shù)據(jù)不刷盤(pán)?
- FIFO 的 設(shè)計(jì)目的 是進(jìn)程間通信,數(shù)據(jù)不需要持久化。
- 刷盤(pán)會(huì) 拖慢速度,且毫無(wú)意義(通信完數(shù)據(jù)即可丟棄)。
(3)半雙工通信
- 同一時(shí)間只能 單向流動(dòng)(讀或?qū)懀?/li>
- 如果需要雙向通信,需創(chuàng)建 兩個(gè) FIFO(一個(gè)負(fù)責(zé) A→B,一個(gè)負(fù)責(zé) B→A)。
4. 類(lèi)比理解
把 FIFO 想象成 一條水管:
寫(xiě)入端:進(jìn)程 A 往水管里倒水(數(shù)據(jù))。
讀取端:進(jìn)程 B 從水管接水(數(shù)據(jù))。
特性:
- 水管沒(méi)有儲(chǔ)水功能(數(shù)據(jù)不持久化)。
- 如果沒(méi)人接水,倒水的人會(huì)等待(阻塞寫(xiě)入)。
- 如果沒(méi)人倒水,接水的人會(huì)等待(阻塞讀?。?。
總結(jié)
| 特性 | 命名管道(FIFO) | 普通文件 |
|---|---|---|
| 存儲(chǔ)位置 | 內(nèi)存緩沖區(qū) | 磁盤(pán) |
| 刷盤(pán) | 永不刷盤(pán) | 定期刷盤(pán) |
| 多進(jìn)程訪(fǎng)問(wèn) | 共享同一緩沖區(qū) | 可能數(shù)據(jù)不同步 |
| 阻塞行為 | 讀/寫(xiě)會(huì)阻塞 | 無(wú)阻塞 |
| 用途 | 進(jìn)程間通信 | 數(shù)據(jù)存儲(chǔ) |
簡(jiǎn)單來(lái)說(shuō):
FIFO 是 披著文件外衣的內(nèi)存管道,讓進(jìn)程可以通過(guò)文件路徑名通信,數(shù)據(jù) 不落盤(pán),速度極快!
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
ubuntu運(yùn)行g(shù)cfsd-admin守護(hù)進(jìn)程需要認(rèn)證的解決方案
在Ubuntu中運(yùn)行`gcfsd-admin`守護(hù)進(jìn)程時(shí),如果每次進(jìn)入都需要輸入密碼,可以通過(guò)重新掛載`/data`磁盤(pán)來(lái)解決這個(gè)問(wèn)題,總結(jié)來(lái)說(shuō),需要對(duì)磁盤(pán)進(jìn)行重新掛載以消除鎖子2026-01-01
apache負(fù)載均衡的安裝和實(shí)現(xiàn)方法
在負(fù)載均衡技術(shù)中,硬件設(shè)備是比較昂貴的,對(duì)于負(fù)載均衡的學(xué)習(xí)者如果不是在企業(yè)中應(yīng)用或者是學(xué)員中學(xué)習(xí),很少有機(jī)會(huì)能碰到實(shí)際操作的訓(xùn)練。所以,很多朋友都會(huì)選擇軟件方面的設(shè)置進(jìn)行研究。現(xiàn)在我們就來(lái)介紹一下再Apache下的Tomcat負(fù)載均衡的一些使用問(wèn)題2012-10-10
Linux中出現(xiàn)“No space left on device”錯(cuò)誤的排查與解決方法
這篇文章主要給大家介紹了關(guān)于在Linux中出現(xiàn)"No space left on device"錯(cuò)誤的排查與解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-09-09
Linux(Ubuntu) adb 無(wú)法識(shí)別的問(wèn)題解決方法
這篇文章主要介紹了Linux(Ubuntu) adb 無(wú)法識(shí)別的問(wèn)題解決方法的相關(guān)資料,需要的朋友可以參考下2016-11-11
Linux環(huán)境實(shí)現(xiàn)替換jar包中class文件
文章介紹了如何通過(guò)替換class文件來(lái)避免重復(fù)打包的問(wèn)題,具體步驟包括查找目標(biāo)文件目錄、解壓目標(biāo)文件、替換已解壓文件、替換jar包文件等2025-11-11
基于windowx的Hyper-v安裝CentOS系統(tǒng)
這篇文章主要介紹了基于windowx的Hyper-v安裝CentOS系統(tǒng) ,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07
linux系列之常用運(yùn)維命令整理筆錄(小結(jié))
這篇文章主要介紹了linux系列之常用運(yùn)維命令整理筆錄(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01

