引言 - 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
運行結果如下
表示當前連接數為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 或內存使用是可以了解了.
對於高級部分擴展, 那就隨著業務的需求進行優化和擴展了. 每一項技術都是無底洞, 因業務需求而定最好.
後記
到這裡基本完畢, 有問題可以交流, 會快速改正. 拜~~