UNIX 下 C 實(shí)現(xiàn) Pipe Descriptors 映射

翻譯整理自 http://unixwiz.net/techtips/remap-pipe-fds.html

在UNIX中畦贸,通過pipe()可以創(chuàng)建一對(duì)單向的pipe descriptors陨闹;通過fork()可以創(chuàng)建一個(gè)子進(jìn)程(即創(chuàng)建一個(gè)當(dāng)前進(jìn)程的“鏡像”),兩者初始時(shí)狀態(tài)相同薄坏,執(zhí)行互不干擾趋厉。利用這兩點(diǎn),我們可以實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)交互胶坠,進(jìn)而可以將一個(gè)進(jìn)程的幾個(gè)standard POSIX file通過dup2()函數(shù)被自定義的pipe所映射觅廓。這里主要介紹該實(shí)現(xiàn)原理和幾個(gè)注意點(diǎn)以避免descriptor之間產(chǎn)生沖突引起的I/O errors。

UNIX Pipes

pipe(int fd[2])函數(shù)可得到兩個(gè)file descriptors形成一個(gè)單向的pipe涵但,fd[0]固定為讀端杈绸,fd[1]固定為寫端。

單向pipe

通常pipe作為進(jìn)程間的讀寫通道矮瘟,并不會(huì)用在單獨(dú)的一個(gè)進(jìn)程中瞳脓。

The "usual" way

UNIX pipe由一對(duì)file descriptors組成造挽,即一端寫汤徽,一端讀。對(duì)于單進(jìn)程來說并沒有意義厌漂,但可以作為多個(gè)進(jìn)程之間的數(shù)據(jù)通道哨啃,由于UNIX pipe是單向的烧栋,所以在數(shù)據(jù)交換時(shí)常會(huì)用到多個(gè)pipe。例如兩個(gè)進(jìn)程之間需要完成數(shù)據(jù)的交互拳球,那么就需要兩個(gè)pipe审姓,即四個(gè)file descriptors。
使用舉例如下:

...

int writepipe[2] = {-1,-1}, /* parent -> child */
    readpipe [2] = {-1,-1}; /* child -> parent */
pid_t   childpid;

/*------------------------------------------------------------------------
 * CREATE THE PAIR OF PIPES
 *
 * Pipes have two ends but just one direction: to get a two-way
 * conversation you need two pipes. It's an error if we cannot make
 * them both, and we define these macros for easy reference.
 */
writepipe[0] = -1;

if ( pipe(readpipe) < 0  ||  pipe(writepipe) < 0 )
{
    /* FATAL: cannot create pipe */
    /* close readpipe[0] & [1] if necessary */
}

#define PARENT_READ readpipe[0]
#define CHILD_WRITE readpipe[1]
#define CHILD_READ  writepipe[0]
#define PARENT_WRITE    writepipe[1]

if ( (childpid = fork()) < 0)
{
    /* FATAL: cannot fork child */
}
else if ( childpid == 0 )   /* in the child */
{
    close(PARENT_WRITE);
    close(PARENT_READ);

    dup2(CHILD_READ,  0);  close(CHILD_READ);
    dup2(CHILD_WRITE, 1);  close(CHILD_WRITE);

    /* do child stuff */
}
else                /* in the parent */
{
    close(CHILD_READ);
    close(CHILD_WRITE);

    /* do parent stuff */
}

descriptors崩潰

在pipe(fd)執(zhí)行時(shí)祝峻,通常會(huì)為fd分配兩個(gè)當(dāng)前最小且可用的值作為文件描述符放入fd[2]數(shù)組魔吐;正常情況下不會(huì)造成問題扎筒,但如果恰好此時(shí)FD#0和#1(stdin, stdout)均可用且在該狀態(tài)下是最小的,那么這兩個(gè)描述符會(huì)被分配到fd中酬姆。此時(shí)嗜桌,若進(jìn)行上述的dup2操作,會(huì)產(chǎn)生矛盾辞色,先看下面一段代碼:

#define PARENT_READ  pipe1[0]   /* fd#0 */
#define CHILD_WRITE  pipe1[1]   /* fd#1 */
#define CHILD_READ   pipe2[0]   /* fd#2 */
#define PARENT_WRITE pipe2[1]   /* fd#3 */

