Linux --進程間通信--管道

一、進程的間通信的原理

   進程間交換數據必須通過內核,在內核中開闢一塊緩衝區,進程1把數據空間拷貝到內核緩衝區,進程2再從內核緩衝區把數據讀走。這樣就實現了進程間通信。

二、進程通信的進制--管道(pipe)

   調用 pipe(int filedes[2])函數在內存中開闢的空間稱爲管道,它一端讀數據一端寫數據,通過filedes傳出給用戶程序的兩個描述符,filedes[0]指向讀端,filedes[1]指向寫端。所以通過read(filedes[0);或者write(filedes),來從管道讀或寫文件。

三、 用管道實現進程通信的方法

(1)父進程創建管道,得到兩個文件描述符指向管道的兩端、

(2)父進程調用fork()創建子進程,那麼子進程也有兩個文件描述符指向管道的兩端。

(3)父進程關閉寫端,子進程關閉讀端,子進程往管道寫,父進程讀,管道是用環形隊列實現的,數據從寫端寫入,讀端讀出,這樣就實現了進程通信

--代碼-----

 #include<stdio.h>
 #include<unistd.h>
 int main()
 {
   int _pipe[2];
   int  ret=pipe(_pipe);
   if(ret < 0)
   {
     printf(" pipe()");
     return 1;
   }
   pid_t id=fork();
   if(id < 0)
   {
     printf("fork()");
     return 2;
   }
   else if(id==0)
   {
     close(_pipe[0]);
     char buf[]="hello world";
     int count=10;
     while(count)
     {
      sleep(1);
      ssize_t _size=write(_pipe[1],buf,sizeof(buf));
      if(_size < 0)
      {
        printf("write()");
        return 3;
      }
      count--;
      }
  
    }else
    {
    close(_pipe[1]);
    char buf[1024];
    int i=0;
    while(i++<10)
    {
      ssize_t _size=read(_pipe[0],buf,sizeof(buf));
      if(_size >0)
      { 
         buf[_size]='\n';
        printf("%s\n",buf);
      }
      else
      {
        printf("child is empty or exit") ;
        break;
      }
    }
    }
    return 0;
  }

運行結果:

wKioL1cMl8OzqK9dAAD6VXNGptg146.jpg

四、管道的限制

    兩個進程用一個管道只能實現單進程通信,一個寫一個讀,不能同時進行讀寫,如果要讓父進程寫,子進程讀,就必須重新另開一個管道。管道的讀寫端是通過打開的文件描述符來傳遞的,所以兩個進程必須從父進程那裏繼承文件描述符。只有兩個進程都能訪問同一管道時,他們才能通信。

五、管道的4種特殊情況

    (1)如果指向管道寫端的文件描述符都關閉了,但是仍有進程從管道的讀端讀數據,那麼管道中剩餘的數據被讀完後,再次read 就會返回0。

    (2)如果有指向管道寫端的文件描述符沒關閉,而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那麼管道中剩餘的數據都被讀取後,再次read會阻塞,直到管道中有數據可讀了纔讀取數據並返回。

    (3)如果所有指向管道讀端的文件描述符都關閉了,這時有進程向管道的寫端write,那麼該進程會收到信號SIGPIPE,通常會導致進程異常終止。

    (4)如果所有指向管道讀端的文件描述符沒關閉,但是也不從管道中讀取數據,這是如果進程向管道寫端寫數據,那麼在管道被寫滿時,再寫的話就會被阻塞,直到管道中有了空位置才寫入數據並返回。


  管道的缺點在於是沒有名字,因此,只能用於有親緣關係的進程之間進行通信,爲了解決這個問題,又引入了命名管道(FIFO),命名管道提供了路徑名與之關聯,以FIFO的文件形式存儲於文件系統中,所以只要訪問該路勁,就能實現進程間的通信。FIFO總是按照先進先出的原則工作,第一個被寫入的先出來。

一、命名管道的創建

    用mkfifo函數來創建FIFO

    int mkfifo(const char *path,mode_t mode);

二、命名管道的使用方法

    先調用open()將其打開,要以只讀的方式打開,以寫的方式打開寫入(O_WRONLY)

三、命名管道的程序代碼:

client端

 #include<stdio.h>
 #include<error.h>
 #include<stdlib.h>
 #include<string.h>
 #include<fcntl.h>
 #include<sys/types.h>
 #include<sys/stat.h>
 #include<unistd.h>
 #define _PATH_ "./.tem"
 int main()
 {
   int fd=open(_PATH_,O_RDONLY);
   if(fd<0)
   {
     printf("open error");
   }
   char buf[1024];
   memset(buf,'\0',sizeof(buf));
   while(1)
   {
    int ret =read(fd,buf,sizeof(buf));
     if(ret>0)
     {
       buf[ret]='\n';
       printf("serve said:%s\n",buf);
     }
   }
   close(fd);
    return 0;
 }

serve端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#define _PATH_ "./.tem"
int main()
 {
   umask(0);
   int ret = mkfifo(_PATH_,S_IFIFO |0666) ;
   if(ret<0)
   {
     printf("mkfifo error\n");
     return 1;
    }
   int fd=open(_PATH_,O_WRONLY);
   if(fd<0)
   {
     printf("open error");
   }
   char buf[1024];
   memset(buf,'\0',sizeof(buf));
   while(1)
   {
      printf("please iput\n");
      scanf("%s",buf);
      int ret =write(fd,buf,strlen(buf)+1);
      if(ret<0)
      {
        printf("write error\n");
        break;
      }
    }
    close(fd);
     return 0;
  }

運行結果:

wKioL1cMo1OC3rNaAAIWh1VZOCg524.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章