国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 操作系統(tǒng) > 正文

進程間通信之共享存儲

2024-06-28 13:27:52
字體:
供稿:網(wǎng)友
進程間通信之共享存儲

共享存儲允許兩個或更多進程共享一個給定的存儲區(qū)。因為數(shù)據(jù)不需要在客戶進程和服務(wù)器進程之間復(fù)制,所以這是最快的一種ipC。使用共享存儲時要掌握的唯一竅門是多個進程之間對一個給定存儲區(qū)的同步訪問。若服務(wù)器進程正在將數(shù)據(jù)放入共享存儲區(qū),則在它做完這一操作之前,客戶進程不應(yīng)當(dāng)去取這些數(shù)據(jù)。通常,信號量被用來實現(xiàn)對共享存儲訪問的同步。(記錄鎖也可以用于這種場合。)

內(nèi)核為每個共享存儲段設(shè)置了一個shmid_ds結(jié)構(gòu)。

struct shmid_ds {    struct ipc_perm    shm_perm;        size_t             shm_segsz;       /* size of segment in bytes */    pid_t              shm_lpid;        /* pid of last shmop() */    pid_t              shm_cpid;        /* pid of creator */    shmatt_t           shm_nattch;      /* number of current attaches */    time_t             shm_atime;       /* last-attach time */    time_t             shm_dtime;       /* last-detach tiime */    time_t             shm_ctime;       /* last-change time */    ...};

(按照支持共享存儲段的需要,每種實現(xiàn)會在shmid_ds結(jié)構(gòu)中增加其他成員。)

shmatt_t類型定義為不帶符號整型,它至少與unsigned short一樣大。

 為獲得一個共享存儲標(biāo)識符,調(diào)用的第一個函數(shù)通常是shmget

#include <sys/shm.h>int shmget(key_t key, size_t size, int flag);返回值:若成功則返回共享存儲ID,若出錯則返回-1

http://www.CUOXin.com/nufangrensheng/p/3561681.html中標(biāo)識符和鍵部分,說明了將key變換為標(biāo)識符的規(guī)則,討論了是否創(chuàng)建一個新集合,或是引用一個現(xiàn)存集合。

當(dāng)創(chuàng)建一個新段時,初始化shmid_ds結(jié)構(gòu)的下列成員:

  • ipc_perm結(jié)構(gòu)按http://www.CUOXin.com/nufangrensheng/p/3561681.html中權(quán)限結(jié)構(gòu)所述進行初始化。該結(jié)構(gòu)中mode成員按flag中的相應(yīng)權(quán)限位設(shè)置。這些權(quán)限用http://www.CUOXin.com/nufangrensheng/p/3561681.html表15-2中的常量指定。
  • shm_lpid、shm_nattach、shm_atime、以及shm_dtime都設(shè)置為0。
  • shm_ctime設(shè)置為當(dāng)前時間。
  • shm_segsz設(shè)置為請求的長度(size)。

參數(shù)size是該共享存儲段的長度(單位:字節(jié))。實現(xiàn)通常將其向上取為系統(tǒng)頁長的整數(shù)倍。但是,若應(yīng)用指定的size值并非系統(tǒng)頁長的整數(shù)倍,那么最后一頁的余下部分是不可使用的。如果正在創(chuàng)建一個新段(一般是在服務(wù)器進程中),則必須指定其size。如果正在引用一個現(xiàn)存的段(一個客戶進程),則將size指定為0。當(dāng)創(chuàng)建一新段時,段內(nèi)的內(nèi)容初始化為0。

shmctl函數(shù)對共享存儲段執(zhí)行多種操作。

#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);返回值:若成功則返回0,若出錯則返回-1

cmd參數(shù)指定下列5中命令中一種,使其在shmid指定的段上執(zhí)行。

IPC_STAT    取此段的shmid_ds結(jié)構(gòu),并將它存放在由buf指向的結(jié)構(gòu)中。

