Linux--進程間通信(信號量,共享內存)



一. 信號量  

l信號量: 解決進程之間的同步與互斥的IPC機制

 

多個進程同時運行,之間存在關聯
  •同步關係
  •互斥關係
互斥與同步關係存在的根源在於臨界資源
  •臨界資源是在同一個時刻只允許有限個(通常只有一個)進程可以訪問(讀)或修改(寫)的資源
    –硬件資源(處理器、內存、存儲器以及其他外圍設備等)
    –軟件資源(共享代碼段,共享結構和變量等)
  •臨界區,臨界區本身也會成爲臨界資源
 
 
一個稱爲信號量的變量
  •信號量對應於某一種資源,取一個非負的整型值
  •信號量值指的是當前可用的該資源的數量,若它等於0則意味着目前沒有可用的資源
在該信號量下等待資源的進程等待隊列
對信號量進行的兩個原子操作(PV操作)
  •P操作
  •V操作
 
最簡單的信號量是隻能取0 和1 兩種值,叫做二維信號量
 
編程步驟:
  創建信號量或獲得在系統已存在的信號量
    •調用semget()函數
    •不同進程使用同一個信號量鍵值來獲得同一個信號量
  初始化信號量
    •使用semctl()函數的SETVAL操作
    •當使用二維信號量時,通常將信號量初始化爲1
  進行信號量的PV操作
    •調用semop()函數
    •實現進程之間的同步和互斥的核心部分
  如果不需要信號量,則從系統中刪除它
    •使用semclt()函數的IPC_RMID操作
    •在程序中不應該出現對已被刪除的信號量的操作
 

 

 eg. 通過對信號量PV操作,消除父子進程間的競爭條件,使得其調用順序可控。
複製代碼
 1 union semun {
 2     int val;
 3     struct semid_ds *buf;
 4     unsigned short *array;
 5 };
 6 
 7 // 將信號量sem_id設置爲init_value
 8 int init_sem(int sem_id,int init_value) {
 9     union semun sem_union;
10     sem_union.val=init_value;
11     if (semctl(sem_id,0,SETVAL,sem_union)==-1) {
12         perror("Sem init");
13         exit(1);
14     }
15     return 0;
16 }
17 // 刪除sem_id信號量
18 int del_sem(int sem_id) {
19     union semun sem_union;
20     if (semctl(sem_id,0,IPC_RMID,sem_union)==-1) {
21         perror("Sem delete");
22         exit(1);
23     }
24     return 0;
25 }
26 // 對sem_id執行p操作
27 int sem_p(int sem_id) {
28     struct sembuf sem_buf;
29     sem_buf.sem_num=0;//信號量編號
30     sem_buf.sem_op=-1;//P操作
31     sem_buf.sem_flg=SEM_UNDO;//系統退出前未釋放信號量,系統自動釋放
32     if (semop(sem_id,&sem_buf,1)==-1) {
33         perror("Sem P operation");
34         exit(1);
35     }
36     return 0;
37 }
38 // 對sem_id執行V操作
39 int sem_v(int sem_id) {
40     struct sembuf sem_buf;
41     sem_buf.sem_num=0;
42     sem_buf.sem_op=1;//V操作
43     sem_buf.sem_flg=SEM_UNDO;
44     if (semop(sem_id,&sem_buf,1)==-1) {
45         perror("Sem V operation");
46         exit(1);
47     }
48     return 0;
49 }
複製代碼
複製代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <sys/types.h>
 5 #include <unistd.h>
 6 #include <sys/sem.h>
 7 #include <sys/ipc.h>
 8 #include "sem_com.c"
 9 
10 #define DELAY_TIME 3
11 
12 int main() {
13     pid_t pid;
14 //    int sem_id;
15 //    key_t sem_key;
16 
17 //    sem_key=ftok(".",'a');
18 //    以0666且create mode創建一個信號量,返回給sem_id
19 //    sem_id=semget(sem_key,1,0666|IPC_CREAT);
20 //    將sem_id設爲1
21 //    init_sem(sem_id,1);
22 
23     if ((pid=fork())<0) {
24         perror("Fork error!\n");
25         exit(1);
26     } else if (pid==0) {
27 //        sem_p(sem_id); //    P操作
28         printf("Child running...\n");
29         sleep(DELAY_TIME);
30         printf("Child %d,returned value:%d.\n",getpid(),pid);
31 //        sem_v(sem_id); //    V操作
32         exit(0);
33     } else {
34 //        sem_p(sem_id); //    P操作
35         printf("Parent running!\n");
36         sleep(DELAY_TIME);
37         printf("Parent %d,returned value:%d.\n",getpid(),pid);
38 //        sem_v(sem_id); //    V操作
39 //        waitpid(pid,0,0);
40 //        del_sem(sem_id);
41         exit(0);
42     }
43 
44 }
複製代碼

在以上程序註釋//未去掉時,即沒用信號量機制時,其結果爲:

顯然,此處存在競爭條件。

 

在以上程序註釋//去掉後,即使用信號量機制,其結果爲:

由於父子進程採用同一信號量且均執行各自PV操作,故必先等一個進程的V操作後,另一個進程才能工作。

 

 

