linux進程間通信之共享內存

1:共享內存

共享內存允許兩個或多個進程共享一給定的內存。因爲數據不需要再進程間複製,可以直接讀寫內存,所以它是目前最快的一種進程間通信方式。內核專門爲共享內存留出一塊專有內存區域,在使用前只需要將該地址映射到進程的私有地址空間,就可以直接訪問,一般需要結合互斥鎖或信號量等同步機制來進行訪問。

這裏寫圖片描述

上圖示意了共享內存原理示意圖, 在內核部分將講述共享內存區的劃分及實現。

2:主要函數

對共享內存操作主要分爲兩步,創建共享內存和映射共享內存

創建共享內存函數:
int shmget(key_t key, int size, int shmflg)
參數:key值就是共享內存的鍵值,多個進程可以通過它訪問同一個共享內存,其中有個特殊值IPC_PRIVATE.它用於創建當前進程的私有共享內存
size: 共享內存大小
shmflg:同open()函數的權限位,也可以用八進制表示法。

共享內存映射函數:
char shmat(int shmid, const void shmaddr, int shmflg)
參數:shmid:要映射的共享內存區標識符。
shmaddr:將共享內存映射到指定地址(若爲0, 則表示系統自動分配地址並把該段共享內存映射到調用進程的地址空間)
shmflg:共享內存權限。默認爲0:共享內存可讀寫。SHM_RDONLY:共享內存只讀。

當該進程操作完共享內存時,需要進程脫接該段:
int shmdt(const void *shmaddr)
參數: shmaddr:被映射的共享內存段地址。

3:實驗

實驗1:查看共享內存在每個進程的映射地址:

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


#define BUFFER_SIZE 2048

int main()
{
    pid_t pid;
    int shmid;
    char *shm_addr;
    char flag[] = "WROTE";
    char *buff;

    /*create share memory*/
    if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
    {
        perror("Shmget");
        exit(1);
    } else {
        printf("Create share memory: %d\n", shmid);
    }


    pid = fork();
    if ( pid == -1)
    {
        perror("fork");
        exit(1);
    } else if (pid == 0)
    {
        sleep(1);

        /*map share memory*/
        if ((shm_addr = shmat(shmid, 0 , 0)) == (void *) -1)
        {
            perror("Child: shmat");
            exit(1);
        }

        printf("Child: Share memory addess :0x%x\n", (unsigned long)shm_addr);

        /* delet memory map*/
        if ((shmdt(shm_addr)) < 0)
        {
            perror("Shmdt");
            exit(1);
        } else {
            printf("Child: Dettach share memory\n");
        }


        /*Delete share memory */
        if (shmctl(shmid, IPC_RMID, NULL) == -1)
        {
            perror("Child: shmctl(IPC_RMID)\n");
            exit(1);
        } else {
            printf("Delete share memory\n");
        }


    } else {
        /* map share memory */
        if ((shm_addr = shmat(shmid, 0, 0) ) == (void *) -1)
        {
            perror("Parent: shmat");
            exit(1);
        }

        printf("Father process share address 0x%x\n",(unsigned long)shm_addr);

        /* Delete share memory map */
        if ((shmdt(shm_addr)) < 0)
        {
            perror("Parent: shmdt");
            exit(1);
        } else {
            printf("Parent: Deattach share memory\n");
        }

        waitpid(pid, NULL, 0);
        printf("Finished \n");
    }
    exit(0);
}

運行結果:
root# ./a.out
Create share memory: 1933326
Father process share address 0xb76da000
Parent: Deattach share memory
Child: Share memory addess :0xb76da000
Child: Dettach share memory
Delete share memory
Finished

實驗2:下面是參考《linux應用程序開發標準教程》中的一個例子,沒有對共享內存加入同步機制,因爲是同時只有一個進程對該內存進行寫,直接上代碼:

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


#define BUFFER_SIZE 2048

int main()
{
    pid_t pid;
    int shmid;
    char *shm_addr;
    char flag[] = "WROTE";
    char *buff;

    /*create share memory*/
    if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
    {
        perror("Shmget");
        exit(1);
    } else {
        printf("Create share memory: %d\n", shmid);
    }


    /* show share memory */
    system("ipcs -m");

    pid = fork();
    if ( pid == -1)
    {
        perror("fork");
        exit(1);
    } else if (pid == 0)
    {

        /*map share memory*/
        if ((shm_addr = shmat(shmid, 0 , 0)) == (void *) -1)
        {
            perror("Child: shmat");
            exit(1);
        } else
        {
            printf("Child: Attach share memory: %p\n", shm_addr);
        }
        system("ipcs -m");


        /* check memory head */
        while(strncmp(shm_addr, flag, strlen(flag)))
        {
            printf("Child: Wait for enable data ...\n");
            sleep(5);
        }

        /*Get data*/
        strcpy(buff, shm_addr + strlen(flag));
        printf("Child: Share memory :%s\n", buff);

        /* delet memory map*/
        if ((shmdt(shm_addr)) < 0)
        {
            perror("Shmdt");
            exit(1);
        } else {
            printf("Child: Dettach share memory\n");
        }

        system("ipcs -m");

        /*Delete share memory */
        if (shmctl(shmid, IPC_RMID, NULL) == -1)
        {
            perror("Child: shmctl(IPC_RMID)\n");
            exit(1);
        } else {
            printf("Delete share memory\n");
        }

        system("ipcs -m");

    } else {
        /* map share memory */
        if ((shm_addr = shmat(shmid, 0, 0) ) == (void *) -1)
        {
            perror("Parent: shmat");
            exit(1);
        } else {
            printf("Parent :Attach shared memory :%p\n", shm_addr);
        }        sleep(1);
        printf("n Input some string:\n");
        fgets(buff, BUFFER_SIZE, stdin);
        strncpy(shm_addr + strlen(flag), buff, strlen(buff));
        strncpy(shm_addr, flag, strlen(flag));

        /* Delete share memory map */
        if ((shmdt(shm_addr)) < 0)
        {
            perror("Parent: shmdt");
            exit(1);
        } else {
            printf("Parent: Deattach share memory\n");
        }

        system("ipcs -m");

        waitpid(pid, NULL, 0);
        printf("Finished \n");
    }
    exit(0);
}

