日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 之进程间通信-------------InterProcess Communication

發布時間:2025/4/16 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 之进程间通信-------------InterProcess Communication 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  進程間通信至少可以通過傳送打開文件來實現,不同的進程通過一個或多個文件來傳遞信息,事實上,在很多應用系統里,都使用了這種方法。但一般說來,進程間 通信(IPC:InterProcess Communication)不包括這種似乎比較低級的通信方法。Unix系統中實現進程間通信的方法很多,而且不幸的是,極少方法能在所有的Unix系 統中進行移植(唯一一種是半雙工的管道,這也是最原始的一種通信方式)。而Linux作為一種新興的操作系統,幾乎支持所有的Unix下常用的進程間通信 方法:管道、消息隊列、共享內存、信號量、套接口等等。

  1、管道(pipe)

  管道是進程間通信中最古老的方式,它包括無名管道和有名管道兩種,

    無名管道用于父進程和子進程間的通信,

    有名管道用于運行于同一臺機器上的任意兩個進程間的通信。

  無名管道由pipe()函數創建:

    #include? <unistd.h>

    int pipe(int? filedis[2]);

  參數filedis返回兩個文件描述符:filedes[0]為讀而打開,filedes[1]為寫而打開;filedes[1]的輸出是filedes[0]的輸入

1 1 #include <stdio.h> 2 2 #include <string.h> 3 3 #include <stdlib.h> 4 4 #include <errno.h> 5 5 #include <unistd.h> 6 6 7 7 #define INPUT 0 8 8 #define OUTPUT 1 9 9 10 10 int main() 11 11 { 12 12 int file_descriptors[2]; 13 13 pid_t pid; 14 14 char buf[256]; 15 15 char send1[256]; 16 16 int returned_count; 17 17 18 18 //創建無名管道 19 19 pipe(file_descriptors); 20 20 //創建子進程 21 21 if((pid=fork()) == -1) 22 22 { 23 23 printf("Error in fork\n"); 24 24 exit(1); 25 25 } 26 26 //執行子進程 27 27 if(pid == 0) 28 28 { 29 29 printf("int the child process....\n"); 30 30 fgets(send1,256, stdin); 31 31 32 32 //子進程向父進程寫數據,關閉管道的讀端; 33 33 close(file_descriptors[INPUT]); 34 34 write(file_descriptors[OUTPUT], send1, strlen(send1)); 35 35 exit(0); 36 36 } 37 37 else //執行父進程 38 38 { 39 39 printf("int the parent process...\n"); 40 40 //父進程從管道讀取子進程寫的數據,關閉管道的寫端 41 41 close(file_descriptors[OUTPUT]); 42 42 returned_count = read(file_descriptors[INPUT], buf, sizeof(buf)); 43 43 printf("%d bytest of data received from child process: %s\n" 44 44 ,returned_count,buf); 45 45 } 46 46 47 47 return 0; 48 48 } 49 50 [root@cp ~]# ./a.out 51 int the parent process... 52 int the child process.... 53 lkfjasldkf 54 11 bytest of data received from child process: lkfjasldkf

  在linux系統下,有名管道可由兩種方式創建:命令行方式mknod? 系統調用 和函數mkfifo。

  生成了有名管道后,就可以使用一般的文件I/O函數如open, close, read, write等來對它進行操作。

什么是命名管道稱為FIFO文件,它是一種特殊類型的文件,它在文件系統中以文件名的形式存在;

  由于Linux中所有的事物都可被視為文件,所以對命名管道的使用也就變得與文件操作非常統一,也使得它的使用非常方便,同時我們也可以像平常的文件名一樣在命令中使用。

  使用下面兩個函數均可以創建一個命名管道,函數原型如下:

  #include ? <sys/types.h>

  #include? <sys/stat.h>

  int? mkfifo(const? char *filename,?? mode_t? mode);

  int mknod(const? char *filename,?? mode_t? mode? | S_IFIFO,?? (dev_t)0);

這兩個函數都能創建一個FIFO文件,注意是創建一個真實存在于文件系統中的文件,filename指定了文件名,而mode則指定了文件的讀寫權限;

  mknod 是比較老的函數,而使用mkfifo函數更加簡單和規范,所以建議在可能的情況下,盡量使用mkfifo而不是mknod。

