Linux進程間通信 - 共享內存


0. 前言

   進程是一個獨立的資源管理單元,不同進程間的資源是獨立的,不能在一個進程中訪問另一個進程的用戶空間和內存空間。但是,進程不是孤立的,不同進程之間需要信息的交互和狀態的傳遞,因此需要進程間數據的傳遞、同步和異步的機制。

    當然,這些機制不能由哪一個進程進行直接管理,只能由操作系統來完成其管理和維護,Linux提供了大量的進程間通信機制,包括同一個主機下的不同進程和網絡主機間的進程通信,如下圖所示:
mark

  • 同主機間的信息交互
  • 無名管道
    特點:多用於親緣關系進程間通信,方向為單向;為阻塞讀寫;通信進程雙方退出后自動消失
    問題:多進程用同一管道通信容易造成交叉讀寫的問題
  • 有名管道
    FIFO(First In First Out),方向為單向(雙向需兩個FIFO),以磁盤文件的方式存在;通信雙方一方不存在則阻塞
  • 消息隊列
    可用於同主機任意多進程的通信,但其可存放的數據有限,應用於少量的數據傳遞
  • 共享內存
    可實現同主機任意進程間大量數據的通信,但多進程對共享內存的訪問存在着競爭
  • 同主機進程間同步機制:信號量(Semaphore)
  • 同主機進程間異步機制:信號(Signal)
  • 網絡主機間數據交互:Socket(套接字)

1. 共享內存 - 概念

mark
共享內存,主要是實現進程間大量數據的傳輸。所謂共享內存,即在內存中開辟一段特殊的內存空間,多個進程可互斥訪問,該內存空間具有自身特有的數據結構。
共享內存的數據結構如下 - struct shmid_ds

/*  Come from /usr/include/linux/shm.h  */

/* Obsolete, used only for backwards compatibility and libc5 compiles */
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 */
};

多個進程在使用此共享內存空間時候,必須在進程地址空間與共享內存地址空間之間建立連接,即將共享內存空間掛載到進程中;
共享內存是由一個進程開辟,其它任何進程都可以掛載;
共享內存並不會隨着進程的退出而消失,因此最后不使用此內存空間時,必須要手動刪除

2. 共享內存管理

1). 創建共享內存 - shmget

  • 作用
    創建一個共享內存空間

  • 頭文件

      #include <sys/shm.h>
    
  • 函數原型

      int shmget(key_t key, size_t size, int shmflg)
    
  • 參數

  • key: ftok()的返回值
  • size: 想要創建的共享內存的大小 (字節)
  • shmflg: 共享內存段的創建標識
Macro No. Description Head File
IPC_CREAT 01000 若key內存段不存在,則創建;否則返回內存首地址 /usr/include/linux/ipc.h
IPC_EXCL 02000 若key內存段存在,則返回錯誤
IPC_NOWAIT 04000 不等待直接返回
shm_r 0400 可讀 /usr/include/linux/shm.h
shm_w 0200 可寫 /usr/include/linux/shm.h
  • 返回值
    成功:共享內存空間的標志 shm_id
    失敗:-1

2). 共享內存控制 - shmctl

  • 作用
    對共享內存進程操作,包括:讀取/設置狀態,刪除操作

  • 頭文件

      #include<sys/shm.h>
    
  • 函數原型

      int shmctl(int shmid, int cmd, struct shmid_ds *buf)
    
  • 參數

  • shmid : shmget()返回值
  • buf:臨時共享內存變量信息
  • cmd :
Macro No. Description Return
IPC_RMID 0 刪除 0
IPC_SET 1 設置 ipc_perm 參數 0
IPC_STAT 2 獲取 ipc_perm 參數
IPC_INFO 3 ipcs命令
SHM_LOCK 11 鎖定共享內存段 0
SHM_UNLOCK 12 解鎖共享內存段 0
  • 返回值
    成功:
    失敗:-1

3). 共享內存映射 - shmat()

  • 作用
    將共享內存空間掛載到進程中

  • 頭文件

      #include <sys/shm.h>
    
  • 函數原型

      void *shmat(int shmid, const void *shmaddr, int shmflg)
    
  • 參數

  • shmid : shmget()返回值
  • shmaddr: 共享內存的映射地址,一般為0(由系統自動分配地址)
  • shmflg : 訪問權限和映射條件
Macro No. Descripton Remind
- 0 默認有讀寫權限 常用
SHM_RDONLY 010000 只讀
SHM_RDN 020000 Round attach address to SHMLBA boundary
SHM_REMAP 040000 take-over region on attach
  • 返回值
    成功:共享內存段首地址
    失敗:NULL / (void *)-1

4). 共享內存分離 - shmdt()

  • 作用
    將進程與共享內存空間分離 (只是與共享內存不再有聯系,並沒有刪除共享內存)

  • 頭文件

       #include <sys/shm.h>
    
  • 函數原型

      int shmdt(const void *shmaddr)
    
  • 參數

