中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

管道的內核實現

發布時間:2020-07-28 14:50:19 來源:網絡 閱讀:251 作者:ye小灰灰 欄目:編程語言

一.概述:

     管道是進程間通信的手段之一,它實際上是一個存在于內存的特殊文件,而這個文件要通過兩個已經打開的文件才能進行操作,這兩個文件分別指向管道的兩端。

            管道是通過在內存中開辟一個緩存區來實現進程間的通信的,這個緩存區的大小是固定的。在linux中,這個緩存區的大小為一頁,即4k。但固定的大小會帶來問題,當緩存區已經被write操作寫滿時,之后的write操作會阻塞,等待緩存區內的某些數據被讀取,以便騰出空間給寫操作調用。

            在linux中,管道并沒有專門的數據結構,而是通過file結構和inode共同實現。兩個file結構指向一個inode,而這個inode對應了內存的某個區域。

管道的內核實現

注:一個file中的f_op只對應寫或者讀操作中的一個。


          而管道創建的緩存區是一個環形緩存區,即當用到緩存區的尾部時,下一個可用區間為緩存區的首部。緩存區一般采用的是數組形式,即申請的是一個線性的地址空間。而形成環狀用的是    模    緩存區長度。而訪問這個緩存區的方式是一種典型的“生產者—消費者模型”,即當生產者有大量的數據要寫時,而緩存區的大小只有1k,所以當緩存區被寫滿時,生產者必須等待,等待消費者讀取數據,以便騰出空間讓消費者寫。而當消費者發現緩存區內并沒有數據時,消費者必須等待,等待生產者往緩存區內寫數據來提供消費者讀數據。

          那么又如何判斷是“空”還是“滿”呢。當read和write指向環形緩存區的同一位置時為空或滿。為了區別空和滿,規定read和write重疊時為空,而當write比read快,追到距離read還有一個元素間隔時,就認為是滿。

         摘:(不明覺厲)        

 并發訪問

考慮到在不同環境下,任務可能對環形緩沖區的訪問情況不同,需要對并發訪問的情況進行分析。

在單任務環境下,只存在一個讀任務和一個寫任務,只要保證寫任務可以順利的完成將數據寫入,而讀任務可以及時的將數據讀出即可。如果有競爭發生,可能會出現如下情況:

Case1:假如寫任務在“寫指針加1,指向下一個可寫空位置”執行完成時被打斷,此時寫指針write指向非法位置。當系統調度讀任 務執行時,如果讀任務需要讀多個數據,那么不但應該讀出的數據被讀出,而且當讀指針被調整為0是,會將以前已經讀出的數據重復讀出。

管道的內核實現

Case2:假設讀任務進行讀操作,在“讀指針加1”執行完時被打斷,此時read所處的位置是非法的。當系統調度寫任務執行時,如果 寫任務要寫多個數據,那么當寫指針指到尾部時,本來緩沖區應該為滿狀態,不能再寫,但是由于讀指針處于非法位置,在讀任務執行前,寫任務會任務緩沖區為 空,繼續進行寫操作,將覆蓋還沒有來的及讀出的數據。


二.用管道實現進程間的通信:

代碼:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
    //.....創建管道
    int pipefd[2] ={-1,-1};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
        perror("pipe");
        return -1;
    }

    //....創建子進程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return -1;
    }

    else if(id == 0)
    {
         //....子進程關閉讀端
         close(pipefd[0]);

         char* buf = "i'm  child";
         int count = 5;
         while(count--)
         {
             write(pipefd[1],buf,strlen(buf));
             sleep(1);
         }
    }
    else
    {
        //....父進程關閉寫端
        close(pipefd[1]);

        char buf[1024];
        int count = 5;
        while(count--)
        {
              memset(buf, '\0', sizeof(buf));
              ssize_t size = read(pipefd[0],buf,sizeof(buf) - 1);
              buf[size] = '\0';
              printf("%s\n",buf);
        }
     }

}

結果截圖:

管道的內核實現


三.使用管道會出現的四種情況(假設都是阻塞I/O操作,沒有設置O_NONBLOCK標志):

1:當所有寫端都關閉時,但還有進程要從管道中讀取數據,那么當管道中的數據讀完后,再次read時,就會返回0,就像讀到文件末尾一樣。

代碼如下:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
    //.....創建管道
    int pipefd[2] ={-1,-1};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
        perror("pipe");
        return -1;
    }

    //....創建子進程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return -1;
    }

    else if(id == 0)
    {
        //....子進程關閉寫端
        close(pipefd[1]);

        char buf[1024];
        int count = 5;
        while(count--)
        {
              memset(buf, '\0', sizeof(buf));
              ssize_t size = read(pipefd[0],buf,sizeof(buf) - 1);
              if(size > 0)
              {
                    buf[size] = '\0';
                    printf("%s\n",buf);
              }
              else
              {
                  printf("read error");
              }
        }
     }
    else
    {
         //....父進程關閉讀端
         close(pipefd[0]);

         char* buf = "i'm  father";
         int count = 5;
         int i = 0;
         while(count--)
         {
             if(i == 3)
             {
                 printf("i just want to sleep");
                 break;
             }
             i++;
             write(pipefd[1],buf,strlen(buf));
             sleep(1);
         }
    }
}