IPC_SET      按buf指向結(jié)構(gòu)中的值設(shè)置與此段相關(guān)結(jié)構(gòu)中的下列三個字段:shm_perm.uid、shm_perm.gid以及shm_perm.mode。此命令只能由下列兩種進程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid的進程;另一種是具有超級用戶特權(quán)的進程。

IPC_RMID    從系統(tǒng)中刪除該共享存儲段。因為每個共享存儲段有一個連接計數(shù)(shmid_ds結(jié)構(gòu)中的shm_nattach字段),所以除非使用該段的最后一個進程終止或與該段脫節(jié),否則不會實際上刪除該存儲段。不管此段是否仍在使用,該段標(biāo)識符立即被刪除,所以不能再用shmat與該段連接。此命令只能由下列兩種進程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid的進程,另一種是具有超級用戶特權(quán)的進程。

linux和Solaris提供了下列另外兩種命令,但它們并非Single UNIX Specification的組成部分:

SHM_LOCK        將共享存儲段鎖定在內(nèi)存中。此命令只能由超級用戶執(zhí)行。

SHM_UNLOCK    解鎖共享存儲段。此命令只能由超級用戶執(zhí)行。

一旦創(chuàng)建了一個共享存儲段,進程就可調(diào)用shmat將其連接到它的地址空間中。

#include <sys/shm.h>void *shmat(int shmid, const void *addr, int flag);返回值:若成功則返回指向共享存儲的指針,若出錯則返回-1

共享存儲段連接到調(diào)用進程的哪個地址上與addr參數(shù)以及在flag中是否指定SHM_RND位有關(guān)。

  • 如果addr為0,則此段連接到由內(nèi)核選擇的第一個可用地址上。這是推薦的使用方式。
  • 如果addr非0,并且沒有指定SHM_RND,則此段連接到addr所指定的地址上。
  • 如果addr非0,并且指定了SHM_RND,則此段連接到(addr-(addr mod ulus SHMLBA))所表示的地址上。SHM_RND命令的意思是“取整”。SHMLBA的意思是“低邊界地址倍數(shù)”,它總是2的乘方。該算式是將地址向下取最近1個SHMLBA的倍數(shù)。

除非只計劃在一種硬件上運行應(yīng)用程序(這在當(dāng)今是不大可能的),否則不應(yīng)指定共享段所連接到的地址。所以一般應(yīng)指定addr為0,以便由內(nèi)核選擇地址。

如果在flag中指定了SHM_RDONLY位,則以只讀方式連接此段。否則以讀寫方式連接此段。

shmat的返回值是該段所連接的實際地址,如果出錯則返回-1。如果shmat成功執(zhí)行,那么內(nèi)核將使該共享存儲段shmid_ds結(jié)構(gòu)中的shm_nattach計數(shù)器值加1.

當(dāng)對共享存儲段的操作已經(jīng)結(jié)束時,則調(diào)用shmdt脫接該段。注意,這并不從系統(tǒng)中刪除其標(biāo)識符以及數(shù)據(jù)結(jié)構(gòu)。該標(biāo)識符仍然存在,直至某個進程(一般是服務(wù)器進程)調(diào)用shmctl(帶命令I(lǐng)PC_RMID)特地刪除它。

#include <sys/shm.h>int shmdt(void *addr);返回值:若成功則返回0,若出錯則返回-1

addr參數(shù)是以前調(diào)用shmat時的返回值。如果成功,shmdt將使相關(guān)shmid_ds結(jié)構(gòu)中的shm_nattach計數(shù)器值減1。

實例

內(nèi)核將以addr=0連接的共享存儲段放在什么位置上與系統(tǒng)密切相關(guān)。程序清單15-11打印以寫信息,它們與特定系統(tǒng)將各種不同類型的數(shù)據(jù)放在什么位置有關(guān)。