dup2(CHILD_READ=2,  0);  close(CHILD_READ=2);
dup2(CHILD_WRITE=1, 1);  close(CHILD_WRITE=1);

假如為pipe1分配了 FD#0 #1骨宠,為pipe2分配了FD#2 #3,而第一個(gè)dup2函數(shù)又將pipe2[0](CHILD_READ)指向FD#0相满,即指向了pipe[0](PARENT_READ)层亿。后臺(tái)守護(hù)進(jìn)程檢測到PARENT_READ不再工作,便會(huì)關(guān)閉PARENT_READ雳灵。接下來CHILD_READ也被關(guān)閉棕所,那么此時(shí)PARENT進(jìn)程的read和write操作將會(huì)不再有意義。

Getting it right

如果要進(jìn)行映射的file descriptor的值和目標(biāo)均不同悯辙,(即所有dup2(souce, target)調(diào)用中souces和targets的值均不同)琳省,那么可以按照上述代碼片段1進(jìn)行file descriptor映射,否則就需要重新設(shè)計(jì)調(diào)用邏輯躲撰。
這里我們以上述的#0#1(stdin, stdout)為例针贬,需要考慮souce這一file descriptor的三種情況:

  • 0 - 可能造成沖突
  • 1 - 也可能造成沖突
  • >1 - 安全

考慮到一個(gè)pipe的兩端(同一個(gè)pipe的read和write文件描述符)值是不會(huì)相等的,可以通過一個(gè)表格來審計(jì)所有需要考慮的操作拢蛋。

#define DUP2CLOSE(oldfd, newfd) ( dup2(oldfd, newfd), close(oldfd) )

每個(gè)特定情況下所需的操作如下表:

| read FD (should be 0) | write FD (should be 1) | Action |
| :-----------: | :-------------: | : ----- |
| 0 | 1 | nothing - it's already done |
| >=1 | >1 | DUP2CLOSE(rfd, 0); DUP2CLOSE(wfd, 1); |
| 0 | >1 | DUP2CLOSE(wfd, 1); |
| >1 | 1 | DUP2CLOSE(rfd, 0); |
| >1 | 0 | DUP2CLOSE(wfd, 1); DUP2CLOSE(rfd, 0); |
| 1 | 0 | tmp = dup(wfd); close(wfd); DUP2CLOSE(rfd, 0); DUP2CLOSE(tmp, 1); |

在類似的映射操作中桦他,通過上述遍歷情況矩陣的形式審計(jì)映射操作會(huì)大大減少錯(cuò)誤的發(fā)生。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谆棱,一起剝皮案震驚了整個(gè)濱河市快压,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垃瞧,老刑警劉巖蔫劣,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異个从,居然都是意外死亡脉幢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門嗦锐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫌松,“玉大人,你說我怎么就攤上這事奕污∥幔” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵菊值,是天一觀的道長外驱。 經(jīng)常有香客問我育灸,道長腻窒,這世上最難降的妖魔是什么昵宇? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮儿子,結(jié)果婚禮上瓦哎,老公的妹妹穿的比我還像新娘。我一直安慰自己柔逼,他們只是感情好蒋譬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愉适,像睡著了一般犯助。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上维咸,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天剂买,我揣著相機(jī)與錄音,去河邊找鬼癌蓖。 笑死瞬哼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的租副。 我是一名探鬼主播坐慰,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼用僧!你這毒婦竟也來了结胀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤责循,失蹤者是張志新(化名)和其女友劉穎糟港,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沼死,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡着逐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了意蛀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸别。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖县钥,靈堂內(nèi)的尸體忽然破棺而出秀姐,到底是詐尸還是另有隱情,我是刑警寧澤若贮,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布省有,位于F島的核電站痒留,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蠢沿。R本人自食惡果不足惜伸头,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舷蟀。 院中可真熱鬧恤磷,春花似錦、人聲如沸野宜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匈子。三九已至河胎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虎敦,已是汗流浹背游岳。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留原茅,地道東北人吭历。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像擂橘,于是被迫代替她去往敵國和親晌区。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容