歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux進程間通信(IPC)編程實踐(七)共享內存的使用-System V共享內存(API)

Linux進程間通信(IPC)編程實踐(七)共享內存的使用-System V共享內存(API)

日期:2017/3/1 12:20:51   编辑:關於Linux

上一篇博文提到的系統調用mmap通過映射一個普通文件實現共享內存。那麼本文中介紹的System V 共享內存則是通過映射特殊文件系統shm中的文件實現進程間的共享內存通信。也就是說,每個共享內存區域對應特殊文件系統shm中的一個文件。執行過程是先調用shmget,再調用shmat。對於每個共享的內存區,內核維護如下的信息結構,定義在頭文件中。

//System V 共享內存基本數據結構  
struct shmid_ds  
{  
    struct ipc_perm shm_perm;    /* Ownership and permissions: System V IPC所共有的數據結構 */  
    size_t          shm_segsz;   /* Size of segment (bytes): 共享內存段的大小 */  
    time_t          shm_atime;   /* Last attach time */  
    time_t          shm_dtime;   /* Last detach time */  
    time_t          shm_ctime;   /* Last change time */  
    pid_t           shm_cpid;    /* PID of creator */  
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */  
    shmatt_t        shm_nattch;  /* No. of current attaches */  
    ...  
};  

System V共享內存常用API

#include 
#include 
/*
創建一個新的內存共享區或者訪問一個已經存在的共享內存區
返回共享內存區標識符
*/
int shmget(key_t key, size_t size, int shmflg);
/*
創建或打開一個共享內存區後,調用shmat把它連接到調用進程的地址空間
*/
void *shmat(int shmid, const void *shmaddr,int shmflg);
/*

當一個進程完成某個共享內存區的使用時,調用shmdt斷開這個內存區
*/
int shmdt(const void *shmaddr);
/*
對內存區進行多種操作
cmd取值:
IPC_RMID:從系統中刪除由shmid標識的共享內存區並拆除它
IPC_SET:給指定的共享內存區設置其shmid_ds結果成員
IPC_STAT:通過buff參數向調用者返回所指定共享內存區當前的shmid_ds結構
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

調用shmget函數使用指定的路徑名和長度創建一個共享內存區,如下:

int shmget(key_t key, size_t size, int shmflg);

創建共享內存,並將該內存的內容初始化為0;

打開一個已經存在共享內存, 如果打開時不知道共享內存的大小, 可以將size指定為0, shmflg可以指定為0(按照默認的權限打開);

參數:

key:這個共享內存段名字;

size:共享內存大小(bytes);

shmflg:用法類似msgget中的msgflg參數;

返回值:

成功返回一個非負整數,即該共享內存段的標識碼;失敗返回-1

/**示例: 創建並打開一個共享內存 **/  
int main(int argc,char **argv)  
{  
    const int SHM_SIZE = 1024;  
    int shmid = shmget(0x1234, SHM_SIZE, 0666|IPC_CREAT);  
    if (shmid == -1)  
        err_exit("shmget error");  
    cout << "share memory get success" << endl;  
}  
shmat

void *shmat(int shmid, const void *shmaddr, int shmflg);

連接到本進程地址空間, 成功連接之後, 對該內存的操作就與malloc來的一塊內存非常類似了, 而且如果這塊內存中有數據, 則就可以直接將其中的數據取出來。

參數:

shmaddr:指定連接的地址(大小不知道的話推薦使用NULL)

shmflg:一般指定為0, 表示可讀,可寫; 而它的另外兩個可能取值是SHM_RND和SHM_RDONLY(見下)

返回值:

成功返回一個指針,指向共享內存起始地址;失敗返回(void *) -1


shmaddr與shmflg組合說明

shmaddr為NULL

Linux內核自動為進程連接到進程的內存(推薦使用)

shmaddr不為NULL且shmflg無SHM_RND標記

以shmaddr為連接地址

shmaddr不為NULL且shmflg設置了SHM_RND標記

連接的地址會自動向下調整為SHMLBA的整數倍;

公式:shmaddr - (shmaddr % SHMLBA)

SHMLBA為內存頁面的大小(4K)

shmflg=SHM_RDONLY

只讀共享內存, 不然的話就是可讀,可寫的, 注意: 此處沒有可讀,可寫這個概念

shmdt

當一個進程完成某個共享內存區的使用時,調用shmdt斷開這個內存區:

int shmdt(const void *shmaddr);

參數:

shmaddr: 由shmat所返回的指針

注意:將共享內存段與當前進程脫離不等於刪除共享內存段

/** 示例: 將數據寫入/讀出共享內存 
程序write: 將數據寫入共享內存 
程序read: 將數據讀出共享內存(當然, 可以讀取N多次) 
**/  
//write程序  
struct Student  
{  
    char name[26];  
    int age;  
};  
int main(int argc,char **argv)  
{  
    int shmid = shmget(0x1234, sizeof(Student), 0666|IPC_CREAT);  
    if (shmid == -1)  
        err_exit("shmget error");  
  
    // 以可讀, 可寫的方式連接該共享內存  
    Student *p = (Student *)shmat(shmid, NULL, 0);  
    if (p == (void *)-1)  
        err_exit("shmat error");  
    strcpy(p->name, "xiaofang");  
    p->age = 22;  
    shmdt(p);  
}  
//read程序  
int main(int argc,char **argv)  
{  
    int shmid = shmget(0x1234, 0, 0);  
    if (shmid == -1)  
        err_exit("shmget error");  
  
    // 以只讀方式連接該共享內存  
    Student *p = (Student *)shmat(shmid, NULL, 0);  
    if (p == (void *)-1)  
        err_exit("shmat error");  
  
    // 直接將其中的內容打印輸出  
    cout << "name: " << p->name << ", age: " << p->age << endl;  
    shmdt(p);  
}  
shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf); 

設置/獲取共享內存屬性
參數:
cmd:將要采取的動作(三個取值見下)
buf:指向一個保存著共享內存的模式狀態和訪問權限的數據結構

IPC_RMID:從系統中刪除由shmid標識的共享內存區並拆除它
IPC_SET:給指定的共享內存區設置其shmid_ds結果成員
IPC_STAT:通過buff參數向調用者返回所指定共享內存區當前的shmid_ds結構

System V共享內存小結:

1)如果共享內存已經與所有訪問它的進程斷開了連接,則調用IPC_RMID子命令後,系統將立即刪除共享內存的標識符,並刪除該共享內存區,以及所有相關的數據結構;

2)如果仍有別的進程與該共享內存保持連接,則調用IPC_RMID子命令後,該共享內存並不會被立即從系統中刪除,而是被設置為IPC_PRIVATE狀態,並被標記為"已被刪除"(使用ipcs命令可以看到dest字段);直到已有連接全部斷開,該共享內存才會最終從系統中消失。

3)另外:一旦通過shmctl對共享內存進行了刪除操作,則該共享內存將不能再接受任何新的連接,即使它依然存在於系統中!所以,可以確知,在對共享內存刪除之後不可能再有新的連接,則執行刪除操作是安全的;否則,在刪除操作之後如仍有新的連接發生,則這些連接都將可能失敗。

/** 示例: 刪除共享內存 **/  
int main(int argc,char *argv[])  
{  
    int shmid = shmget(0x1234, 0, 0);  
    if (shmid == -1)  
        err_exit("shmget error");  
  
    if (shmctl(shmid, IPC_RMID, NULL) == -1)  
        err_exit("shmctl IPC_RMID error");  
    cout << "share memory remove success" << endl;  
}  



Copyright © Linux教程網 All Rights Reserved