訪問命名管道

  1)打開FIFO文件

    與打開其他文件一樣,FIFO文件也可以使用open調用來打開。注意,mkfifo函數僅僅只是創建一個FIFO文件,要使用命名管道還要將其打開。

  注意點:

    1、程序不能以O_RDWR模式打開FIFO文件進行讀寫操作,而其行為沒有明確定義。因為假如一個管道以讀/寫方式打開,進程就會讀回自己的輸出,同時我們通常使用FIFO只是為了單向的數據傳遞

    2、傳遞給open調用的是FIFO的路徑名,而不是正常的文件。

打開FIFO文件通常有四種方式

  open(const? char *path,? O_RDONLY);?? //只讀? 并默認是阻塞

  open(const? char? *path,? O_RDONLY | O_NOBLOCK);? //無阻塞只讀

  open(const? char? *path,?? O_WRONLY);?? //只寫 并默認是阻塞

  open(const? char? *path,??? O_WRONLY | O_NONBLOCK);? //無阻塞只寫

open調用的阻塞是怎么一回事? 很簡單,對于只讀(O_RDONLY)打開的FIFO文件,如果open調用是阻塞的,除非有一個進程以寫方式打開同一個FIFO, 否則它讀不到數據就不會返回; 如果open調用是非阻塞的,則即使沒有其他進程以寫方式打開同一FIFO方式,open調用將成功并立即返回。

  對于以只寫方式(O_WRONLY)打開的FIFO文件,如果open調用是阻塞的(即第二個參數為O_WRONLY),open調用將被阻塞,直到有一 個進程以只讀方式打開同一個FIFO文件為止;如果open調用是非阻塞的(即第二個參數為O_WRONLY?| O_NONBLOCK),open總會立即返回,但如果沒有其他進程以只讀方式打開同一個FIFO文件,open調用將返回-1,并且FIFO也不會被打 開。

  使用FIFO實現進程間的通信

 兩個進程如何通過FIFO實習通信,首先要有兩個進程,一個源文件為fifowrite.c,? 它在需要時創建管道,然后向管道寫入數據,數據有文件Data.txt提供,大小為10M, 內容都是字符‘0’, 另一個源文件為fiforead.c,它從FIFO中讀取數據,并把讀到的數據保存到另一個文件DataFormFIFO.txt中,

  命名管道的安全問題

  前面指的是兩個進程之間通信問題,也就是說,一個進程向FIFO文件寫數據,而另一個進程從FIFO文件中讀取數據。

  假如只使用一個FIFO文件,如果有多個進程同時向同一個FIFO文件寫數據,而只有一個讀FIFO進程在同一個FIFO文件中讀取數據時,則會發生數據塊的相互交錯問題。而在實際中多個不同進程向一個FIFO讀進程發送數據是很常見的;

  為了解決這一問題,就是讓寫操作的原子化。即: 系統規定:在一個以O_WRONLY(阻塞方式)打開的FIFO中,如果寫入的數據長度小于等待PIPE_BUF,? 結果是 要么寫入全部字節, 要么 一個字節都不寫入。??? 如果所有的寫請求都是發往一個阻塞的FIFO時,并且每個寫請求的數據長度小于等于PIPE_BUF字節,系統就可以確保數據決不會交錯在一起;

  為了數據的安全,我們很多時候要采用阻塞的FIFO,讓寫操作變成原子操作。

  //fiforead.c
1
#include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <sys/types.h> 6 #include <limits.h> 7 #include <string.h> 8 9 int main() 10 { 11 int pipe_fd = -1; 12 int data_fd = -1; 13 const char *fifo_name = "./my_fifo"; 14 int res = 0; 15 int open_mode = O_RDONLY; 16 char buffer[PIPE_BUF + 1]; 17 int bytes_read = 0; 18 int bytes_write = 0; 19 20 memset(buffer, '\0', sizeof(buffer)); 21 printf("Process %d opening FIFOO O_RDONLY \n", getpid()); 22 23 //以只讀阻塞方式打開管道文件, 名字必須與fifowrite.c文件中的FIFO同名 24 pipe_fd = open(fifo_name, open_mode); 25 26 //以只寫方式創建保存數據的文件 27 data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644); 28 printf("Process %d result %d\n", getpid(), pipe_fd); 29 30 if(pipe_fd != -1) 31 { 32 do 33 { 34 //讀取FIFO中的數據,并把它保存在文件DataFormFIFO.txt文件中 35 res = read(pipe_fd, buffer, PIPE_BUF); 36 bytes_write = write(data_fd, buffer, res); 37 bytes_read += res; 38 }while(res >0); 39 close(pipe_fd); 40 close(data_fd); 41 } 42 else 43 { 44 exit(EXIT_FAILURE); 45 } 46 printf("Process %d finished, %d bytes read \n", getpid(), bytes_read); 47 exit(EXIT_SUCCESS); 48 return 0; 49 }
[root@cp pipe]# gcc -o write fifowrite.c
[root@cp pipe]# ./write
//該線程處于阻塞狀態;

