Linux共享內存


1.什么是共享內存
在前面講虛擬內存機制時,有講到Linux的內存映射機制
初始化虛擬內存區域時,會把虛擬內存和磁盤文件對象對應起來。
由於內存映射機制,一個磁盤文件對象可被多個進程共享訪問,也可被多個進程私有訪問。
當共享訪問時,一個進程的對該對象的修改會顯示到其他進程。
當私有訪問時,修改時會產生保護故障,內核會拷貝這個私有對象,修改的是這個新對象,其他進程指向的是原來的對象。
所以,共享內存是指不同進程訪問同一個邏輯內存

2.共享內存的使用
Linux提供了一組共享內存API,聲明在頭文件sys/shm.h中。
1)shmget函數:新建共享內存
int shmget(key_t key,size_t size,int shmflg);
key:共享內存鍵值,可以理解為共享內存的唯一性標記。
size:共享內存大小
shmflag:創建進程和其他進程的讀寫權限標識。
返回值:相應的共享內存標識符,失敗返回-1

2)shmat函數:連接共享內存到當前進程的地址空間
void *shmat(int shm_id,const void *shm_addr,int shmflg);
shm_id:共享內存標識符
shm_addr:指定共享內存連接到當前進程的地址,通常為0,表示由系統來選擇。
shmflg:通常為0
返回值:指向共享內存第一個字節的指針,失敗返回-1

3)shmdt函數:當前進程分離共享內存
int shmdt(const void *shmaddr);

4)shmctl函數
和信號量的semctl函數類似,控制共享內存
int shmctl(int shm_id,int command,struct shmid_ds *buf);
shm_id:共享內存標識符
command: 有三個值
      IPC_STAT:獲取共享內存的狀態,把共享內存的shmid_ds結構復制到buf中。
      IPC_SET:設置共享內存的狀態,把buf復制到共享內存的shmid_ds結構。
      IPC_RMID:刪除共享內存
buf:共享內存管理結構體。具體結構可參考定義。

3.共享內存需要注意的問題
共享內存沒有同步機制,當多個進程同時向共享內存讀寫數據時,我們需要使用互斥鎖,讀寫鎖,信號量,條件變量等來確保數據的一致性。

4.共享內存使用示例

我們編寫了兩個程序shmread.c,shmwrite.c分別對共享內存讀和寫。
需要做到可寫,然后可讀,然后可寫,然后可讀,循環下去,直到寫入的是"end",結束寫讀進程。
我們使用了前面講的信號量來處理讀寫同步的問題。
示例代碼如下:

shm_data.h

#pragma once                                                                    
#define TEXT_SZ 2048
struct shared_use_st
{
        char text[TEXT_SZ];
};

shmread.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include"shmdata.h"
union semun  
{  
    int val;  
    struct semid_ds *buf;  
    unsigned short *arry;  
}; 
int sem_id;
int set_semvalue()  
{  
    //用於初始化信號量,在使用信號量前必須這樣做  
    union semun sem_union;  
  
    sem_union.val = 1;  
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)  
        return 0;  
    return 1;  
}  
  
