引言 - ipc - shm 共享内存

  本文会通过案例了解ipc 的共享内存机制使用, 后面会讲解C 如何使用外部内存服务memcached. 好先开始了解 linux 共享内存机制.

推荐先参看下面内容回顾一下 共享内存 linux api.

  linux进程间的通信(C): 共享内存    http://blog.chinaunix.net/uid-26000296-id-3421346.html

上面文章可以简单看一下概念.  下面这篇文章好些, 可以细看加深共享内存api使用熟练度.

  Linux共享内存(一)  http://www.cnblogs.com/hicjiajia/archive/2012/05/17/2506632.html

那我们开始吧. 先看 初步编译文件 Makefile

CC = gcc 
DEBUG = -ggdb3 -Wall
RUN = $(CC) $(DEBUG) -o $@ $^

all:shmsrv.out shmclt.out

shmsrv.out:shmsrv.c
    $(RUN)
shmclt.out:shmclt.c
    $(RUN)

# 删除 make clean 操作
.PHONY:clean
clean:
    rm -rf *.i *.s *.o *.out *~ core_*; ls -al

 先看 共享内存 服务器端, 主要是写内容到共享内存中. shmsrv.c

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

// 控制台打印错误信息, fmt必须是双引号括起来的宏
#define CERR(fmt, ...) \
    fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
             __FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__)

// 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
#define CERR_EXIT(fmt,...) \
        CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)

// 简单检测,意外就退出
#define IF_CHECK(code) \
      if((code) < 0) \
        CERR_EXIT(#code)

// 共享内存key
#define _INT_SHMKEY (0x12321)

/*
 * 这里设置一个共享内存, 写入数据, 让别人来读.
 * 写入的数据内容来自用户输入.
 * 需要先启动
 */
int main(int argc, char* argv[]) {
    int shmid, i, j;
    char* shm;

    // 检测参数输入
    if(argc < 2)
        CERR_EXIT("uage: %s argv[1] [argv[.]].", argv[0]);

    /*
     *  0764 是0开头表示八进制数,
     *  7表示 当前进程具有读写执行权限, 
     *     6表示 同会话组具有读写权限, 同组表示groupid 相同 
     *     4表示 其它表示具有读权限
     */
    IF_CHECK(shmid = shmget(_INT_SHMKEY, BUFSIZ+1, 0764|IPC_CREAT));
    
    // 添加简单测试 
    printf("test stdio.h BUFSIZ = %d\n", BUFSIZ);

    // 开始共享内存关联
    shm = shmat(shmid, NULL, 0);
    // 这里写入数据
    for(i=j=0; i<argc; ++i) {
        const char* ts = argv[i];
        while(*ts) {
            shm[j++] = *ts++;
            if(j>=BUFSIZ)
                break;
        }
        if(j>=BUFSIZ)
            break;
        shm[j++] = ' ';
    }
    shm[j] = '\0';

    // 这里查看一下共享内存信息
    system("ipcs -m");

    // 取消关联
    IF_CHECK(shmdt(shm));

    // 删除共享内存
    //IF_CHECK(shmctl(shmid, IPC_RMID, NULL));
    
    return 0;    
}

推荐看 上面代码 了解共享内存api使用方式, 先创建或打开, 后面绑定, 再到取消绑定等价于内核引用计数减一.

可能需要注意的是 对于 0764 详细解释, 这个是约定, 采用八进制数, 第一个数 7 = 4 + 2 + 1 .

4表示读权限, 2表示写权限, 1表示 可执行权限. 文件权限可以 搜一下了解.

例如 文件权限   http://blog.chinaunix.net/uid-20864319-id-448817.html

后面

ipcs -m 

表示查看 所有共享内存状态. 具体的操作命令可以继续搜一搜.

例如  ipc 命令 http://www.cnblogs.com/cocowool/archive/2012/05/22/2513027.html

运行结果如下

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版) 

表示当前连接数为1. 大小为8193 权限是 0764, 名称为 0x00012321.

再来看 客户端只读取 shmclt.c 

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

// 控制台打印错误信息, fmt必须是双引号括起来的宏
#define CERR(fmt, ...) \
    fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
             __FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__)

// 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
#define CERR_EXIT(fmt,...) \
        CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)