二. 共享內存


最爲高效的進程間通信方式
 
進程直接讀寫內存,不需要任何數據的拷貝
  •爲了在多個進程間交換信息,內核專門留出了一塊內存區
  •由需要訪問的進程將其映射到自己私有地址空間
  •進程直接讀寫這一內存區而不需要進行數據的拷貝,提高了效率
 
多個進程共享一段內存,需要依靠某種同步機制,如互斥鎖和信號量等

 

l共享內存編程步驟:
  1. 創建共享內存
    •函數shmget()
    •從內存中獲得一段共享內存區域
 
  2. 映射共享內存
    •把這段創建的共享內存映射到具體的進程空間中
    •函數shmat()
 
  3. 使用這段共享內存
    •可以使用不帶緩衝的I/O讀寫命令對其進行操作
 
  4. 撤銷映射操作: 函數shmdt()
 
  5. 刪除共享內存: 函數shctl()
 

 

eg. 下面這個例子完成:父進程從stdin讀取字符串並保存到共享內存中,子進程從共享內存中讀出數據並輸出到stdout

複製代碼
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <sys/types.h>
  5 #include <sys/ipc.h>
  6 #include <sys/shm.h>
  7 
  8 #define BUFFER_SIZE 2048
  9 
 10 int main() {
 11     pid_t pid;
 12     int shmid;
 13     char *shm_addr;
 14     char flag[]="Parent";
 15     char buff[BUFFER_SIZE];
 16 //    創建當前進程的私有共享內存
 17     if ((shmid=shmget(IPC_PRIVATE,BUFFER_SIZE,0666))<0) {
 18         perror("shmget");
 19         exit(1);
 20     } else 
 21         printf("Create shared memory: %d.\n",shmid);
 22 
 23 //    ipcs 命令往標準輸出寫入一些關於活動進程間通信設施的信息
 24 //    -m 表示共享內存
 25     printf("Created shared memory status:\n");
 26     system("ipcs -m");
 27 
 28     if((pid=fork())<0) {
 29         perror("fork");
 30         exit(1);
 31     }else if (pid==0) {
 32 //    自動分配共享內存映射地址,爲可讀可寫,映射地址返回給shm_addr
 33         if ((shm_addr=shmat(shmid,0,0))==(void*)-1) {
 34             perror("Child:shmat");
 35             exit(1);
 36         }else
 37             printf("Child: Attach shared-memory: %p.\n",shm_addr);
 38 
 39         printf("Child Attach shared memory status:\n");
 40         system("ipcs -m");
 41 //    比較shm_addr,flag的長度爲strlen(flag)的字符
 42 //    當其內容相同時,返回0
 43 //    否則返回(str1[n]-str2[n])
 44         while (strncmp(shm_addr,flag,strlen(flag))) {
 45             printf("Child: Waiting for data...\n");
 46             sleep(10);
 47         }
 48 
 49         strcpy(buff,shm_addr+strlen(flag));
 50         printf("Child: Shared-memory: %s\n",buff);
 51 //    刪除子進程的共享內存映射地址
 52         if (shmdt(shm_addr)<0) {
 53             perror("Child:shmdt");
 54             exit(1);
 55         }else
 56             printf("Child: Deattach shared-memory.\n");
 57 
 58         printf("Child Deattach shared memory status:\n");
 59         system("ipcs -m");
 60 
 61     }else{
 62         sleep(1);
 63 //    自動分配共享內存映射地址,爲可讀可寫,映射地址返回給shm_addr
 64         if ((shm_addr=shmat(shmid,0,0))==(void*)-1) {
 65             perror("Parent:shmat");
 66             exit(1);
 67         }else
 68             printf("Parent: Attach shared-memory: %p.\n",shm_addr);
 69 
 70         printf("Parent Attach shared memory status:\n");
 71         system("ipcs -m");
 72 //    shm_addr爲flag+stdin
 73         sleep(1);
 74         printf("\nInput string:\n");
 75         fgets(buff,BUFFER_SIZE-strlen(flag),stdin);
 76         strncpy(shm_addr+strlen(flag),buff,strlen(buff));
 77         strncpy(shm_addr,flag,strlen(flag));
 78 //    刪除父進程的共享內存映射地址
 79         if (shmdt(shm_addr)<0) {
 80             perror("Parent:shmdt");
 81             exit(1);
 82         }else
 83             printf("Parent: Deattach shared-memory.\n");
 84 
 85         printf("Parent Deattach shared memory status:\n");
 86         system("ipcs -m");
 87 //    保證父進程在刪除共享內存前,子進程能讀到共享內存的內容        
 88         waitpid(pid,NULL,0);
 89 //    刪除共享內存
 90         if (shmctl(shmid,IPC_RMID,NULL)==-1) {
 91             perror("shmct:IPC_RMID");
 92             exit(1);
 93         }else
 94             printf("Delete shared-memory.\n");
 95 
 96         printf("Child Delete shared memory status:\n");
 97         system("ipcs -m");
 98 
 99         printf("Finished!\n");
100     }
101     
102     exit(0);
103 }
複製代碼

發佈了14 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章