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


    引言 - 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 "
    ",
                 __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
    ", 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] = '';
    
        // 这里查看一下共享内存信息
        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

    运行结果如下

     

    表示当前连接数为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 "
    ",
                 __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;    
    }

    运行结果是

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

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

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

    前言 - memcache 服务安装使用

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

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

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

    安装 命令

    sudo apt-get install memcached

    安装好了 采用

    ps -ef | grep memcached

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

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

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

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

    一种操作方式 如下

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

    第一个 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

    执行过程结果

    进去之后结果如下

    到这里 执行下面步骤

    ./configure
    make
    sudo make install

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

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

    具体命令如下

    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, "添加服务器列表失败!
    ");
            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
    ", rc);
    
        // 这里得到数据
        res = memcached_get(memc, key, klen, &vlen, &flag, &rc);
        if(rc == MEMCACHED_SUCCESS) 
            printf("get value:%s, len:%ld, flag:%d
    ", res, vlen, flag);
        
        // 删除数据
        rc = memcached_delete(memc, key, klen, expir);
        if(rc == MEMCACHED_SUCCESS)
            printf("%s 删除成功!
    ", key);
        else
            puts("删除失败!");
        
        // free
        memcached_free(memc);
    
        return 0;    
    }

    编译命令 执行命令如下

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

    最后执行结果如下

    好这里我们基本的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
    ", 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 
    ", rc == MEMCACHED_NOTSTORED);
            memcached_free(memc);
            exit(EXIT_FAILURE);
        }
        
        printf("得到锁资源 这里等待 : %ld s后结束
    ", expir);
        // 等待 10s ,可以用另一个 进程测试
        sleep(expir);
    
        // free
        memcached_free(memc);
    
        return 0;    
    }

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

    第二会话在这期间测试

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

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

    后记

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

  • 相关阅读:
    Quartz.net
    Perfview 分析进程性能
    windbg 分析cpu异常
    ansible-vault 教程
    ansible 自动化运维(2)
    简单生成随机测试数据
    基于 RabbitMQ-EasyNetQ 实现.NET与Go的消息调度交互
    自绘 TreeDataView 控件
    C# 创建音频WAVE文件头信息(*.wav)
    C# GOF 23种设计模式 学习Log【更新中】
  • 原文地址:https://www.cnblogs.com/life2refuel/p/5452124.html
Copyright © 2020-2023  润新知