1 #include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <sys/types.h> 6 #include <limits.h> 7 #include <string.h> 8 9 int main() 10 { 11 int pipe_fd = -1; 12 int data_fd = -1; 13 const char *fifo_name = "./my_fifo"; 14 int res = 0; 15 int open_mode = O_RDONLY; 16 char buffer[PIPE_BUF + 1]; 17 int bytes_read = 0; 18 int bytes_write = 0; 19 20 memset(buffer, '\0', sizeof(buffer)); 21 printf("Process %d opening FIFOO O_RDONLY \n", getpid()); 22 23 //以只讀阻塞方式打開管道文件, 名字必須與fifowrite.c文件中的FIFO同名 24 pipe_fd = open(fifo_name, open_mode); 25 26 //以只寫方式創建保存數據的文件 27 data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644); 28 printf("Process %d result %d\n", getpid(), pipe_fd); 29 30 if(pipe_fd != -1) 31 { 32 do 33 { 34 //讀取FIFO中的數據,并把它保存在文件DataFormFIFO.txt文件中 35 res = read(pipe_fd, buffer, PIPE_BUF); 36 bytes_write = write(data_fd, buffer, res); 37 bytes_read += res; 38 }while(res >0); 39 close(pipe_fd); 40 close(data_fd); 41 } 42 else 43 { 44 exit(EXIT_FAILURE); 45 } 46 printf("Process %d finished, %d bytes read \n", getpid(), bytes_read); 47 exit(EXIT_SUCCESS); 48 return 0; 49 }
//[root@cp pipe]# gcc -o write fifowrite.c
[root@cp pipe]# ./write

2、消息隊列

  

3、共享內存

  共享內存是運行在同一臺機器上的進程間通信最快的方式,因為數據不需要在不同的進程間復制。通常由一個進程創建一塊共享內存區,其余進程對這塊內存區進行讀寫。得到共享內存有兩種方式:映射/dev/mem設備和內存映像文件。前一種方式不給系統帶來額外的開銷,但現實中并不常用,因為它控制存取的將是實際的物理內存,在linux系統下,這只有通過限制linux系統存取的內存才可以做到,這當然不太實際。常用的方式是通過shmxxx函數族來實現利用共享內存進行存儲的。

  首先是用的函數是shmget,它獲得一個共享存儲標識符。

  #include <sys/types.h>

  #include <sys/ipc.h>

  #include <sys/shm.h>

  int shmget(key_t? key, int? size,? int flag);

  這個函數有點類似malloc函數,系統按照請求分配size大小的內存用作共享內存。linux系統內核中每個IPC結構都有一個非負整數的標識符,這樣對一個消息隊列發送消息時總要引用標識符就可以了。這個標識符是內核由IPC結構的關鍵字得到的,這個關鍵字,就是上面第一個函數的key。 數據類型key_t是頭文件sys/types.h 中定義的,它是一個長整形的數據。

  當共享內存創建后,其余進程可以調用shmat() 將其連接到自身的地址空間中;

  void? *shmat(int? shmid, void *addr,? int? flag);

  shmid 為shmget函數返回的共享存儲標識符, addr 和flag參數決定了以什么方式來確定連接的地址,函數的返回值即是該進程數據段所連接的實際地址, 進程可以對此進程進行讀寫操作;

  使用共享存儲來實現進程間通信的注意點是對數據存取的同步,必須確保當一個進程去讀取數據時,它所想要的數據已經寫好了。通常,信號量被要來實現對共享存儲數據存取的同步,另外,可以通過使用shmctl函數設置共享存儲內存的某些標志位如SHM_LOCK,

SHM_UNLOCK等來實現。

