C++ pipe與buffer overflow

本篇主要講述C++中pipe()函數的相關行為

因為pipe的buffer大小並非無限大,如果輸入流資料量過大,很有可能會碰到buffer塞滿而輸入還沒結束的問題
寫了以下的code來測試pipe buffer如果塞爆了會發生甚麼事情

實驗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <vector>
#include <sstream>
#include <cstring>
using namespace std;
int main(int argc, char* argv[]){
// 創造pipe
int my_pipe[2];
if(pipe(my_pipe) == -1){
fprintf(stderr, "Error creating pipe\n");
}
//-------------------------
pid_t child_id;
child_id = fork();
if(child_id == -1){
fprintf(stderr, "Fork error\n");
}
// 創造一個子process,將buffer塞爆
if(child_id == 0)
{
close(my_pipe[0]);
dup2(my_pipe[1], 1);

string some = "something";
for(int i=0;i<1000000000000000;i++){
cout<<some;
}
cout<<"Chind,finished";
exit(0);
}
//---------------------------------
else
{
int aaa = fork();
// 再創造一個子process,讓他先sleep 4秒以後再嘗試在已經被塞爆的buffer寫入
if(aaa == 0){
sleep(4);
close(my_pipe[0]);
for(int i=0;i<1999;i++){

cout<<"Child2 round "<<i+1<<"\n";
write(my_pipe[1],"NP",sizeof("NP")); // is stuck because of the fullness of pipe
}
exit(0);
}
//----------------------------

// 原本的父process,先sleep 10秒讓我們觀察child process被塞爆的現象再read一些東西來疏通pipe,但這樣的量不足以讀完所有的input
else{

close(my_pipe[1]); // parent doesn't write

char reading_buf[1000]={'\0'};
sleep(10);
for(int i=0;i<10000;i++){
read(my_pipe[0], reading_buf, 100);
}
}
}
//等待第一次fork出來的子process
wait(&child_id);

//這行永遠不會印出來,因為子process永遠不會輸出完成而結束
cout<<my_pipe[0]<<my_pipe[1]; //3,4
return 0;
}

實際編譯執行後,出現以下結果
upload successful
Child2卡在第1897次輸出就寫不下去了,當然第一個process也是卡住的狀態(見Ps)

Pipe特性

從這個小實驗可以知道一些事情

  • pipe的buffer有限,是會被塞爆的

  • 原生的pipe如果被寫滿,會讓想繼續寫入的所有process被卡住不能繼續執行,直到pipe被讀取

    • 不可以wait process完整寫入發signal以後才fork另一個process再讀取,這樣很可能導致整個程式卡死
         - 如果卡住的process太多,可能會讓父process再也fork不出東西來讓本該讀取的process去接,造成整個程式卡死
  • 要注意針對大資料的處理,pipe應該要有個”抒發管道”,不能期待他真的裝下全世界

補充

我另外做了一個反向實驗:
讓輸入資料的process延遲輸入,而讀取的process開始讀空的pipe,得到以下心得

  • 讀取端會等read(fd,char* buffer,int size)裡面的size被填滿才會執行接下來的動作
    • 想到以前上課老師說的,只要還有process 對pipe的輸入端還未關閉,讀取端就會一直認為有東西要進來,而呆呆地等 -> 關不用的stream很重要!

P.S
測試這個東西的時候還有用背景執行,創造了不少卡著的process

其中相連的pid可以看出來是同一個程式來的,原本的父process,連帶所有child process都無法被正常結束…