歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

Linux IPC 共享內存用法

Linux IPC 常見的方式

寫 Linux Server 端程序,必然會涉及到進程間通信 IPC. 通信必然伴隨著同步機制,下面是一些常見的通信與同步機制:
進程間通信:匿名管道,命名管道,消息隊列,共享內存,
Domain Socket
,
本機 TCP Socket
,文件
進程間同步:信號,信號量
線程間同步:條件變量,互斥量,讀寫鎖,自旋鎖,Barrier.
對於大部分的業務場景,
本機 TCP Socket
足以,現在Linux 也對
本機 TCP Socket
做了很好的優化。而且如果以後需要分機器部署進程,那麼程序不需要怎麼改動,十分方便。至於 Domain Socket,應用也很廣泛,可以看這篇文章:UNIX Domain Socket IPC。

共享內存IPC的原理

用共享內存做IPC手段的最大優勢是效率最高,缺點是需要借助其他同步機制
共享內存IPC的基本原理也很簡單:把同一塊物理內存映射到多個進程的虛擬地址空間,進程虛擬地址布局可以看這篇:Linux虛擬地址空間布局。Linux 在
<sys/shm.h>
提供了相關操作的接口:
獲取 利用
key
創建或獲取一個存在的共享內存對象;
關聯
attach
, 關聯到當前進程的虛擬地址空間;
分離
detach
, 從當前進程中分離;
控制 如: 設參數, 鎖定, 釋放等;
詳細的函數接口說明可以看: Linux進程間通信——使用共享內存

共享內存 IPC 例子

下面的 demo 一個讀,一個寫,IPC采用共享內存,同步就用最簡單的條件變量配合。最終實現在一個進程讀標准輸入,另外一個進程輸出到控制台。
可以使用
ipcs -m
查看系統使用的共享內存 IPC資源列表。
公共頭文件:
[code]/* file: shm_comm.h */

#ifndef _SHM_COMM_H
#define _SHM_COMM_H

#define MY_SHM_KEY 0x12
#define READ_SHM 1
#define WRITE_SHM -1

typedef struct {
    int flag;
    char buff[1020];
}__attribute__((packed)) S_SHM_BUF;  // 單字節對齊,方便 ipcs -m 查看對比大小

#endif

讀進程
[code]/* file:shm_read.c */

#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
#include "shm_comm.h"

int main()
{
    void *shm = NULL;
    S_SHM_BUF *shbuf;
    int ret = 0;
    int shmid = 0;

    /* 由 key 申請共享內存, 得到 id */
    shmid = shmget(MY_SHM_KEY, sizeof(S_SHM_BUF), IPC_CREAT);
    if (-1 == shmid) {
        printf("get share memory failed!\n");
        exit(EXIT_FAILURE);
    }

    /* 根據 id 把共享內存 attach 到進程地址空間,得到首地址 */
    shm = shmat(shmid, 0, 0);
    if (-1L == (long)shm) {
        printf("share memory attach failed!\n");
    }
    printf("share memory attached at %p\n", shm);

    shbuf = (S_SHM_BUF*)shm;
    while (1) {
        if (READ_SHM == shbuf->flag) {
            printf("read: %s\n", shbuf->buff);
            shbuf->flag = WRITE_SHM;
            if (0 == strncmp(shbuf->buff, "end", 3)) {
                break;
            }
        } else {
            sleep(1);
        }
    }

    /* 根據首地址,把共享內存從進程空間 detach 掉 */
    ret = shmdt(shm);
    if (-1 == ret) {
        printf("share memory detach failed!\n");
        exit(EXIT_FAILURE);
    }

    /* 根據id,讓操作系統刪掉共享內存 */
    ret = shmctl(shmid, IPC_RMID, 0);
    if (-1 == ret) {
        printf("share memory remove failed!\n");
        exit(EXIT_FAILURE);
    }

    printf("program end ok!\n");
    return 0;
}

寫進程
[code]/* file: shm_write.c */

#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
#include "shm_comm.h"

int main()
{
    void *shm = NULL;
    S_SHM_BUF *shbuf;
    int ret = 0;
    int shmid = 0;

    shmid = shmget(MY_SHM_KEY, sizeof(S_SHM_BUF), IPC_CREAT);
    if (-1 == shmid) {
        printf("get share memory failed!\n");
        exit(EXIT_FAILURE);
    }

    shm = shmat(shmid, 0, 0);
    if (-1L == (long)shm) {
        printf("share memory attach failed!\n");
    }
    printf("share memory attached at %p\n", shm);

    shbuf = (S_SHM_BUF*)shm;
    memset(shbuf, 0, sizeof(*shbuf));
    shbuf->flag = WRITE_SHM;

    while (1) {
        if (WRITE_SHM == shbuf->flag) {
            printf("write: ");
            scanf("%s", shbuf->buff);
            shbuf->flag = READ_SHM;
            if (0 == strncmp(shbuf->buff, "end", 3)) {
                break;
            }
        } else {
            sleep(1);
        }
    }

    ret = shmdt(shm);
    if (-1 == ret) {
        printf("share memory detach failed!\n");
        exit(EXIT_FAILURE);
    }

    printf("program end ok!\n");
    return 0;
}

Makefile
[code]# Makefile
CFLAGS  = -Wall -Werror -g -m64
compile = $(CC) $(CFLAGS) $^ -o $@

.PHONY : all
all : shm_read.bin shm_write.bin

# 注意 makefile 的縮進要用<TAB>
shm_read.bin : shm_read.c shm_comm.h
    $(compile)

shm_write.bin : shm_write.c shm_comm.h
    $(compile)

.PHONY : clean
clean:
    rm *.bin
Copyright © Linux教程網 All Rights Reserved