// 简单检测,意外就退出
#define IF_CHECK(code) \
      if((code) < 0) \
        CERR_EXIT(#code)

// 共享内存key
#define _INT_SHMKEY (0x12321)

/*
 * 这里设置一个共享内存, 写入数据, 让别人来读.
 * 写入的数据内容来自用户输入.
 * 需要先启动
 */
int main(int argc, char* argv[]) {
    int shmid;
    char* shm;

    IF_CHECK(shmid = shmget(_INT_SHMKEY, BUFSIZ+1, IPC_CREAT));

    // 开始共享内存关联
    shm = shmat(shmid, NULL, 0);

    // 输出内容
    puts(shm);
    // 这里查看一下共享内存信息
    system("ipcs -m");

    // 取消关联
    IF_CHECK(shmdt(shm));
    // 删除共享内存
    IF_CHECK(shmctl(shmid, IPC_RMID, NULL));
    
    return 0;    
}

运行结果是

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

 打印出结果了, 后面 ipcs -m 就查不出结果了.

删特定共享内存 命令是 ipcrm -m shmid

其它就多尝试. 共享内存本质多个进程将虚拟内存地址映射到相同的物理内存地址.

 

前言 - memcache 服务安装使用

  到这里我们了解了共享内存基础使用, 后面扩展一点了解memcache 缓存机制(外部内存). 有机会再研究分析它的源码,

再来分享. 扯一点,memcache 是这个高速缓存项目的名称, 就是这个项目, memcached表示最后启动的服务名称.

前言部分主要是 了解 memcache的安装 和 基本协议命令. 采用 环境是 ubuntu 15. 10版本.

安装 命令

sudo apt-get install memcached

安装好了 采用

ps -ef | grep memcached

测试 安装成功结果, 是启动了 memcached 服务

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

后面可以看看 memcache 命令中文手册 , 也可以通过 memcached -h 查看, 翻译的中文可以简单参照下面.

memcached 中文手册 http://www.jinbuguo.com/man/memcached.html

后面 我们开始使用 memcache . 主要围绕, 设置数据, 更新数据, 删除数据.

一种操作方式 如下

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

 进入后 add set get 操作如下 示例如下

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

第一个 set id 0 0 5 表示 设置 key 为 id , 第一个0表示标识为 unsigned short . 第二个0表示没有过期时间, 5表示 后面插入字符长度为5.

后面 输入 nihao 就是 set 进去的数据.

成功返回 STORED, 失败返回 NOT_STORED.

add 和 set 相似只能在没有待插入key 时候才会成功, 否则都失败.

详细的可以看

memcache telnet 维护  http://blog.csdn.net/love__coder/article/details/7828253

更加详细的参看 下面

 Memcache 协议 (译)  http://www.cnblogs.com/warriorlee/archive/2011/09/18/2180661.html

 memcache 简易介绍笔记 http://blog.sina.com.cn/s/blog_53f716d40100hls0.html  

 Memcached通信协议(中文版) http://www.cnblogs.com/kevintian/articles/1197681.html

具体设置命令还是比较多, 这里只列举了最常用的用法. 更多的经验还得自己试错.

memcache 还是很好用的. 到这里memcache 基础协议部分可以过了. 下面会通过 其驱动正式开发.

 

正文 - C调用使用memcached服务

  memcahce 确实比较不错, 挺好用的.  首先直接看下面例子, 到这里还是比较重要的. 请一定要注意仔细了, 能完整跑起来的不容易.

那开始跟着我做吧. 我们memcached 服务器已经安装好了, 但是少个客户端驱动, 否则无法调用它服务进行处理.

在Linux 上我们采用 libmemcachde 库.

Introducing the C Client Library for memcached  http://docs.libmemcached.org/libmemcached.html

源码安装下载地址  https://github.com/memcached/memcached/wiki/ReleaseNotes1425

下载下来后执行

tar –xvf libmemcached-1.0.18.tar
cd libmemcached-1.0.18

执行过程结果

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

进去之后结果如下

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

到这里 执行下面步骤

./configure
make
sudo make install

执行上面之后 保证成功了. 可能在之前 你需要安装 libevent-dev, sudo apt-get install libevent-dev. 安装网络库.

安装完毕之后 需要为 其配置 lib 环境变量 (很重要, 理解为 window上 path) 看下图

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

具体命令如下

cd
vi .bashrc
Shift + G
i
# 为 memcached 客户端libmemcahced添加 的库目录
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
wq!

source .bashrc

中间命令是为了 进去 .bashrc 文件在最后一行添加 新的 环境变量, 其中 /usr/local/lib 是本用户安装的库目录.

好了到这里一切妥当了.  先写个简单的demo memheoo.c 测试一下

#include <stdio.h>
#include <stdlib.h>
#include <libmemcached/memcached.h>

/*
 * 测试 memcached 的使用
 */
int main(int argc, char* argv[]) {
    // connect server
    memcached_st* memc;
    memcached_return rc;
    memcached_server_st* mems;
    time_t expir = 0;
    uint32_t flag = 0;
    const char* res;
    const char* key = "hello";
    size_t klen = strlen(key), vlen;

    // 开始测试, 可以不用添加检测代码, 这里只为了知道有这个api
    memc = memcached_create(NULL);
    mems = memcached_server_list_append(NULL, "127.0.0.1", 11211, &rc);
    if(!memcached_success(rc)){
        fprintf(stderr, "添加服务器列表失败!\n");
        exit(EXIT_FAILURE);
    }
    // 这东西目前唯一资料就是libmemcached源码
    rc = memcached_server_push(memc, mems);
    if(!memcached_success(rc)) {
        fprintf(stderr, "添加服务器列表向客户端失败!");
        exit(EXIT_FAILURE);
    }
    memcached_server_list_free(mems);

    // 开始设置数据
    rc = memcached_set(memc, key, klen, "world", 5 , expir, flag);
    if(rc == MEMCACHED_SUCCESS)
        printf("Set data<hello, world> => %d\n", rc);

    // 这里得到数据
    res = memcached_get(memc, key, klen, &vlen, &flag, &rc);
    if(rc == MEMCACHED_SUCCESS) 
        printf("get value:%s, len:%ld, flag:%d\n", res, vlen, flag);
    
    // 删除数据
    rc = memcached_delete(memc, key, klen, expir);
    if(rc == MEMCACHED_SUCCESS)
        printf("%s 删除成功!\n", key);
    else
        puts("删除失败!");
    
    // free
    memcached_free(memc);

    return 0;    
}

编译命令 执行命令如下

gcc -g -Wall -o memheoo.out memheoo.c -lmemcachedls

./memheoo.out

最后执行结果如下

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

好这里我们基本的demo都执行完毕了. 全部都跑起来. 瞬间感觉顺畅了一点.

最后我们通过 libmemcached 构建一件有意思的时间锁.  具体如下 memlock.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libmemcached/memcached.h>

/*
 * 测试 memcached 的使用
 */
int main(int argc, char* argv[]) {
    // connect server
    memcached_st* memc;
    memcached_return rc;
    memcached_server_st* mems;
    time_t expir = 10; // 过期时间为10s
    const char* key = "__mem_key_lock";
    size_t klen = strlen(key);

    // 创建服务器地址添加到客户端中
    memc = memcached_create(NULL);
    mems = memcached_server_list_append(NULL, "127.0.0.1", 11211, &rc);
    rc = memcached_server_push(memc, mems);
    if(rc != MEMCACHED_SUCCESS) {
        fprintf(stderr, "添加服务器地址失败!=>%s\n", memcached_error(memc));    
        exit(EXIT_FAILURE);
    }
    memcached_server_list_free(mems);

    // 开始通过数据加锁
    rc = memcached_add(memc, key, klen, "0", 1, expir, 0);
    if(rc != MEMCACHED_SUCCESS) {
        printf("这里竞争锁失败! MEMCACHED_NOTSTORED = %d \n", rc == MEMCACHED_NOTSTORED);
        memcached_free(memc);
        exit(EXIT_FAILURE);
    }
    
    printf("得到锁资源 这里等待 : %ld s后结束\n", expir);
    // 等待 10s ,可以用另一个 进程测试
    sleep(expir);

    // free
    memcached_free(memc);

    return 0;    
}

看第一个会话进程开启测试

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

第二会话在这期间测试

C扩展 从共享内存shm到memcache外部内存
    




Linux共享内存(一)
Memcache 协议 (译)
Memcached通信协议(中文版)

这里通过 memcached 服务构建一个带时效性的 lock, 是不是很有意思.  到这里基本上关于memcahed 或内存使用是可以了解了.

对于高级部分扩展, 那就随着业务的需求进行优化和扩展了. 每一项技术都是无底洞, 因业务需求而定最好.

 

后记

  到这里基本完毕, 有问题可以交流, 会快速改正. 拜~~

相关文章:

  • 2021-12-18
  • 2021-11-16
猜你喜欢
  • 2021-05-17
  • 2022-12-23
  • 2021-08-02
  • 2022-12-23
  • 2022-12-23
  • 2022-01-18
  • 2021-12-31
相关资源
相似解决方案