運行結果
root@x:/repo/training/fork# ./a.out
Create share memory: 1867790

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 0

Parent :Attach shared memory :0xb7768000
Child: Attach share memory: 0xb7768000

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 2

Child: Wait for enable data …
asdn Input some string:
wds
Parent: Deattach share memory

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 1

Child: Share memory :asdwds

Child: Dettach share memory

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest
0x00000000 1867790 root 666 2048 0

Delete share memory

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 294912 baohua 600 524288 2 dest
0x00000000 393217 baohua 600 524288 2 dest
0x00000000 491522 baohua 600 524288 2 dest
0x00000000 589827 baohua 600 524288 2 dest
0x00000000 688132 baohua 600 524288 2 dest
0x00000000 786437 baohua 600 524288 2 dest
0x00000000 1376262 baohua 600 2097152 2 dest
0x00000000 1310727 baohua 600 524288 2 dest
0x00000000 983048 baohua 600 2097152 2 dest
0x00000000 1081353 baohua 600 524288 2 dest
0x00000000 1212426 baohua 600 67108864 2 dest
0x00000000 1474571 baohua 600 524288 2 dest
0x00000000 1671180 baohua 600 524288 2 dest
0x00000000 1736717 baohua 600 2097152 2 dest

Finished

實驗三:增加信號量與共享內存向結合使用實驗用例:

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


#define BUFFER_SIZE 2048

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};


int init_sem(int sem_id, int init_value)
{
    union semun sem_union;

    sem_union.val = init_value;
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
    {
        perror("Initialize semaphore");
        return -1;
    }

    return 0;
}


int del_sem(int sem_id)
{
    union semun sem_union;

    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    {
        perror("Delete semaphore");
        return -1;
    }

}
int sem_p(int sem_id)
{
    struct sembuf sem_b;

    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sem_b, 1) == -1)
    {
        perror("P operation fail");
        return -1;
    }

    return 0;
}

int sem_v(int sem_id)
{
    struct sembuf sem_b;

    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sem_b, 1) == -1)
    {
        perror("V operation");
        return -1;
    }

    return 0;
}

int ignore_signal(void)
{
    signal(SIGINT, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
}

int main()
{
    pid_t pid;
    char *shm_addr;
    char flag[] = "WROTE";
    char *buff;
    int shmid, semid;

    ignore_signal();

    semid = semget(ftok(".", 'a'), 1, 0666 | IPC_CREAT);
    init_sem(semid, 1);

    /*create share memory*/
    if ((shmid = shmget(ftok(".", 'b'), BUFFER_SIZE, 0666 | IPC_CREAT)) < 0)
    {
        perror("Shmget");
        exit(1);
    } else {
        printf("Create share memory: %d\n", shmid);
    }



    pid = fork();
    if ( pid == -1)
    {
        perror("fork");
        exit(1);
    } else if (pid == 0)
    {
       sleep(1);
        /*map share memory*/
        if ((shm_addr = shmat(shmid, 0 , 0)) == (void *) -1)
        {
            perror("Child: shmat");
            exit(1);
        } else
        {
            printf("Child: Attach share memory: %p\n", shm_addr);
        }

        do {
            sem_p(semid);
            if (strncmp(shm_addr, "quit", 4) == 0)
            {
                sem_v(semid);
                break;           }

            printf("Child: get from father: %s\n", shm_addr);

            memset(shm_addr, 0, BUFFER_SIZE);
            sem_v(semid);
        } while(1);


        /* delet memory map*/
        if ((shmdt(shm_addr)) < 0)
        {
            perror("Shmdt");
            exit(1);
        } else {
            printf("Child: Dettach share memory\n");
        }

        sleep(5);
        /*Delete share memory */
        if (shmctl(shmid, IPC_RMID, NULL) == -1)
        {
            perror("Child: shmctl(IPC_RMID)\n");
            exit(1);
        } else {           printf("Child: Dettach share memory\n");
        }

        sleep(5);
        /*Delete share memory */
        if (shmctl(shmid, IPC_RMID, NULL) == -1)
        {
            perror("Child: shmctl(IPC_RMID)\n");
            exit(1);
        } else {
            printf("Delete share memory\n");
        }


    } else {
        /* map share memory */
        if ((shm_addr = shmat(shmid, 0, 0) ) == (void *) -1)
        {
            perror("Parent: shmat");
            exit(1);
        } else {
            printf("Parent :Attach shared memory :%p\n", shm_addr);
        }
      do {
            sem_p(semid);
            printf("n Input some string:\n");
            if (fgets(shm_addr, BUFFER_SIZE, stdin) == NULL)
            {
                perror("fgets");
                sem_v(semid);
                break;
            }

            if (strncmp(shm_addr, "quit", 4) == 0)
            {
                sem_v(semid);
                break;
            }

            sem_v(semid);
         } while(1);

        /* Delete share memory map */
        if ((shmdt(shm_addr)) < 0)
        {
            perror("Parent: shmdt");
            exit(1);
       } else {
            printf("Parent: Deattach share memory\n");
        }

        waitpid(pid, NULL, 0);
        printf("Finished \n");
    }
    exit(0);

實驗代碼在:https://github.com/zhikunhuo/training.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章