Linux 進程通信(共享內存區)


共享內存是由內核出於在多個進程間交換信息的目的而留出的一塊內存區(段)。
如果段的權限設置恰當,每個要訪問該段內存的進程都可以把它映像到自己的私有地址空間中。
如果一個進程更新了段中的數據,其他進程也立即會看到更新。
由一個進程創建的段,也可以由另一個進程讀寫。
每個進程都把它自己對共享內存的映像放入自己的地址空間。
創建共享內存區
int shmget(key_t key,size_t size,int shm-flg);
參數key既可以是IPC_PRIVATE(IPC_PRIVATE表示讓系統分配一個Key),也可以是ftok函數返回的一個關鍵字。
參數size指定段的大小。
參數flgs--八進制數,0666,轉化為二進制后分別代表rw-rw-rw-
shmget成功返回段標識符,失敗返回-1(一般不會創建失敗,除非系統沒有內存).
注意:只有創建權限是0666的才可以用命令行"ipcs -m"查看,其他類型權限的共享內存區無法被這個命令所查看。
//創建共享內存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/ipc.h>
#include <sys/shm.h>

int main(int arg, char *args[])
{
    int shmid=shmget(IPC_PRIVATE,sizeof(char)*1024,0666);
    if(shmid==-1)
    {
        printf("error\n");
        return -1;
    }
    printf("創建共享內存成功!內存段標識符是%d\n",shmid);
    return 0;
}
在命令行執行"ipcs -m "顯示已經成功的創建了一塊共享內存區。
nattch字段顯示已經附加到這個內存區的進程數。

 

附加共享內存區
void * shmat(int shmid,void * shmaddr,int shmflg);
int shmdt(const * void shmaddr);
參數shmid是要附加的共享內存區標識符(在命令行執行ipcs -m 顯示)。
總是把參數shmaddr設為0,0表示系統會自動在被附加的進程內創建一個內存段,映射到共享內存區。當然也可以指定一個被附加的進程內的一塊內存,但是需要用戶自己malloc分配內存,並將地址傳給shmaddr參數,比較麻煩,一般使用系統自動分配。
參數shmflg可以為SHM_RDONLY,這意味着附加段是只讀的,參數為0時,表示可以讀寫。
shmat成功返回被附加了段的地址,失敗返回-1,並設置errno,雖然返回值是指針類型,但是返回的的確是-1。
函數shmdt是將附加在shmaddr的段從調用進程的地址空間分離出去,這個地址必須是shmat返回的(即從自己進程內釋放被附加的共享內存,強調不是釋放系統的那個共享內存)。
注意:進程里附加共享內存區的大小一定是和系統共享內存一樣大,為了方便映射(兩塊內存不一樣大如何同步數據?)。
//創建附加共享內存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/shm.h>

int main(int arg, char *args[])
{
    if (arg < 3)
    {
        printf("請輸入2個參數!\n");
        return -1;
    }
    int flag = 0;
    int shmid = 0;
    shmid = atoi(args[1]);
    flag = atoi(args[2]);
    //創建本進程被附加的共享內存
    void * shmbuf = NULL;
    shmbuf = shmat(shmid, 0, 0);
    /*
     編譯警告:warning: cast from pointer to integer of different size
     這句警告意思是指針類型大小和Int類型大小不同,根據以前的知識,指針都是4個字節大小,
     其實指針的大小不是固定的4個字節。32位cpu的計算機上是4字節,64位cpu的計算機上是8個字節。
     這是因為32位計算機中的地址是32位的(可以在32位系統下查看指針的值例如--0x003bf9a3)
     但是在64位計算機中地址是64位的(可以在64位系統下查看指針的值例如--0xffffffffffffffff)
     十六進制數字的1位等於二進制的4位
     雖然指針的值在32位系統和64位系統中有區別,但是一個字節的大小在兩個系統中是相同的,
     舉例說Int類型數據,不管是在32位系統和64位系統中都在內存中占據4個字節大小的內存空間
     */
    if ((int) shmbuf == -1)
    {
        printf("創建附加共享內存區失敗!error message:%s\n", strerror(errno));
        return -1;
    }
    sleep(10);
    if (flag == 1)
    {
        //將數據寫入本進程自己的附加共享內存區
        //這里系統共享內存區的大小是1kb
        read(STDIN_FILENO, shmbuf, sizeof(char) * 1024);
    } else if (flag == 2)
    {
        printf("%s", shmbuf);
    }
    return 0;
}
shmdt函數
--功能:將共享內存段與當前進程脫離
--參數
    shmarr:由shmat所返回的指針
--返回值:成功返回0,失敗返回-1
--注意:將共享內存段與當前進程脫離不等於刪除共享內存

 

釋放共享內存區
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數shmid是共享內存區段標識符,
參數cmd一般有三個值IPC_STAT(獲取共享內存區的狀態,由第三個參數獲取狀態),IPC_SET(設置共享內存區),IPC_RMID(刪除共享內存區,此時第三個參數一般傳0);
shmctl成功返回0,失敗返回-1,並且設置errno的值
//創建共享內存區
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(int arg,char * args[])
{
    if(arg<2)
    {
        printf("請輸入一個參數!\n");
        return -1;
    }
    int shmid=atoi(args[1]);
    int flag=shmctl(shmid,IPC_RMID,0);
    if(flag<0)
    {
        printf("error message:%s\n",strerror(errno));
        return -1;
    }
    printf("返回值是%d\n",flag);
    return 0;
}
命令行釋放共享內存區
在命令行執行"ipcrm -m shmid" 或者 "ipcrm shm shmid"

 


免責聲明!

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



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