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 }



 
 




 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM