• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            doing5552

            記錄每日點滴,不枉人生一世

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              73 Posts :: 0 Stories :: 94 Comments :: 0 Trackbacks

            公告

            常用鏈接

            留言簿(24)

            我參與的團隊

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 455263
            • 排名 - 48

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜

            Linux 進程間通信 - 共享內存
            2010年04月02日 星期五 下午 02:49
            共享內存(Shared Memory)

            共享內存區域是被多個進程共享的一部分物理內存。如果多個進程都把該內存區域映射到自己的虛擬地址空間,則這些進程就都可以直接訪問該共享內存區域,從而 可以通過該區域進行通信。共享內存是進程間共享數據的一種最快的方法,一個進程向共享內存區域寫入了數據,共享這個內存區域的所有進程就可以立刻看到其中 的內容。這塊共享虛擬內存的頁面,出現在每一個共享該頁面的進程的頁表中。但是它不需要在所有進程的虛擬內存中都有相同的虛擬地址。



            圖 共享內存映射圖

            象所有的 System V IPC對象一樣,對于共享內存對象的訪問由key控制,并要進行訪問權限檢查。內存共享之后,對進程如何使用這塊內存就不再做檢查。它們必須依賴于其它機 制,比如System V的信號燈來同步對于共享內存區域的訪問。

            每一個新創建的共享內存對象都用一個shmid_kernel數據結構來表達。系統中所有的shmid_kernel數據結構都保存在shm_segs向 量表中,該向量表的每一個元素都是一個指向shmid_kernel數據結構的指針。shm_segs向量表的定義如下:
            struct shmid_kernel *shm_segs[SHMMNI];

            SHMMNI為128,表示系統中最多可以有128個共享內存對象。

            數據結構shmid_kernel的定義如下:
            struct shmid_kernel
            {   
            struct shmid_ds u;         /* the following are private */
            unsigned long shm_npages;  /* size of segment (pages) */
            unsigned long *shm_pages;  /* array of ptrs to frames -> SHMMAX */
            struct vm_area_struct *attaches;  /* descriptors for attaches */
            };

            其中:shm_pages是該共享內存對象的頁表,每個共享內存對象一個,它描述了如何把該共享內存區域映射到進程的地址空間的信息。
            shm_npages是該共享內存區域的大小,以頁為單位。
            shmid_ds是一個數據結構,它描述了這個共享內存區的認證信息,字節大小,最后一次粘附時間、分離時間、改變時間,創建該共享區域的進程,最后一次 對它操作的進程,當前有多少個進程在使用它等信息。其定義如下:
            struct shmid_ds {
            struct ipc_perm shm_perm;   /* operation perms */
            int shm_segsz;              /* size of segment (bytes) */
            __kernel_time_t shm_atime;  /* last attach time */
            __kernel_time_t shm_dtime;  /* last detach time */
            __kernel_time_t shm_ctime;  /* last change time */
            __kernel_ipc_pid_t shm_cpid; /* pid of creator */
            __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
            unsigned short shm_nattch;   /* no. of current attaches */
            unsigned short shm_unused;   /* compatibility */
            void *shm_unused2;           /* ditto - used by DIPC */
            void *shm_unused3;           /* unused */
            };

            attaches描述被共享的物理內存對象所映射的各進程的虛擬內存區域。每一個希望共享這塊內存的進程都必須通過系統調用將其粘附(attach)到它 的虛擬內存中。這一過程將為該進程創建了一個新的描述這塊共享內存的vm_area_struct數據結構。進程可以選擇共享內存在它的虛擬地址空間的位 置,也可以讓Linux為它選擇一塊足夠的空閑區域。

            這個新的vm_area_struct結構除了要連接到進程的內存結構mm中以外,還被連接到共享內存數據結構shmid_kernel的一個鏈表中,該 結構中的attaches指針指向該鏈表。vm_area_struct數據結構中專門提供了兩個指針:vm_next_shared和 vm_prev_shared,用于連接該共享區域在使用它的各進程中所對應的vm_area_struct數據結構。其實,虛擬內存并沒有在粘附的時候 創建,而要等到第一個進程試圖訪問它的時候才創建。



            圖 System V IPC 機制 - 共享內存

            Linux為共享內存提供了四種操作。
            1. 共享內存對象的創建或獲得。與其它兩種IPC機制一樣,進程在使用共享內存區域以前,必須通過系統調用sys_ipc (call值為SHMGET)創建一個鍵值為key的共享內存對象,或獲得已經存在的鍵值為key的某共享內存對象的引用標識符。以后對共享內存對象的訪 問都通過該引用標識符進行。對共享內存對象的創建或獲得由函數sys_shmget完成,其定義如下:
            int sys_shmget (key_t key, int size, int shmflg)

            這里key是表示該共享內存對象的鍵值,size是該共享內存區域的大小(以字節為單位),shmflg是標志(對該共享內存對象的特殊要求)。

            它所做的工作如下:
            1) 如果key == IPC_PRIVATE,則創建一個新的共享內存對象。
            * 算出size對應的頁數,檢查其合法性。
            * 搜索向量表shm_segs,為新創建的共享內存對象找一個空位置。
            * 申請一塊內存用于建立shmid_kernel數據結構,注意這里申請的內存區域大小不包括真正的共享內存區,實際上,要等到第一個進程試圖訪問它的時候 才真正創建共享內存區。
            * 根據該共享內存區所占用的頁數,為其申請一塊空間用于建立頁表(每頁4個字節),將頁表清0。
            * 填寫shmid_kernel數據結構,將其加入到向量表shm_segs中為其找到的空位置。
            * 返回該共享內存對象的引用標識符。

            2) 在向量表shm_segs中查找鍵值為key的共享內存對象,結果有三:
            * 如果沒有找到,而且在操作標志shmflg中沒有指明要創建新共享內存,則錯誤返回,否則創建一個新的共享內存對象。
            * 如果找到了,但該次操作要求必須創建一個鍵值為key的新對象,那么錯誤返回。
            * 否則,合法性、認證檢查,如有錯,則錯誤返回;否則,返回該內存對象的引用標識符。

            共享內存對象的創建者可以控制對于這塊內存的訪問權限和它的key是公開還是私有。如果有足夠的權限,它也可以把共享內存鎖定在物理內存中。
            參見include/linux/shm.h

            2. 粘附。在創建或獲得某個共享內存區域的引用標識符后,還必須將共享內存區域映射(粘附)到進程的虛擬地址空間,然后才能使用該共享內存區域。系統調用 sys_ipc(call值為SHMAT)用于共享內存區到進程虛擬地址空間的映射,而真正完成粘附動作的是函數sys_shmat,其定義如下:
            int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)

            其中:shmid是共享內存對象的引用標識符;
            shmaddr該共享內存區域在進程的虛擬地址空間對應的虛擬地址;
            shmflg是映射標志;
            raddr是實際映射的虛擬空間地址。

            該函數所做的工作如下:
            1) 根據shmid找到共享內存對象。
            2) 如果shmaddr為0,即用戶沒有指定該共享內存區域在它的虛擬空間中的位置,則由系統在進程的虛擬地址空間中為其找一塊區域(從1G開始);否則,就 用shmaddr作為映射的虛擬地址。
            3) 檢查虛擬地址的合法性(不能超過進程的最大虛擬空間大小—3G,不能太接近堆棧棧頂)。
            4) 認證檢查。
            5) 申請一塊內存用于建立數據結構vm_area_struct,填寫該結構。
            6) 檢查該內存區域,將其加入到進程的mm結構和該共享內存對象的vm_area_struct隊列中。

            共享內存的粘附只是創建一個vm_area_struct數據結構,并將其加入到相應的隊列中,此時并沒有創建真正的共享內存頁。

            當進程第一次訪問共享虛擬內存的某頁時,因為所有的共享內存頁還都沒有分配,所以會發生一個page fault異常。當Linux處理這個page fault的時候,它找到發生異常的虛擬地址所在的vm_area_struct數據結構。在該數據結構中包含有這類共享虛擬內存的一組處理例程,其中的 nopage操作用來處理虛擬頁對應的物理頁不存在的情況。對共享內存,該操作是shm_nopage(定義在ipc/shm.c中)。該操作在描述這個 共享內存的shmid_kernel數據結構的頁表shm_pages中查找發生page fault異常的虛擬地址所對應的頁表條目,看共享頁是否存在(頁表條目為0,表示共享頁是第一次使用)。如果不存在,它就分配一個物理頁,并為它創建一 個頁表條目。這個條目不但進入當前進程的頁表,同時也存到shmid_kernel數據結構的頁表shm_pages中。

            當下一個進程試圖訪問這塊內存并得到一個page fault的時候,經過同樣的路徑,也會走到函數shm_nopage。此時,該函數查看shmid_kernel數據結構的頁表shm_pages時, 發現共享頁已經存在,它只需把這里的頁表項填到進程頁表的相應位置即可,而不需要重新創建物理頁。所以,是第一個訪問共享內存頁的進程使得這一頁被創建, 而隨后訪問它的其它進程僅把此頁加到它們的虛擬地址空間。

            3. 分離。當進程不再需要共享虛擬內存的時候,它們與之分離(detach)。只要仍舊有其它進程在使用這塊內存,這種分離就只會影響當前的進程,而不會影響 其它進程。當前進程的vm_area_struct數據結構被從shmid_ds中刪除,并被釋放。當前進程的頁表也被更新,共享內存對應的虛擬內存頁被 標記為無效。當共享這塊內存的最后一個進程與之分離時,共享內存頁被釋放,同時,這塊共享內存的shmid_kernel數據結構也被釋放。

            系統調用sys_ipc(call值為SHMDT)用于共享內存區與進程虛擬地址空間的分離,而真正完成分離動作的是函數sys_shmdt,其定義如 下:
            int sys_shmdt (char *shmaddr)

            其中shmaddr是進程要分離的共享頁的開始虛擬地址。

            該函數搜索進程的內存結構中的所有vm_area_struct數據結構,找到地址shmaddr對應的一個,調用函數do_munmap將其釋放。

            在函數do_munmap中,將要釋放的vm_area_struct數據結構從進程的虛擬內存中摘下,清除它在進程頁表中對應的頁表項(可能占多個頁表 項),調用該共享內存數據結構vm_area_struct的操作例程中的close操作(此處為shm_close)做進一步的處理。

            在函數shm_close(定義在ipc/shm.c中)中,找到該共享內存對象在向量表shm_segs中的索引,從而找到該共享內存對象,將該共享內 存在當前進程中對應的vm_area_struct數據結構從對象的共享內存區域鏈表(由vm_next_share和vm_pprev_share指針 連接)中摘下。如果目前與該共享內存對象粘附的進程數變成了0,則釋放共享內存頁,釋放共享內存頁表,釋放該對象的shmid_kernel數據結構,將 向量表shm_segs中該共享內存對象所占用的項改為IPC_UNUSED。

            如果共享的虛擬內存沒有被鎖定在物理內存中,分離會更加復雜。因為在這種情況下,共享內存的頁可能在系統大量使用內存的時候被交換到系統的交換磁盤。為了 避免這種情況,可以通過下面的控制操作,將某共享內存頁鎖定在物理內存不允許向外交換。共享內存的換出和換入,已在第3章中討論。

            4. 控制。Linux在共享內存上實現的第四種操作是共享內存的控制(call值為SHMCTL的sys_ipc調用),它由函數sys_shmctl實現。 控制操作包括獲得共享內存對象的狀態,設置共享內存對象的參數(如uid、gid、mode、ctime等),將共享內存對象在內存中鎖定和釋放(在對象 的mode上增加或去除SHM_LOCKED標志),釋放共享內存對象資源等。

            共享內存提供了一種快速靈活的機制,它允許進程之間直接共享大量的數據,而無須使用拷貝或系統調用。共享內存的主要局限性是它不能提供同步,如果兩個進程 企圖修改相同的共享內存區域,由于內核不能串行化這些動作,因此寫的數據可能任意地互相混合。所以使用共享內存的進程必須設計它們自己的同步協議,如用信 號燈等。

            以下是使用共享內存機制進行進程間通信的基本操作:

            需要包含的頭文件:

            #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/shm.h>

            1.創建共享內存:

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

            參數說明:

            key:用來表示新建或者已經存在的共享內存去的關鍵字。

            size:創建共享內存的大小。

            shmflg:可以指定的特殊標志。IPC_CREATE,IPC_EXCL以及低九位的權限。

            eg:

            int shmid;

            shmid=shmget(IPC_PRIVATE,4096,IPC_CREATE|IPC_EXCL|0660);

            if(shmid==-1)

            perror("shmget()");

             

            2.連接共享內存

            char *shmat(int shmid,char *shmaddr,int shmflg);

            參數說明:

            shmid:共享內存的關鍵字

            shmaddr:指定共享內存出現在進程內存地址的什么位置,通常我們讓內核自己決定一個合適的地址位置,用的時候設為0。

            shmflg:制定特殊的標志位。

            eg:

            int shmid;

            char *shmp;

            shmp=shmat(shmid,0,0);

            if(shmp==(char *)(-1))

            perror("shmat()\n");

            3.使用共享內存

            在使用共享內存是需要注意的是,為防止內存訪問沖突,我們一般與信號量結合使用。

            4.分離共享內存:當程序不再需要共享內后,我們需要將共享內存分離以便對其進行釋放,分離共享內存的函數原形如下:

            int shmdt(char *shmaddr);

            5.

            釋放共享內存

            int shmctl(int shmid,int cmd,struct shmid_ds *buf);

            posted on 2010-07-18 21:16 doing5552 閱讀(673) 評論(0)  編輯 收藏 引用
            99久久无码一区人妻a黑| 久久免费线看线看| 久久99国产精品久久99小说| 日韩精品久久久久久久电影| 久久久精品人妻一区二区三区蜜桃 | 热综合一本伊人久久精品| 久久午夜福利无码1000合集| 精品人妻久久久久久888| 国产999精品久久久久久| 久久久久久曰本AV免费免费| 色综合久久中文色婷婷| 亚洲午夜久久久久妓女影院| 青青青国产成人久久111网站| 亚洲国产成人久久一区WWW| 潮喷大喷水系列无码久久精品| 狠狠色丁香婷婷综合久久来来去| 午夜天堂av天堂久久久| 无码人妻少妇久久中文字幕| 99久久免费国产特黄| 久久久久久精品久久久久| 国产精品永久久久久久久久久| 色综合久久久久综合体桃花网| 香港aa三级久久三级老师2021国产三级精品三级在 | Xx性欧美肥妇精品久久久久久| 97视频久久久| 久久激情五月丁香伊人| 精品久久一区二区| 狠狠色丁香久久综合五月| 热re99久久6国产精品免费| 久久人人爽人人爽人人片AV高清 | 久久人人爽人人澡人人高潮AV| 久久精品99久久香蕉国产色戒| 无码人妻久久一区二区三区蜜桃 | 无码人妻久久一区二区三区免费丨 | 久久er国产精品免费观看2| 亚洲第一极品精品无码久久| 久久久久久久波多野结衣高潮| 精品久久久久久久久免费影院| 日韩精品久久久久久久电影| 久久精品国产99国产精品亚洲| 综合久久国产九一剧情麻豆|