執行結果:

管道的內核實現

2.如果指向管道寫端的文件標識符沒關閉,但寫端也不向管道中寫數據時,如果此時有進程從管道中讀數據,那么當緩存區內的數據被讀完之后,再次read,讀進程會進入阻塞狀態,直到緩存區內有數據才進行讀數據。

代碼如下:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
    //.....創建管道
    int pipefd[2] ={-1,-1};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
        perror("pipe");
        return -1;
    }

    //....創建子進程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return -1;
    }

    else if(id == 0)
    {
        //....子進程關閉寫端
        close(pipefd[1]);

        char buf[1024];
        int count = 5;
        while(count--)
        {
              memset(buf, '\0', sizeof(buf));
              ssize_t size = read(pipefd[0],buf,sizeof(buf) - 1);
              if(size > 0)
              {
                    buf[size] = '\0';
                    printf("%s\n",buf);
              }
        }
     }
    else
    {
         //....父進程關閉讀端
         close(pipefd[0]);

         char* buf = "i'm  father";
         int count = 5;
         int i = 0;
         while(count--)
         {
             if(i == 3)
             {
                 printf("i just want to sleep\n");
                 sleep(5);
             }
             i++;
             write(pipefd[1],buf,strlen(buf));
             sleep(1);
         }
    }
}

執行結果:

管道的內核實現


3.如果所有指向管道的讀端都關閉了,但還有進程要往管道中寫數據,那么進程就會收到信號SIGPIPE,通常會導致進程異常終止。(因為此時再寫也沒用,又沒進程要讀)

代碼如下:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
    //.....創建管道
    int pipefd[2] ={-1,-1};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
        perror("pipe");
        return -1;
    }

    //....創建子進程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return -1;
    }
    else if(id == 0)
    {
        //....子進程關閉讀端
        close(pipefd[0]);

        char buf[] = "i'm  child";
        int count = 5;
        while(count--)
        {
              write(pipefd[1],buf,strlen(buf));
              sleep(1);
        }
    }
    else
    {
         //....父進程關閉寫端
         close(pipefd[1]);
         char buf[1024];
         int count = 5;
         int i = 0;
         while(count--)
         {
             if(i == 3)
             {
                   close(pipefd[0]);
                   break;
             }
             i++;
 
            int ret = 0;
            memset(buf, '\0', sizeof(buf));
            ret =  read(pipefd[0],buf,sizeof(buf) - 1);
            buf[ret] = '\0';
            if(ret > 0)           

            {
                 printf("%s\n",buf);
                 fflush(stdout);
            }
         }
         int status = 0;
         pid_t pid = waitpid(id, &status, 0);
         printf("pid: [%d]  signal:[%d]\n",id,status|0xff);
         fflush(stdout);
    }
}

執行結果:

管道的內核實現


4.如果指向管道讀端的文件標識符沒有關閉,但又不讀緩存區內的內容,那么如果此時還有進程要往緩存區內寫,而緩存區已被寫滿時,寫進程會阻塞,直到緩存區內有空位置之后才能寫。

代碼如下:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
    //.....創建管道
    int pipefd[2] ={-1,-1};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
        perror("pipe");
        return -1;
    }

    //....創建子進程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return -1;
    }
    else if(id == 0)
    {
        //....子進程關閉讀端
        close(pipefd[0]);
 
        char buf[] = "i'm  child";
        int count = 5;
        while(count--)
        {
              write(pipefd[1],buf,strlen(buf));
              sleep(1);
        }
    }
    else
    {
         //....父進程關閉寫端
         close(pipefd[1]);
         char buf[1024];
         int count = 5;
         int i = 0;
         while(count--)
         {
             if(i == 3)
             {
                   printf("i just want to sleep\n");
                   fflush(stdout);
                   sleep(5);
             }
             i++;
 
            int ret = 0;
            memset(buf, '\0', sizeof(buf));
             ret =  read(pipefd[0],buf,sizeof(buf) - 1);
            buf[ret] = '\0';
            if(ret > 0)
            {
                 printf("%s\n",buf);
                 fflush(stdout);
            }
         }
         int status = 0;
         pid_t pid = waitpid(id, &status, 0);
         printf("pid: [%d]  signal:[%d]\n",id,status|0xff);
         fflush(stdout);
    }
}

執行結果:

管道的內核實現


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

上高县| 霞浦县| 湖北省| 三门峡市| 浦江县| 永顺县| 奉贤区| 苏尼特左旗| 客服| 甘孜县| 秦皇岛市| 进贤县| 巴塘县| 怀远县| 湘乡市| 屏边| 莱阳市| 名山县| 武山县| 囊谦县| 永昌县| 隆化县| 南投县| 华宁县| 丹棱县| 乌什县| 米林县| 新绛县| 邹平县| 兴海县| 喀喇沁旗| 西平县| 永善县| 牟定县| 衡水市| 招远市| 鲁山县| 济宁市| 棋牌| 恭城| 梓潼县|