shmaddr:共享內存的首地址

  • 返回值
    成功: 0
    失敗: -1

3. 示例代碼:

mark
兩個進程通過共享內存傳輸數據,因共享內存不可同時讀寫,因此采用二元信號量進行進程互斥,具體操作如下:

  • init: 設置信號量為0,此時只允許寫入,不允許讀取(因為共享內存沒有數據);
  • Sender: 在sem=0時,寫入數據到共享內存(阻塞讀);寫入完成后,sem=1,此時可以讀取,不可以寫入;
  • Receiver: 在sem=1時,讀取數據;讀取完成后,sem=0,此時只允許寫入。

1- Sender.c

/*
 * Filename: Sender.c
 * Description: 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>

int main(int argc, char *argv[])
{
    key_t  key;
    int shm_id;
    int sem_id;
    int value = 0;

    //1.Product the key
    key = ftok(".", 0xFF);

    //2. Creat semaphore for visit the shared memory
    sem_id = semget(key, 1, IPC_CREAT|0644);
    if(-1 == sem_id)
    {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    //3. init the semaphore, sem=0
    if(-1 == (semctl(sem_id, 0, SETVAL, value)))
    {
        perror("semctl");
        exit(EXIT_FAILURE);
    }

    //4. Creat the shared memory(1K bytes)
    shm_id = shmget(key, 1024, IPC_CREAT|0644);
    if(-1 == shm_id)
    {
        perror("shmget");
        exit(EXIT_FAILURE);
    }

    //5. attach the shm_id to this process
    char *shm_ptr;
    shm_ptr = shmat(shm_id, NULL, 0);
    if(NULL == shm_ptr)
    {
        perror("shmat");
        exit(EXIT_FAILURE);
    }

    //6. Operation procedure
    struct sembuf sem_b;
    sem_b.sem_num = 0;      //first sem(index=0)
    sem_b.sem_flg = SEM_UNDO;
    sem_b.sem_op = 1;           //Increase 1,make sem=1
    
    while(1)
    {
        if(0 == (value = semctl(sem_id, 0, GETVAL)))
        {
            printf("\nNow, snd message process running:\n");
            printf("\tInput the snd message:  ");
            scanf("%s", shm_ptr);

            if(-1 == semop(sem_id, &sem_b, 1))
            {
                perror("semop");
                exit(EXIT_FAILURE);
            }
        }

        //if enter "end", then end the process
        if(0 == (strcmp(shm_ptr ,"end")))
        {
            printf("\nExit sender process now!\n");
            break;
        }
    }

    shmdt(shm_ptr);

    return 0;
}

2- Receiver.c

/*
 * Filename: Receiver.c
 * Description: 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>

int main(int argc, char *argv[])
{
    key_t  key;
    int shm_id;
    int sem_id;
    int value = 0;

    //1.Product the key
    key = ftok(".", 0xFF);

    //2. Creat semaphore for visit the shared memory
    sem_id = semget(key, 1, IPC_CREAT|0644);
    if(-1 == sem_id)
    {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    //3. init the semaphore, sem=0
    if(-1 == (semctl(sem_id, 0, SETVAL, value)))
    {
        perror("semctl");
        exit(EXIT_FAILURE);
    }

    //4. Creat the shared memory(1K bytes)
    shm_id = shmget(key, 1024, IPC_CREAT|0644);
    if(-1 == shm_id)
    {
        perror("shmget");
        exit(EXIT_FAILURE);
    }

    //5. attach the shm_id to this process
    char *shm_ptr;
    shm_ptr = shmat(shm_id, NULL, 0);
    if(NULL == shm_ptr)
    {
        perror("shmat");
        exit(EXIT_FAILURE);
    }

    //6. Operation procedure
    struct sembuf sem_b;
    sem_b.sem_num = 0;      //first sem(index=0)
    sem_b.sem_flg = SEM_UNDO;
    sem_b.sem_op = -1;           //Increase 1,make sem=1
    
    while(1)
    {
        if(1 == (value = semctl(sem_id, 0, GETVAL)))
        {
            printf("\nNow, receive message process running:\n");
            printf("\tThe message is : %s\n", shm_ptr);

            if(-1 == semop(sem_id, &sem_b, 1))
            {
                perror("semop");
                exit(EXIT_FAILURE);
            }
        }

        //if enter "end", then end the process
        if(0 == (strcmp(shm_ptr ,"end")))
        {
            printf("\nExit the receiver process now!\n");
            break;
        }
    }

    shmdt(shm_ptr);
    //7. delete the shared memory
    if(-1 == shmctl(shm_id, IPC_RMID, NULL))
    {
        perror("shmctl");
        exit(EXIT_FAILURE);
    }

    //8. delete the semaphore
    if(-1 == semctl(sem_id, 0, IPC_RMID))
    {
        perror("semctl");
        exit(EXIT_FAILURE);
    }

    return 0;
}


免責聲明!

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



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