本文關鍵字: linux 共享內存,linux 共享內存編程
可以說,共享內存是一種為高效的進程間通信方式,因為進程可以直接讀寫內存,不需要任何數據的復制。為了在多個進程間交換信息,內核專門留出了一塊內存區,這段內存區可以由需要訪問的進程將其映射到自己的私有地址空間。因此,進程就可以直接讀寫這一內存區而不需要進行數據的復制,從而大大提高了效率。當然,由于多個進程共享一段內存,因此也需要依靠某種同步機制,如互斥鎖和信號量等。其原理示意圖如圖1所示。
 圖1 共享內存原理示意圖
共享內存的實現分為兩個步驟:第一步是創建共享內存,這里用到的函數是shmget(),也就是從內存中獲得一段共享內存區域;第二步是映射共享內存,也就是把這段創建的共享內存映射到具體的進程空間中,這里使用的函數是shmat()。到這里,就可以使用這段共享內存了,也就是可以使用不帶緩沖的I/O讀寫命令對其進行操作。除此之外,還有撤銷映射的操作,其函數為shmdt()。這里主要介紹這3個函數。
表1 列舉了shmget()函數的語法要點。
表1 shmget()函數語法要點
所需頭文件 |
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
|
函數原型 |
int shmget(key_t key, int size, int shmflg) |
函數傳入值 |
key:共享內存的鍵值,多個進程可以通過它訪問同一個共享內存,其中有個特殊值IPC_PRIVATE,用于創建當前進程的私有共享內存 |
size:共享內存區大小 |
shmflg:同open()函數的權限位,也可以用八進制表示法 |
函數返回值 |
成功:共享內存段標識符 |
出錯:-1 |
表2列舉了shmat()函數的語法要點。
表2 shmat()函數語法要點
所需頭文件 |
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
|
函數原型 |
char *shmat(int shmid, const void *shmaddr, int shmflg) |
函數傳入值 |
shmid:要映射的共享內存區標識符 |
shmaddr:將共享內存映射到指定地址(若為0則表示系統自動分配地址并把該段共享內存映射到調用進程的地址空間) |
shmflg |
SHM_RDONLY:共享內存只讀 |
默認0:共享內存可讀寫 |
函數返回值 |
成功:被映射的段地址 |
出錯:-1 |
表3列舉了shmdt()函數的語法要點。
表3 shmdt()函數語法要點
所需頭文件 |
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
|
函數原型 |
int shmdt(const void *shmaddr) |
函數傳入值 |
shmaddr:被映射的共享內存段地址 |
函數返回值 |
成功:0 |
出錯:-1 |
以下實例說明了如何使用基本的共享內存函數。首先創建一個共享內存區(采用的共享內存的鍵值為IPC_PRIVATE,是因為本實例中創建的共享內存是父子進程間的共用部分),然后創建子進程,在父子兩個進程中將共享內存分別映射到各自的進程地址空間中。
父進程先等待用戶輸入,然后將用戶輸入的字符串寫入到共享內存,之后向共享內存的頭部寫入“WROTE”字符串表示父進程已成功寫入數據。子進程一直等到共享內存的頭部字符串為“WROTE”,然后將共享內存的有效數據(在父進程中用戶輸入的字符串)在屏幕上打印。父子兩個進程在完成以上工作后,分別解除與共享內存的映射關系。
后在子進程中刪除共享內存。因為共享內存自身并不提供同步機制,所以應額外實現不同進程間的同步(如信號量)。為了簡單起見,在本實例中用標志字符串來實現非常簡單的父子進程間的同步。
這里要介紹的一個命令是ipcs,用于報告進程間通信機制狀態,它可以查看共享內存、消息隊列等各種進程間通信機制的情況,這里使用了system()函數調用shell命令“ipcs”。程序源代碼如下:
/* shmem.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 2048
int main()
{
pid_t pid;
int shmid;
char *shm_addr;
char flag[] = "WROTE";
char *buff;
/* 創建共享內存 */
if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
{
perror("shmget");
exit(1);
}
else
{
printf("Create shared-memory: %d\n",shmid);
}
/* 顯示共享內存情況 */
system("ipcs -m");
pid = fork();
if (pid == -1)
{
perror("fork");
exit(1);
}
else if (pid == 0) /* 子進程處理 */
{
/* 映射共享內存 */
if ((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
{
perror("Child: shmat");
exit(1);
}
else
{
printf("Child: Attach shared-memory: %p\n", shm_addr);
}
system("ipcs -m");
/* 通過檢查在共享內存的頭部是否有標志字符串“WROTE”來確認父進程已經向共享內存寫入有效數據 */
while (strncmp(shm_addr, flag, strlen(flag)))
{
printf("Child: Wait for enable data...\n");
sleep(5);
}
/* 獲取共享內存的有效數據并顯示 */
strcpy(buff, shm_addr + strlen(flag));
printf("Child: Shared-memory :%s\n", buff);
/* 解除共享內存映射 */
if ((shmdt(shm_addr)) < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("Child: Deattach shared-memory\n");
}
system("ipcs -m");
/* 刪除共享內存 */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("Child: shmctl(IPC_RMID)\n");
exit(1);
}
else
{
printf("Delete shared-memory\n");
}
system("ipcs -m");
}
else /* 父進程處理 */
{
/* 映射共享內存 */
if ((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
{
perror("Parent: shmat");
exit(1);
}
else
{
printf("Parent: Attach shared-memory: %p\n", shm_addr);
}
sleep(1);
printf("\nInput some string:\n");
fgets(buff, BUFFER_SIZE, stdin);
strncpy(shm_addr + strlen(flag), buff, strlen(buff));
strncpy(shm_addr, flag, strlen(flag));
/* 解除共享內存映射 */
if ((shmdt(shm_addr)) < 0)
{
perror("Parent: shmdt");
exit(1);
}
else
{
printf("Parent: Deattach shared-memory\n");
}
system("ipcs -m");
waitpid(pid, NULL, 0);
printf("Finished\n");
}
exit(0);
}
下面是運行結果,從該結果中可以看出,nattch的值隨著共享內存狀態的變化而變化,共享內存的值根據不同的系統會有所不同。
$ ./shmem
Create shared-memory: 753665
/* 在剛創建共享內存時(尚未有任何地址映射)共享內存的情況 */
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 753665 david 666 2048 0
Child: Attach shared-memory: 0xb7f59000 /* 共享內存的映射地址 */
Parent: Attach shared-memory: 0xb7f59000
/* 在父子進程中進行共享內存的地址映射后共享內存的情況 */
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 753665 david 666 2048 2
Child: Wait for enable data...
Input some string:
Hello /* 用戶輸入字符串“Hello” */
Parent: Deattach shared-memory
/* 在父進程中解除共享內存的映射關系后共享內存的情況 */
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 753665 david 666 2048 1
/* 在子進程中讀取共享內存的有效數據并打印 */
Child: Shared-memory :hello
Child: Deattach shared-memory
/* 在子進程中解除共享內存的映射關系后共享內存的情況 */
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 753665 david 666 2048 0
Delete shared-memory
/* 在刪除共享內存后共享內存的情況 */
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
Finished
本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》
熱點鏈接:
1、linux下的信號量
2、linux下的信號處理實例
3、信號處理函數signal()和信號集函數組
4、信號捕捉函數alarm()和pause()
5、信號發送函數kill()和raise()
更多新聞>> |