System V共享內存介紹

(一)簡單概念數據結構

      共享內存做爲一種進程間通訊的方式,其相較於其餘進程間通訊方式而言最大的優勢就是數據傳輸速率快。其內部實現的方式採用了Linux進程地址空間中的mmap文件映射區,將文件內容直接映射到各自進程的進程地址空間中,進程對各自進程地址空間的訪問便可多線程

完成數據通訊,因爲直接讀取內存的方式,故其效率遠遠快於其餘IPC方法,這是共享內存的一大優點。但對於共享內存來講,保證數據同步問題是一個難點,在通常狀況下能夠採用管道的方式,本節內容的實例代碼採用了互斥鎖機制。函數

 

(二)System V共享內存API函數spa

(1)int shmget(key_t key, ssize_t size, int oflag)操作系統

功能:用於建立共享內存區域。線程

參數:code

key:共享內存的名字,用於惟一肯定一個共享內存;blog

size:建立的共享內存的大小;進程

oflag:共享內存的讀寫權限值集合,與文件建立中的mode標誌是同樣的。一樣還能夠與IPC_CREAT,IPC_EXCL等標誌聯合使用。內存

返回:函數返回共享內存區的標識符。

 

(2)void *shmat(int shmid, const void *shmaddr, int flag)

功能:將一個建立好的共享內存區附接到進程對應的進程地址空間中。

參數:

shmid:shmget函數的返回值;

shmaddr:將共享內存附接道shmaddr指定的進程地址空間的對應地址上。若是設置爲NULL,將由系統指定合法的區域將共享內存映射,這是推薦的作法;

flag:兩個可能取值爲SHM_RND和SHM_RDONLY。

返回:函數返回映射區的起始地址。

 

(3)int shmdt(const void *shmaddr)

功能:將共享內存從該進程地址空間中脫離。

參數:

shmaddr:shmat函數的返回值。

返回:若是成功,函數返回0,出錯返回-1。

注意:該函數只是將共享內存從進程的地址空間中脫離,不會刪除該共享內存區域。而且當一個進程終止時,它當前附接的全部共享內存區都將會自動脫離。

 

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

功能:提供對共享內存區的多種操做

參數:

shmid:shmget函數的返回值;

cmd:具體操做。取值有1)IPC_RMID:從系統中刪除shmid標識的共享內存區並拆除;2)IPC_SET:在進程有足夠權限的前提下,把共享內存的當前關聯值設置爲shmid_ds數據結構中給出的值;3)IPC_START:向調用者返回指定的共享內存區當前的shmid_ds結構。

返回:成功返回0,出錯返回-1。

 

(三)採用互斥鎖與共享內存的進程間通訊方式

      因爲共享內存須要解決的一個問題就是數據同步,進程間的同步方式採用信號量能夠完成,但我考慮可否採用互斥鎖的方式。這裏採用互斥鎖最初的疑惑爲:兩個沒有近親關係的進程,如何可以獲取到同一個互斥鎖(通常互斥鎖用於線程間同步的時候比較多,這裏也說回來,線程之間原本就是共享數據,因此只須要解決數據同步問題便可,故尚未據說過用共享內存來解決線程通訊的 - -。)。

      這裏來看下,若是設置互斥鎖pthread_mutex_t,來使得不一樣進程能夠獲取到同一個互斥鎖。

      對於一個互斥鎖pthread_mutex_t來講,有兩種方式進行初始化,一種是靜態分配,一種是動態初始化。

      1)利用宏PTHREAD_MUTEX_INITALIZER來初始化靜態分配的互斥鎖

      pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER

      2)利用pthread_mutex_init來動態初始化互斥鎖

      函數原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

     這裏第一個參數mutex很好理解,就是定義的須要初始化的互斥鎖。這裏的第二個參數mutexattr表明什麼?Linux下互斥鎖具備一系列的屬性,其中pthread_mutexattr_t結構體定義了一套完整的互斥鎖屬性。兩種經常使用的屬性是pshared和type。其中pshared屬性指定了是否容許跨進程共享互斥鎖,可選值爲:1)PTHREAD_PROCESS_SHARED,互斥鎖能夠被跨進程共享;2)PTHREAD_PROCESS_PRIVATE,互斥鎖只能隸屬於一個進程,默認屬性。

     咱們能夠經過設置互斥鎖自身屬性,來達到跨進程共享互斥鎖的方法。

     結合實例代碼來進行分析:

//shmat.h

#ifndef __SHMAT_H__
#define __SHMAT_H__