4、信號量

  為了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,它可以通過生成并使用令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨占式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來協調進程對共享資源的訪問的。

  信號量是一個特殊的變量,程序對其訪問都是原子操作,且只允許對它進行等待(即P(信號量)) 和? 發送 (即V(信號變量))操作。最簡單的信號量是只能取0和1的變量,這也是信號量最常見的一種形式,叫做二進制信號量。 還有一種可以取多個正整數的信號量稱為通用信號量,下面主要討論二進制信號量;

  信號量的工作原理

  由于信號量只能進行兩種操作等待和發送信號,即P(sv)和V(sv),他們的行為是這樣的:

    P(sv):如果sv的值大于零,就給它減1;如果它的值為零,就掛起該進程的執行。

    V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛起,就給它加1;

就是兩個進程共享信號量sv,

  一旦其中一個進程執行了P(sv)操作,它將得到信號量,并可以進入臨界區,使sv減1。

  而第二個進程將被阻止進入臨界區, 因為當它試圖執行P(sv)時,sv為0,它會被掛起以等待第一個進程離開臨界區域并執行V(sv)釋放信號量,這時第二個進程就可以恢復執行?! ?/p>

  Linux的信號量機制

Linux提供了一組精心設計的信號量接口來對信號進行操作,它們不只是針對二進制信號量,下面將會對這些函數進行介紹,但請注意,這些函數都是用來對成組的信號量值進行操作的。它們聲明在頭文件sys/sem.h中。 1、semget函數 它的作用是創建一個新信號量或取得一個已有的信號量,原型為: int? semget(key_t key, int num_sems,? int sem_flags); 參數說明: 第一個參數:key是整數值(唯一非零),不相關的進程可以通過它訪問一個信號量,它代表程序可能要使用的某個資源,程序對所有信號量的訪問都是間接的, 程序先通過調用semget函數并提供一個鍵,再由系統生成一個相應的信號標識符(semget函數的返回值),只有semget函數才直接使用信號量鍵;所有其他的信號量函數使用由semget函數返回的信號量標識符,如果多個程序使用相同的key值,key將負責協調工作; 第二個參數:num_sems指定需要的信號量數目,它的值幾乎總是1; 第三個參數:sem_flags 是一組標志,當想要在信號量不存在的情況時則創建一個新的信號量,可以和值IPC_CREAT標志后,即使給出的鍵是一個已有信號量的鍵,也不會產生錯誤。而IPC_CREAT|IPC_EXCL則可以創建一個新的,唯一的信號量,如果信號已存在,返回一個錯誤; semget函數成功返回一個相應信號標識符(非零),失敗返回-1; semop函數 功能:改變信號量的值,原型為: int? semop(int sem_id, struct sembuf *sem_opa, size_t? num_sem_ops); 參數說明: 第一個參數:sem_id是由semget返回的信號量標識符, sembuf結構定義如下: struct sembuf{ short sem_num;? //除非使用一組信號量,否則它為0         short?sem_op;?? //信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,P(等待)操作                ?//一個是+1,即V(發送信號)操作。?????????????? short?sem_flg;//通常為SEM_UNDO,使操作系統跟蹤信號,
               //并在進程沒有釋放該信號量而終止時,操作系統釋放信號量 };

  信號量稱為信號燈,它是用來協調不同進程間的數據對象的,而最主要的應用是前一節的共享內存方式的進程間通信。本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取狀況,一般來說,為了獲得共享資源,進程需要執行下列操作:

  1、測試控制該資源的信號量

  2、若此信號量的值為正,則允許進行使用該資源。進程將信號量減1

  3、若此信號量為0,則該資源目前不可用,進程進入睡眠狀態,直到信號量大于0.進程被喚醒,轉入步驟1;

  4、當進程不再使用一個信號量控制的資源時,信號量加1。如果當時有進程正在睡眠等待此信號量,則喚醒此進程。

維護信號量狀態的是Linux內核操作系統而不是用戶進程。我們可以從頭文件/usr/scr/linux/include/linux/sem.h中看到內核用來維護信號量狀態的各個結構的定義。信號量是一個數據集合,用戶可以單獨使用這一集合的每個元素。要調用的第一個函數是semget,用以獲得一個信號量ID;

  

  

  

?

?

?

?

?

?

?

?

?

?

?

?

轉載于:https://www.cnblogs.com/chris-cp/p/3532216.html

總結

以上是生活随笔為你收集整理的linux 之进程间通信-------------InterProcess Communication的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。