程序清單15-11 打印各種不同類型的數(shù)據(jù)所存放的位置

#include "apue.h"#include <sys/shm.h>#define ARRAY_SIZE    40000#define MALLOC_SIZE    100000#define SHM_SIZE    100000#define SHM_MODE    0600    /* user read/write */char    array[ARRAY_SIZE];    /* uninitialized data = bss */int main(void){    int     shmid;    char    *ptr, *shmptr;        PRintf("array[] from %lx to %lx/n", (unsigned long)&array[0],         (unsigned long)&array[ARRAY_SIZE]);    printf("stack aound %lx/n", (unsigned long)&shmid);        if((ptr = malloc(MALLOC_SIZE)) == NULL)        err_sys("malloc error");    printf("malloced from %lx to %lx/n", (unsigned long)ptr,         (unsigned long)ptr+MALLOC_SIZE);    if((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)        err_sys("shmget error");    if((shmptr = shmat(shmid, 0, 0)) == (void *)-1)        err_sys("shmat error");    printf("shared memory attched from %lx to %lx/n",        (unsigned long)shmptr, (unsigned long)shmptr+SHM_SIZE);    if(shmctl(shmid, IPC_RMID, 0) < 0)        err_sys("shmctl error");    exit(0);}

本人系統(tǒng)上運行此程序,根據(jù)輸出結(jié)果可以描繪存儲區(qū)大致分布,發(fā)現(xiàn)它與http://www.CUOXin.com/nufangrensheng/p/3508169.html中的圖7-3中所示的典型存儲區(qū)布局類似。

http://www.CUOXin.com/nufangrensheng/p/3559664.html中曾說明mmap函數(shù)可將一個文件的若干部分映射至進程地址空間。這在概念上類似與用shmat XSI IPC函數(shù)連接一共享存儲段。兩者之間的主要區(qū)別是:用mmap映射的存儲段是與文件相關(guān)聯(lián)的,而XSI共享存儲段則并無這種關(guān)聯(lián)。

實例:/dev/zero的存儲映射

共享存儲可由不相關(guān)的進程使用。但如果進程是相關(guān)的,則某些實現(xiàn)提供了一種不同的技術(shù)。

在讀設(shè)備/dev/zero時,該設(shè)備是0字節(jié)的無限資源。它也接收寫向它的任何數(shù)據(jù)。但又忽略這些數(shù)據(jù)。我們對此設(shè)備作為IPC的興趣在于,當(dāng)對其進行存儲映射時,它具有一些特殊的性質(zhì):

  • 創(chuàng)建一個無名(unnamed)存儲區(qū),其長度是mmap的第二個參數(shù),將其向上取整為系統(tǒng)的最近頁長。
  • 存儲區(qū)都初始化為0.
  • 如果多個進程的共同祖先進程對mmap指定了MAP_SHARED標(biāo)志,則這些進程可共享此存儲區(qū)。

程序清單15-12 在父、子進程間使用/dev/zero存儲映射I/O的IPC

#include "apue.h"#include <fcntl.h>#include <sys/mman.h>#define NLOOPS    1000#define SIZE      sizeof(long);    /* size of shared memory area */static intupdate(long *ptr){    return((*ptr)++);    /* return value before increment */}intmain(void){    int      fd, i, counter;    pid_t    pid;        void     *area;    if((fd = open("/dev/zero", O_RDWR)) < 0)        err_sys("open error");    if((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)        err_sys("mmap error");    close(fd);    /* can close /dev/zero now that it's mapped */TELL_WAIT();    if((pid = fork()) < 0)    {        err_sys("fork error");    }    else if(pid > 0)    /* parent */    {        for(i=0; i<NLOOPS; i+=2)        {            if((counter = update((long *)area)) != i)                err_quit("parent: expected %d, got %d", i, counter);            TELL_CHILD(pid);            WAIT_CHILD();        }    }    else    {        for(i = 1; i < NLOOPS + 1; i += 2)        {            WAIT_PARENT();            if((counter = update((long *)area)) != i)                err_quit("child: expected %d, got %d", i, counter);            TELL_PARENT(getppid());        }    }    exit(0);}

