<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [apue] 神奇的 Solaris pipe

      說到 pipe 大家可能都不陌生,經典的pipe調用配合fork進行父子進程通訊,簡直就是Unix程序的標配。

      然而Solaris上的pipe卻和Solaris一樣是個奇葩(雖然Solaris前途黯淡,但是不妨礙我們從它里面挖掘一些有價值的東西),

      有著和一般pipe諸多的不同之處,本文就來說說Solaris上神奇的pipe和一般pipe之間的異同。

       

      1.solaris pipe 是全雙工的

      一般系統上的pipe調用是半雙工的,只能單向傳遞數據,如果需要雙向通訊,我們一般是建兩個pipe分別讀寫。像下面這樣:

       1     int n, fd1[2], fd2[2]; 
       2     if (pipe (fd1) < 0 || pipe(fd2) < 0)
       3         err_sys ("pipe error"); 
       4 
       5     char line[MAXLINE]; 
       6     pid_t pid = fork (); 
       7     if (pid < 0) 
       8         err_sys ("fork error"); 
       9     else if (pid > 0)
      10     {
      11         close (fd1[0]);  // write on pipe1 as stdin for co-process
      12         close (fd2[1]);  // read on pipe2 as stdout for co-process
      13         while (fgets (line, MAXLINE, stdin) != NULL) { 
      14             n = strlen (line); 
      15             if (write (fd1[1], line, n) != n)
      16                 err_sys ("write error to pipe"); 
      17             if ((n = read (fd2[0], line, MAXLINE)) < 0)
      18                 err_sys ("read error from pipe"); 
      19 
      20             if (n == 0) { 
      21                 err_msg ("child closed pipe"); 
      22                 break;
      23             }
      24             line[n] = 0; 
      25             if (fputs (line, stdout) == EOF)
      26                 err_sys ("fputs error"); 
      27         }
      28 
      29         if (ferror (stdin))
      30             err_sys ("fputs error"); 
      31 
      32         return 0; 
      33     }
      34     else { 
      35         close (fd1[1]); 
      36         close (fd2[0]); 
      37         if (fd1[0] != STDIN_FILENO) { 
      38             if (dup2 (fd1[0], STDIN_FILENO) != STDIN_FILENO)
      39                 err_sys ("dup2 error to stdin"); 
      40             close (fd1[0]); 
      41         }
      42 
      43         if (fd2[1] != STDOUT_FILENO) { 
      44             if (dup2 (fd2[1], STDOUT_FILENO) != STDOUT_FILENO)
      45                 err_sys ("dup2 error to stdout"); 
      46             close (fd2[1]); 
      47         }
      48 
      49         if (execl (argv[1], "add2", (char *)0) < 0)
      50             err_sys ("execl error"); 
      51     }

      這個程序創建兩個管道,fd1用來寫請求,fd2用來讀應答;對子進程而言,fd1重定向到標準輸入,fd2重定向到標準輸出,讀取stdin中的數據相加然后寫入stdout完成工作。父進程在取得應答后向標準輸出寫入結果。

      如果在Solaris上,可以直接用一個pipe同時讀寫,代碼可以重寫成這樣:

       1 int fd[2];
       2 if (pipe(fd) < 0) 
       3     err_sys("pipe error\n");
       4 
       5 char line[MAXLINE];
       6 pid_t pid = fork();
       7 if (pid < 0)
       8     err_sys("fork error\n");
       9 else if (pid > 0)
      10 {
      11     close(fd[1]);
      12     while (fgets(line, MAXLINE, stdin) != NULL) {
      13         n = strlen(line);
      14         if (write(fd[0], line, n) != n)
      15             err_sys("write error to pipe\n")
      16         if ((n = read(fd[0], line, MAXLINE)) < 0) 
      17             err_sys("read error from pipe\n");
      18 
      19         if (n == 0) 
      20             err_sys("child closed pipe\n");
      21         line[n] = 0;
      22         if (fputs(line, stdout) == EOF) 
      23             err_sys("fputs error\n");
      24     }
      25 
      26     if (ferror(stdin))
      27         err_sys("fputs error\n");
      28 
      29     return 0;
      30 }
      31 else {
      32     close(fd[0]);
      33     if (fd[1] != STDIN_FILENO)
      34         if (dup2(fd[1], STDIN_FILENO) != STDIN_FILENO)
      35             err_sys("dup2 error to stdin\n");
      36 
      37     if (fd[1] != STDOUT_FILENO) {
      38         if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
      39             err_sys("dup2 error to stdout\n");
      40         close(fd[1]);
      41     }
      42 
      43     if (execl(argv[1], argv[2], (char *)0) < 0)
      44         err_sys("execl error\n");
      45 
      46 }

      代碼清爽多了,不用去考慮fd1[0]和fd2[1]是啥意思是一件很養腦的事。

      不過這樣的代碼只能在Solaris上運行(聽說BSD也支持?),如果考慮到可移植性,還是寫上面的比較穩妥。

       

      測試程序

      padd2.c 

      add2.c

       

       

      2. solaris pipe 可以脫離父子關系建立

      pipe 好用但是沒法脫離fork使用,一般的pipe如果想讓任意兩個進程通訊,得借助它的變身fifo來實現。

      關于FIFO,詳情可參考我之前寫的一篇文章:

      [apue] FIFO:不是文件的文件

       

      而Solaris上的pipe沒這么多事,加入兩個調用:fattach / fdetach,你就可以像使用FIFO一樣使用pipe了:

       1 int fd[2];
       2 if (pipe(fd) < 0)
       3     err_sys("pipe error\n");
       4 
       5 if (fattach(fd[1], "./pipe") < 0)
       6     err_sys("fattach error\n");
       7 
       8 printf("attach to file pipe ok\n");
       9 
      10 close(fd[1]);
      11 char line[MAXLINE];
      12 while (fgets(line, MAXLINE, stdin) != NULL) {
      13     n = strlen(line);
      14     if (write(fd[0], line, n) != n)
      15         err_sys("write error to pipe\n");
      16     if ((n = read(fd[0], line, MAXLINE)) < 0)
      17         err_sys("read error from pipe\n");
      18 
      19     if (n == 0) 
      20         err_sys("child closed pipe\n");
      21 
      22     line[n] = 0;
      23     if (fputs(line, stdout) == EOF)
      24         err_sys("fputs error\n");
      25 }
      26 
      27 if (ferror(stdin))
      28     err_sys("fputs error\n");
      29 
      30 if (fdetach("./pipe") < 0)
      31     err_sys("fdetach error\n");
      32 
      33 printf("detach from file pipe ok\n");

      在pipe調用之后立即加入fattach調用,可以將管道關聯到文件系統的一個文件名上,該文件必需事先存在,且可讀可寫。

      在fattach調用之前這個文件(./pipe)是個普通文件,打開讀寫都是磁盤IO;

      在fattach調用之后,這個文件就變身成為一個管道了,打開讀寫都是內存流操作,且管道的另一端就是attach的那個進程。

      子進程也需要改造一下,以便使用pipe通訊:

       1 int fd, n, int1, int2;
       2 char line[MAXLINE];
       3 fd = open("./pipe", O_RDWR);
       4 if (fd < 0)
       5     err_sys("open file pipe failed\n");
       6 
       7 printf("open file pipe ok, fd = %d\n", fd);
       8 while ((n = read(fd, line, MAXLINE)) > 0) {
       9     line[n] = 0;
      10     if (sscanf(line, "%d%d", &int1, &int2) == 2) {
      11         sprintf(line, "%d\n", int1 + int2);
      12         n = strlen(line);
      13         if (write(fd, line, n) != n)
      14             err_sys("write error\n");
      15 
      16         printf("i am working on %s\n", line);
      17     }
      18     else {
      19         if (write(fd, "invalid args\n", 13) != 13)
      20             err_sys("write msg error\n");
      21     }
      22 }
      23 
      24 close(fd);

      打開pipe就如同打開普通文件一樣,open直接搞定。當然前提是attach進程必需已經在運行。

      當attach進程detach后,管道文件又將恢復它的本來面目。

       

      脫離了父子關系的pipe其實可以建立多對一關系(多對多關系不可以,因為只能有一個進程attach)。

      例如開4個cmd窗口,分別執行以下命令:

      ./padd2 abc
      ./add2
      ./add2
      ./add2
      

       向attach進程(padd2)發送9個計算請求后,可以看到輸出結果如下:

      -bash-3.2$ ./padd2 abc
      attach to file pipe ok
      1 1
      2
      2 2
      4
      3 3 
      6
      4 4
      8
      5 5
      10
      6 6 
      12
      7 7 
      14
      8 8
      16
      9 9
      18
      

       再回來看各個open管道的進程,輸出分別如下:

      -bash-3.2$ ./add2
      open file pipe ok, fd = 3
      source: 1 1
      i am working on 2
      source: 4 4
      i am working on 8
      source: 7 7 
      i am working on 14 
      

       

      -bash-3.2$ ./add2
      open file pipe ok, fd = 3
      source: 2 2
      i am working on 4
      source: 5 5
      i am working on 10
      source: 9 9
      i am working on 18 
      

       

      -bash-3.2$ ./add2
      open file pipe ok, fd = 3
      source: 2 2
      i am working on 4
      source: 5 5
      i am working on 10
      source: 9 9
      i am working on 18 
      

       

      -bash-3.2$ ./add2
      open file pipe ok, fd = 3
      source: 3 3
      i am working on 6
      source: 6 6
      i am working on 12
      source: 8 8 
      i am working on 16
      

       

      可以發現一個很有趣的現象,就是各個add2進程基本是輪著來獲取請求的,可以猜想底層的pipe可能有一個進程排隊機制。

      但是反過來使用pipe就不行了。就是說當啟動一個add3(區別于上例的add2與padd2)作為fattach端打開pipe,啟動多個padd3作為open端使用pipe,

      然后通過命令行給padd3傳遞要相加的值,可以寫一個腳本同時啟動多個padd3,來查看效果:

      #! /bin/sh
      ./padd3 1 1 &
      ./padd3 2 2 &
      ./padd3 3 3 &
      ./padd3 4 4 &
      

       這個腳本中啟動了4個加法進程,同時向add3發送4個加法請求,腳本中四個進程輸出如下:

      -bash-3.2$ ./padd3.sh
      -bash-3.2$ open file pipe ok, fd = 3
      1 1 = 2
      open file pipe ok, fd = 3
      2 2 = 4
      open file pipe ok, fd = 3
      open file pipe ok, fd = 3
      4 4 = 37
      

       可以看到3+3的請求被忽略了,轉到add3查看輸出:

      -bash-3.2$ ./add3
      attach to file pipe ok
      source: 1 1
      i am working on 1 + 1 = 2
      source: 2 2
      i am working on 2 + 2 = 4
      source: 3 34 4
      i am working on 3 + 34 = 37
      

       原來是3+3與4+4兩個請求粘連了,導致add3識別成一個3+34的請求,所以出錯了。

      多運行幾遍腳本后,發現還有這樣的輸出:

      -bash-3.2$ ./padd3.sh
      -bash-3.2$ open file pipe ok, fd = 3
      4 4 = 2
      open file pipe ok, fd = 3
      2 2 = 4
      open file pipe ok, fd = 3
      3 3 = 6
      open file pipe ok, fd = 3
      1 1 = 8
      

        4+4=2?1+1=8?再看add3這頭的輸出:

      -bash-3.2$ ./add3
      attach to file pipe ok
      source: 1 1
      i am working on 1 + 1 = 2
      source: 2 2
      i am working on 2 + 2 = 4
      source: 3 3
      i am working on 3 + 3 = 6
      source: 4 4
      i am working on 4 + 4 = 8
      

       完全正常呢。

      經過一番推理,發現是4+4的請求取得了1+1請求的應答;1+1的請求取得了4+4的應答。

      可見這樣的結構還有一個弊端,同時請求的進程可能無法得到自己的應答,應答與請求之間相互錯位。

      所以想用fattach來實現多路請求的人還是洗洗睡吧,畢竟它就是一個pipe不是,還能給它整成tcp么?

      而之前的例子可以,是因為請求是順序發送的,上個請求得到應答后才發送下個請求,所以不存在這個例子的問題(但是實用性也不高)。

       

      測試程序

      padd3.c

      add3.c

       

       

      3. solaris pipe 可以通過connld模塊實現類似tcp的多路連接

      第2條剛說不能實現多路連接,第3條就接著來打臉了,這是由于Solaris上的pipe都是基于STREAMS技術構建,

      而STREAMS是支持靈活的PUSH、POP流處理模塊的,再加上STREAMS傳遞文件fd的能力,就可以支持類似tcp中accept的能力。

      即每個open pipe文件的進程,得到的不是原來管道的fd,而是新創建管道的fd,而管道的另一側fd則通過已有的管道發送到attach進程,

      后者使用這個新的fd與客戶進程通訊。為了支持多路連接,我們的代碼需要重新整理一下,首先看客戶端:

      1 int fd;
      2 char line[MAXLINE];
      3 fd = cli_conn("./pipe");
      4 if (fd < 0)
      5     return 0;

      這里將open相關邏輯封裝到了cli_conn函數中,以便之后復用:

       1 int cli_conn(const char *name)
       2 {
       3     int fd;
       4     if ((fd = open(name, O_RDWR)) < 0) {
       5         printf("open pipe file failed\n");
       6         return -1;
       7     }
       8 
       9     if (isastream(fd) == 0) {
      10         close(fd);
      11         return -2;
      12     }
      13 
      14     return fd;
      15 }

      可以看到與之前幾乎沒有變化,只是增加了isastream調用防止attach進程沒有啟動。

      再來看下服務端:

       1 int listenfd = serv_listen("./pipe");
       2 if (listenfd < 0)
       3     return 0;
       4 
       5 int acceptfd = 0;
       6 int n = 0, int1 = 0, int2 = 0;
       7 char line[MAXLINE];
       8 uid_t uid = 0;
       9 while ((acceptfd = serv_accept(listenfd, &uid)) >= 0)
      10 {
      11     printf("accept a client, fd = %d, uid = %ld\n", acceptfd, uid);
      12     while ((n = read(acceptfd, line, MAXLINE)) > 0) {
      13         line[n] = 0;
      14         printf("source: %s\n", line);
      15         if (sscanf(line, "%d%d", &int1, &int2) == 2) {
      16             sprintf(line, "%d\n", int1 + int2);
      17             n = strlen(line);
      18             if (write(acceptfd, line, n) != n) {
      19                 printf("write error\n");
      20                 return 0;
      21             }
      22             printf("i am working on %d + %d = %s\n", int1, int2, line);
      23         }
      24         else {
      25             if (write(acceptfd, "invalid args\n", 13) != 13) {
      26                 printf("write msg error\n");
      27                 return 0;
      28             }
      29         }
      30     }
      31 
      32     close(acceptfd);
      33 }
      34 
      35 if (fdetach("./pipe") < 0) {
      36     printf("fdetach error\n");
      37     return 0;
      38 }
      39 
      40 printf("detach from file pipe ok\n");
      41 close(listenfd);

      首先調用serv_listen建立基本pipe,然后不斷在該pipe上調用serv_accept來獲取獨立的客戶端連接。之后的邏輯與以前一樣。

      現在重點看下封裝的這兩個方法:

       1 int serv_listen(const char *name)
       2 {
       3     int tempfd;
       4     int fd[2];
       5     unlink(name);
       6     tempfd = creat(name, FIFO_MODE);
       7     if (tempfd < 0) {
       8         printf("creat failed\n");
       9         return -1;
      10     }
      11 
      12     if (close(tempfd) < 0) {
      13         printf("close temp fd failed\n");
      14         return -2;
      15     }
      16 
      17     if (pipe(fd) < 0) {
      18         printf("pipe error\n");
      19         return -3;
      20     }
      21 
      22     if (ioctl(fd[1], I_PUSH, "connld") < 0) {
      23         printf("I_PUSH connld failed\n");
      24         close(fd[0]);
      25         close(fd[1]);
      26         return -4;
      27     }
      28 
      29     printf("push connld ok\n");
      30     if (fattach(fd[1], name) < 0) {
      31         printf("fattach error\n");
      32         close(fd[0]);
      33         close(fd[1]);
      34         return -5;
      35     }
      36 
      37     printf("attach to file pipe ok\n");
      38     close(fd[1]);
      39     return fd[0];
      40 }

      serv_listen封裝了與建立基本pipe相關的代碼,首先確保pipe文件存在且可讀寫,然后創建普通的pipe,在fattach調用之前必需先PUSH一個connld模塊到該pipe STREAM中。這樣就大功告成!

       1 int serv_accept(int listenfd, uid_t *uidptr)
       2 {
       3     struct strrecvfd recvfd;
       4     if (ioctl(listenfd, I_RECVFD, &recvfd) < 0) {
       5         printf("I_RECVFD from listen fd failed\n");
       6         return -1;
       7     }
       8 
       9     if (uidptr)
      10         *uidptr = recvfd.uid;
      11 
      12     return recvfd.fd;
      13 }

      當有客戶端連接上來的時候,使用I_RECVFD接收connld返回的另一個pipe的fd。之后的數據將在該pipe進行。

      看了看,感覺和tcp的listen與accept別無二致,看來天下武功,至精深處都是英雄所見略同。

      之前的多個客戶端同時運行的例子再跑一遍,觀察attach端輸出:

      -bash-3.2$ ./add4
      push connld ok
      attach to file pipe ok
      accept a client, fd = 4, uid = 101
      source: 1 1
      i am working on 1 + 1 = 2
      accept a client, fd = 4, uid = 101
      source: 2 2
      i am working on 2 + 2 = 4
      accept a client, fd = 4, uid = 101
      source: 3 3
      i am working on 3 + 3 = 6
      accept a client, fd = 4, uid = 101
      source: 4 4
      i am working on 4 + 4 = 8
      

       一切正常。再看下腳本中四個進程的輸出:

      -bash-3.2$ ./padd4.sh
      -bash-3.2$ open file pipe ok, fd = 3
      1 1 = 2
      open file pipe ok, fd = 3
      2 2 = 4
      open file pipe ok, fd = 3
      3 3 = 6
      open file pipe ok, fd = 3
      4 4 = 8
      

       也是沒問題的,既沒有出現多個請求粘連的情況,也沒有出現請求與應答錯位的情況。

       

      測試程序

      padd4.c

      add4.c

       

       

      4.結論

      Solaris 上的pipe不僅可以全雙工通訊、不依賴父子進程關系,還可以實現類似tcp那樣分離多個客戶端通訊連接的能力。

      雖然Solaris前途未卜,但是希望一些好的東西還是能流傳下來,就比如這個神奇的pipe。

       

      看完今天的文章,你是不是對特立獨行的Solaris又加深了一層了解?歡迎留言區說說你認識的Solaris。

       

      posted @ 2019-11-27 11:28  goodcitizen  閱讀(675)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产办公室秘书无码精品99| 欧美交a欧美精品喷水| 亚洲精品综合一区二区在线| 亚洲精品一区二区三天美| 国产免费AV片在线看| 国产普通话对白刺激| 在线观看中文字幕国产码| 亚洲成av人片无码天堂下载| 国产高清一区二区三区视频| 国产最大成人亚洲精品| 农村老熟女一区二区三区| 忘忧草影视| 贺兰县| 日本边添边摸边做边爱喷水| 成人精品色一区二区三区| 亚洲欧美偷国产日韩| 不卡国产一区二区三区| 国产午夜福利精品视频| FC2免费人成在线视频| 淮滨县| 亚洲永久精品日韩成人av| 国产在线午夜不卡精品影院| 中文字幕精品亚洲无线码二区| 国产又色又爽又黄的视频在线| 亚洲国产精品久久久天堂麻豆宅男| 亚洲中文字幕av无码区| 免费视频欧美无人区码| 亚洲一区成人在线视频| 性一交一黄一片| 精品国产AV无码一区二区三区| 无码伊人66久久大杳蕉网站谷歌| 久久精品无码鲁网中文电影| 亚洲www永久成人网站| 免费久久人人爽人人爽AV| 99久久婷婷国产综合精品青草漫画| 8x国产精品视频| 国产成人精品一区二区三| 国产av一区二区不卡| 亚洲精品久久国产高清| av中文字幕国产精品| 999精品视频在线|