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 [email protected] $^

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

运行结果如下

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

运行结果是

打印出结果了, 后面 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, "添加服务器列表失败!\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

最后执行结果如下

好这里我们基本的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;
}

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

第二会话在这期间测试

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

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

后记

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

时间: 2024-07-28 13:02:43

C扩展 从共享内存shm到memcache外部内存的相关文章

Linux之共享内存shm和内存映射mmap

一.共享内存shm 1 概念:多个进程的地址空间都映射到同一块物理内存,这样多个进程都能看到这块物理内存,实现进程间通信,而且不需要数据的拷贝,所以速度最快. 二.内存映射mmap 1 前言:先介绍一下普通的读写文件的原理,进程调用read/write系统调用后会陷入内核,内核开始读写文件,假设内核是在读文件,内核先把文件读取到内核缓冲区,然后把内核缓冲区的数据拷贝到用户缓冲区,实际上整个过程拷贝了两次数据,即先从文件到内核缓冲区,再从内核缓冲区到用户缓冲区: 2 概念:把某个文件映射到进程的地

memcache的内存管理机制

Memcache使用了Slab Allocator的内存分配机制:按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题Memcache的存储涉及到slab,page,chunk三个概念1.Chunk为固定大小的内存空间,默认为96Byte.2.page对应实际的物理空间,1个page为1M.3.同样大小的chunk又称为slab.Memcached再启动的时候根据-n和-f参数,产生若干slab.具体应用中Memcache每次申请1page,并将这1M空间分割成若干个chu

memcache的内存回收机制

memcache不会释放内存,而是重新利用. 在缓存的清除方面,memcache是不释放已分配内存.当已分配的内存所在的记录失效后,这段以往的内存空间,memcache只会重复利用. memcached的内存回收机制不是说你设置的key到了生命周期就自动从内存中清除的,这个时候必须有一个新的对象入驻请求这个大小的chunk或者 这个过期的对象被get的时候才会清除. 那当所有给memcache的内存都被占用了,这个时候,memcache有两个设置,要么报错,要么,就是用 LRU方法,把last

内存分配原理 -进程分配内存的两种方式,分别有系统调用完成brk() 和mmap()(不设计共享内存)

如何查看进程发生缺页中断的次数? 用ps -o majflt,minflt -C program命令查看. majflt代表major fault,中文名叫大错误,minflt代表minor fault,中文名叫小错误.           这两个数值表示一个进程自启动以来所发生的缺页中断的次数. 发成缺页中断后,执行了那些操作? 当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作: 1.检查要访问的虚拟地址是否合法 2.查找/分配一个物理页 3.填充物理页内容(读取磁盘,或者直接置0

jvm内存模型-回收算法-和内存分配以及jdk、jre、jvm是什么关系(阿里,美团,京东面试题)

1.什么是jvm?(1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的.(2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域.(3)JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行. 2.jdk.jre.jvm是什么关系?(1)JRE(Java

windows内存详解(一) 全面介绍Windows内存管理机制及C++内存分配实例

十分感谢MS社区的帖子,讲得很好~ http://social.technet.microsoft.com/Forums/zh-CN/2219/thread/afc1269f-fe08-4dc7-bb94-c395d607e536 (一):进程空间 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本文目的: 对Windows内存管理机制了解清楚,有效的利用C+

java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任. 对于Java程序员来说,不需要在为每一个new操作去写配对的delete/free,不容易出现内容泄漏和内存溢出错误,看起来由JVM管理内存一切都很美好.不过,也正是因为Java程序员把内存控制的

Android内存优化1 了解java内存分配 1

开篇废话 今天我们一起来学习JVM的内存分配,主要目的是为我们Android内存优化打下基础. 一直在想以什么样的方式来呈现这个知识点才能让我们易于理解,最终决定使用方法为:图解+源代码分析. 欢迎访问我的个人博客:senduo's blog 希望能在我们平时开发写代码的时候,能够知道当前写的这段代码,内存方面是如何分配的. 我们深知,一个Java程序员在很多时候根本不用操心内存的释放,而是依靠JVM去管理,以前写C++代码的时候,却要时刻记着new的空间要及时释放掉,不然程序很容易出现内存溢出

VB.NET 内存指针和非托管内存的应用

介绍 Visual Basic 从来不像在C或C++里一样灵活的操纵指针和原始内存.然而利用.NET框架中的structures 和 classes,可以做许多类似的事情.它们包括 IntPtr,   Marshal 以及 GCHandle. 这些structures(结构) 和classes(类) 允许你在托管和非托管环境中进行交互.本文中将向您展示如何使用这些structures 和 classes 去完成指针和内存的操作. 关于 IntPtr 结构 IntPtr  结构的行为像一个整型指针