它打開此/dev/zero設(shè)備,然后指定長整型的長度調(diào)用mmap。注意,一旦存儲區(qū)映射成功,就關(guān)閉此設(shè)備。然后,進程創(chuàng)建一個子進程。因為在調(diào)用mmap時指定了MAP_SHARED,所以一個進程寫到存儲映射區(qū)的數(shù)據(jù)可由另一個進程見到。(如果已指定MAP_PRIVATE,則此示例程序不能工作)

然后,父、子進程交替運行,使用http://www.CUOXin.com/nufangrensheng/p/3510306.html中的同步函數(shù)各自對共享存儲映射區(qū)中的長整型數(shù)加1。存儲映射區(qū)由mmap初始化為0。父進程先對它進行增1操作,使其成為1,然后子進程對其進程增1操作,使其成為2,然后父進程使其成為3......注意,當(dāng)在update函數(shù)中對長整型值增1時,因為增加的是其值,而不是指針,所以必須使用括號。

以上述方式使用/dev/zero的優(yōu)點是:在調(diào)用mmap創(chuàng)建映射區(qū)之前,無需存在一個實際文件。映射/dev/zero自動創(chuàng)建一個指定長度的映射區(qū)。這種技術(shù)的缺點是:它只在相關(guān)進程間起作用。但在相關(guān)進程之間使用線程可能更為簡單、有效。注意,無論使用哪一種技術(shù),都需對共享數(shù)據(jù)進行同步訪問。

實例:匿名存儲映射

很多實現(xiàn)提供了一種類似于/dev/zero的設(shè)施,稱為匿名存儲映射。為了使用這種功能,在調(diào)用mmap時指定MAP_ANON標(biāo)志,并將文件描述符指定為-1。結(jié)果得到的區(qū)域是匿名的(因為它并不通過一個文件描述符與一個路徑名相結(jié)合),并且創(chuàng)建一個可與后代進程共享的存儲區(qū)。

注意,Linux為此定義了MAP_ANONYMOUS標(biāo)志,并將MAP_ANON標(biāo)志定義為與它相同的值以改善應(yīng)用的可移植性。

為使程序清單15-12所示的程序應(yīng)用這種特征,對它做了三處修改:一是刪除了對于/dev/zero的open語句;二是刪除了對于fd的close語句;三是將mmap調(diào)用修改成:

if((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)

的形式。在此調(diào)用中,指定了MAP_ANON標(biāo)志,并將文件描述符取為-1。程序的其余部分則沒有改變。

最后兩個例子說明了在多個相關(guān)進程之間如何使用共享存儲段。如果在無關(guān)進程之間使用共享存儲段,那么有兩種替換的方法。其一是應(yīng)用程序使用XSI共享存儲函數(shù);另一種是使用mmap將同一文件映射至它們的地址空間,為此使用MAP_SHARED標(biāo)志。

本篇博文內(nèi)容摘自《UNIX環(huán)境高級編程》(第二版),僅作個人學(xué)習(xí)記錄所用。關(guān)于本書可參考:http://www.apuebook.com/


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 枝江市| 双峰县| 金堂县| 沅江市| 蓝田县| 望奎县| 抚松县| 宣恩县| 博客| 钦州市| 栖霞市| 张家港市| 工布江达县| 凤山市| 敦煌市| 连山| 大化| 屏东县| 东平县| 柳河县| 西昌市| 北票市| 奎屯市| 保靖县| 葵青区| 民县| 和硕县| 介休市| 龙海市| 微山县| 原阳县| 沾化县| 土默特右旗| 宜丰县| 化德县| 灵宝市| 云霄县| 章丘市| 安福县| 百色市| 梅州市|