void del_semvalue()  
{  
    //刪除信號量  
    union semun sem_union;  
  
    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)  
        fprintf(stderr, "Failed to delete semaphore\n");  
}  
int semaphore_p()
{
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = -1;//P()  
    sem_b.sem_flg = SEM_UNDO;  
    if(semop(sem_id, &sem_b, 1) == -1)  
    {  
        fprintf(stderr, "semaphore_p failed\n");  
        return 0;  
    }  
    return 1;   
}
int semaphore_v()
{
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = 1;//V()  
    sem_b.sem_flg = SEM_UNDO;  
    if(semop(sem_id, &sem_b, 1) == -1)  
    {  
        fprintf(stderr, "semaphore_v failed\n");  
        return 0;  
    }  
    return 1;  
}
int main()
{
    void *shm=NULL;
    struct shared_use_st *shared;
    int shmid;//共享內存標識符
    //創建共享內存
    shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
    if(shmid==-1)
    {
        fprintf(stderr,"shmget failed\n");
        exit(EXIT_FAILURE);
    }
    //將共享內存連接到當前進程的地址空間
    shm = shmat(shmid,0,0);
    if(shm==(void*)-1)
    {
        fprintf(stderr,"shmat failed\n");
        exit(EXIT_FAILURE);
    }
    printf("memory attached at %x\n",(int)shm);
    //設置共享內存
    shared = (struct shared_use_st*)shm;
    
    //新建信號量
    sem_id = semget((key_t)1234,1,0666|IPC_CREAT);

    //信號量初始化
    if(!set_semvalue())
    {
        fprintf(stderr,"init failed.\n");
        exit(EXIT_FAILURE);
    }
    
    while(1)
    {
        if(!semaphore_p())
            exit(EXIT_FAILURE);
        fflush(stdout);
        printf("you wrote:%s",shared->text);
        if(!semaphore_v())
            exit(EXIT_FAILURE);
        if(strncmp(shared->text,"end",3)==0)
            break;
        sleep(1);
    }
    
     //刪除信號量
    del_semvalue();

    //把共享內存從當前進程中分離
    if(shmdt(shm)==-1)
    {
        fprintf(stderr,"shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    //刪除共享內存
    if(shmctl(shmid,IPC_RMID,0)==-1)
    {
        fprintf(stderr,"shmctl(IPC_RMID) failed");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

shmwrite.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include"shmdata.h"
int sem_id;
int semaphore_p()
{
    //對信號量做減1操作,即等待P(sv)  
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = -1;//P()  
    sem_b.sem_flg = SEM_UNDO;  
    if(semop(sem_id, &sem_b, 1) == -1)  
    {  
        fprintf(stderr, "semaphore_p failed\n");  
        return 0;  
    }  
    return 1;  
}  
int semaphore_v()  
{  
    //這是一個釋放操作,它使信號量變為可用,即發送信號V(sv)  
    struct sembuf sem_b;  
    sem_b.sem_num = 0;  
    sem_b.sem_op = 1;//V()  
    sem_b.sem_flg = SEM_UNDO;  
    if(semop(sem_id, &sem_b, 1) == -1)  
    {  
        fprintf(stderr, "semaphore_v failed\n");  
        return 0;  
    }  
    return 1;  
}  
int main()
{
    sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
    
    void *shm = NULL;
    struct shared_use_st *shared = NULL;
    char buffer[200];
    int shmid;

    //創建共享內存
    shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);
    if(shm==-1)
    {
        fprintf(stderr,"shmget failed\n");
        exit(EXIT_FAILURE);
    }
    //將共享內存連接到當前進程的地址空間
    shm = shmat(shmid,(void*)0,0);
    if(shm == (void*)-1)
    {
        fprintf(stderr,"shmat failed\n");
        exit(EXIT_FAILURE);
    }
    printf("memory attached at %x\n",(int)shm);
    //設置共享內存
    shared = (struct shared_use_st*)shm;
    while(1)
    {
        if(!semaphore_p())
            exit(EXIT_FAILURE);
        //向共享內存中寫入數據
        fflush(stdout);
        printf("Enter some text...\n");
        fgets(buffer,200,stdin);
        strncpy(shared->text,buffer,TEXT_SZ);
        if(!semaphore_v())
            exit(EXIT_FAILURE);
        if(strncmp(shared->text,"end",3)==0)
            break;
        sleep(1);
    }
    //把共享內存從當前進程中分離
    if(shmdt(shm)==-1)
    {
        fprintf(stderr,"shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    sleep(2);
    exit(EXIT_SUCCESS);
}

輸出結果:

5.共享內存的優缺點
1)進程間通信方便,快速。
2)沒有提供同步機制,我們需要使用互斥鎖,讀寫鎖,信號量,條件變量等來確保數據的一致性。


免責聲明!

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



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