#include <pthread.h>
#include <string.h>
#include <sys/shm.h>
#include <stdlib.h>

#define SM_BUF_SIZE 1024
#define SM_ID 0x1234

struct shmat_msg
{
    int flag;
    pthread_mutex_t shmat_mutex;
    char buf[SM_BUF_SIZE];
};

#endif

頭文件聲明瞭共享內存結構體,其中shmat_mutex用於保證對共享內存的互斥訪問。

//shmat-read.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#include "shmat.h"

int main(void)
{
    int running = 1;
    int shm_id, ret;
    void *shared_memory = NULL;
    struct shmat_msg *sm_msg = NULL;

    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);   //設置跨進程屬性

    shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666 | IPC_CREAT);   //建立共享內存,返回共享內存標識符
    if (shm_id < 0)
    {
        perror("fail to shmget\n");
        exit(1);
    }

    shared_memory = shmat(shm_id, NULL, 0);   //映射到進程虛擬地址空間
    if (shared_memory == NULL)
    {
        perror("fail to shmat\n");
        exit(1);
    }
    sm_msg = (struct shmat_msg*)shared_memory;   //轉型爲shmat_msg數據結構
    sm_msg->flag = 0;
    pthread_mutex_init(&sm_msg->shmat_mutex, &attr);   //初始化共享內存的互斥鎖

    while (running)
    {
        pthread_mutex_lock(&sm_msg->shmat_mutex);   //互斥訪問
        if (sm_msg->flag)
        {
            printf("read message : %s\n", sm_msg->buf);
            sm_msg->flag = 0;
            if (strncmp(sm_msg->buf, "exit", 4) == 0)
                running = 0;
            pthread_mutex_unlock(&sm_msg->shmat_mutex);
        }
        else
        {
            printf("no data to read, waiting\n");
            pthread_mutex_unlock(&sm_msg->shmat_mutex);
            sleep(2);
        }
    }

    ret = shmdt(shared_memory);   //脫離共享內存的映射
    if (ret < 0)
    {
        perror("failed to shmdt\n");
        exit(1);
    }

    if (shmctl(shm_id, IPC_RMID, 0) < 0)   //刪除共享內存
    {
        perror("failed to shmctl\n");
        exit(1);
    }

    return 0;
}

讀文件。設置了共享內存互斥鎖爲跨進程屬性。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#include "shmat.h"

int main(void)
{
    int running = 1;
    int shm_id, ret;
    void *shared_memory = NULL;
    struct shmat_msg *sm_msg = NULL;

    shm_id = shmget((key_t)SM_ID, sizeof(struct shmat_msg), 0666);   //獲取共享內存
    if (shm_id < 0)
    {
        perror("failed to shmget\n");
        exit(1);
    }

    shared_memory = shmat(shm_id, NULL, 0);   //映射
    if (shared_memory == NULL)
    {
        perror("failed to shmat\n");
        exit(1);
    }

    sm_msg = (struct shmat_msg*)shared_memory;
    char buff[100];

    while (running)
    {
        pthread_mutex_lock(&sm_msg->shmat_mutex);   //獲取共享內存的互斥鎖
        if (sm_msg->flag == 0)
        {
            fgets(buff, 100, stdin);
            printf("write sm_msg : %s\n", buff);
            strncpy(sm_msg->buf, buff, sizeof(buff));
            sm_msg->flag = 1;
            if (strncmp(sm_msg->buf, "exit", 4) == 0)
                running = 0;
            pthread_mutex_unlock(&sm_msg->shmat_mutex);
        }
        else
        {
            printf("sm_msg waiting read\n");
            pthread_mutex_unlock(&sm_msg->shmat_mutex);
            sleep(2);
        }
    }

    ret = shmdt(shared_memory);
    if (ret < 0)
    {
        perror("failed to shmdt\n");
        exit(1);
    }

    ret = shmctl(shm_id, IPC_RMID, 0);
    if (ret < 0)
    {
        perror("failed to shmctl\n");
        exit(1);
    }

    return 0;
}

寫文件。

程序運行結果:

其中shmat-write程序運行後會阻塞在fgets等待用戶輸入。其中shmat-read與shmat-write兩個程序搶佔lock的時機是不肯定的,可能同一個程序會屢次搶佔互斥鎖,但因爲沒有內容可讀或可寫,則睡眠等待。

unlock解鎖後,操做系統下次調度哪一個進程加鎖是不必定的,若是須要在知足必定條件後才被調度能夠採用條件變量(但通常都是在多線程環境下了)。

相關文章
相關標籤/搜索