28

02/12

Linux动态共享库

12:09 am by livexmm. Filed under: C/C++,Code Play,Linux

前几周甘雨童鞋讨论分享了linux连接器的知识,做为linux菜鸟都算不上的我来说压力很真有点大,尤其是后来调汇编各种跳,作为汇编和组成原理接近满分的男人有点无地自容呀,阳春白雪,曲高和寡呀。但是有几点还是留下了比较深刻的影响的,所以向甘雨童鞋借了书进行学习学习。这里要说的不是连接器的知识,刚开始看,看的差不多了,写篇水文。今天是要说的是当时顺带提到的动态共享库的东西,这里不会去扣原理和内部实现,比较肤浅的说一下使用。
学过win32编程或者MFC之类的都知道windows平台有dll,这个东西,中文叫动态链接库,使用起来相当方便,差不多一年没写win32程序了,依稀还记得LoadLibrary,GetProcAddress,FreeLibrary,使用起来很傻瓜,微软之后在基础上搞出了COM组件原理,这就是后话了。这里说要的是Linux的动态共享库,可以理解为和dll类似的东西,但是有所不同。那天分享上说linux下的动态共享库不能在不同进程间共享数据,比如A对某个数据+1,对B进程无效。但是我依稀记得Windows下的dll是可以实现共享的,并且这是一种进程间通信的手段?这个问题不在此探讨了,不是今天的重点
==========================================正文出现====================================================
这是一篇linux动态动态共享库的入门文章,因项目中使用到所以进行了一下简单的学习,顺带写了一下代码,先给出代码然后做解释吧,其实没有特别高深的东西。
dll1.c

#ifndef __DLL1__
#define __DLL1__
#include <stdio.h>

__attribute((constructor))
void dll1_init(void) {
    printf("dll1_init\n");
}

__attribute((destructor))
void dll1_finit(void) {
    printf("dll1_finit\n");
}

void whats_up() {
    printf("whats_up\n");
}

int add(int a, int b) {
    return a - b;
}

#endif

gcc dll1.c -fPIC -shared -Wall -g -o libdll1.so
将dll.c生成动态贡献库dll.so,简单解释一下参数,-fPIC表示生成和位置无关代码,-shared表示生成共享库。
下面说一下代码,其实一般的c代码没啥不同哈,但是你会先多了2个方法,其实没有这2个方法,也是没有问题的哈,这2个方法的作用是加载前调用,释放时调用,比如你加载前要预处理一些东西呀,释放时处理一些东西呀,哈哈,是不是想到了windows的DllMain是的作用是类似的,Linux动态贡献库没有DllMain这种东西,动态加载其实提供了2个方法_init()和_finit(),悲剧的是这2个被gcc占用了,所以要实现相同的功能只能绕道一下,使用gcc的东西了。

dll2.c

#ifndef __DLL2__
#define __DLL2__
#include <stdio.h>

__attribute((constructor))
void dll1_init(void) {
    printf("dll1_init\n");
}

__attribute((destructor))
void dll1_finit(void) {
    printf("dll1_finit\n");
}

void whats_up();

void nothing() {
    printf("nothing, but test\n");
}

void ans_whats_up() {
    whats_up();
    printf("haha...you guess\n");
}

int sub(int a, int b) {
    return a - b;
}

#endif

gcc dll2.c -fPIC -shared -Wall -g -o libdll2.so
看上面这段代码其实和前面没啥区别 不同的是声明了一个方法whats_up() 嗯,就是dll1里面的,下面会用它来说说明一些事情。

testmain.c

#ifndef __TEST_MAIN__
#define __TEST_MAIN__

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

#ifndef CMDLINE

typedef int (*add_func)(int a, int b);
typedef int (*sub_func)(int a, int b);
typedef void (*ans_whats_up_func)();

void test_dlopen(int dlopen_flag) {
    char* err_info = NULL;
    void* handler_dll1 = NULL;
    void* handler_dll2 = NULL;
    {
        handler_dll1 = dlopen("libdll1.so", dlopen_flag);
        if (handler_dll1 == NULL) {
            fprintf(stderr, "handler_dll1 line %d:\n%s\n", __LINE__, dlerror());
            exit(1);
        }

        dlerror();
        add_func add = dlsym(handler_dll1, "add");
        if ((err_info = dlerror()) != NULL) {
            fprintf(stderr, "handler_dll1->dlsym line %d:\n%s\n", __LINE__, dlerror());
            exit(1);
        }
        int add_ret = add(10, 20);
        printf("add_ret: 10 + 20 = %d\n", add_ret);
    }

    {
        handler_dll2 = dlopen("libdll2.so", RTLD_NOW|RTLD_GLOBAL);
        if (handler_dll2 == NULL) {
            fprintf(stderr, "handler_dll2 line %d:\n%s\n", __LINE__, dlerror());
            exit(1);
        }

        dlerror();
        sub_func sub = dlsym(handler_dll2, "sub");
        if ((err_info = dlerror()) != NULL) {
            fprintf(stderr, "handler_dll2->dlsym line %d:\n%s\n", __LINE__, dlerror());
            exit(1);
        }
        int sub_ret = sub(10, 20);
        printf("sub_ret: 10 - 20 = %d\n", sub_ret);
    }

    {
        dlerror();
        ans_whats_up_func ans_whats_up = dlsym(handler_dll2, "ans_whats_up");
        if ((err_info = dlerror()) != NULL) {
            fprintf(stderr, "ans_whats_up line %d:\n%s\n", __LINE__, dlerror());
            exit(1);
        }
        ans_whats_up();
    }

    dlclose(handler_dll2);
    dlclose(handler_dll1);

    printf("over\n");
}

#ifdef DLOPEN_GLOBAL
void test1_dlopen_global() {
    test_dlopen(RTLD_NOW|RTLD_GLOBAL);
}
#else
void test1_dlopen_local() {
    test_dlopen(RTLD_NOW|RTLD_LOCAL);
}
#endif

#else
void test2_in_cmdline() {
    whats_up();
    nothing();
    ans_whats_up();
}
#endif

int main(int argc, char* argv[]) {
#ifndef CMDLINE

#ifdef DLOPEN_GLOBAL
    test1_dlopen_global();
#else
    test1_dlopen_local();
#endif

#else
    test2_in_cmdline();
#endif
    return 0;
}

#endif

gcc testmain.c -L. -ldll1 -ldll2 -D CMDLINE -Wall -g -o testmain1.out
gcc testmain.c -rdynamic -ldl -Wall -g -o testmain2.out
gcc testmain.c -rdynamic -ldl -D DLOPEN_GLOBAL -Wall -g -o testmain3.out
这代码就有点乱了哈 主要是使用了编译宏,把种情况全部写在一个代码里面了,做一下简单的解释,testmain1.out是使用编译时把动态库指定链接,下面2种是类似LoadLibrary的方式进行,对应的方法是dlopen,dlsym,dlclose另外还有一个输出错误信息和清除错误信息作用的dlerror,下面2种情况的区别是dlopen的参数的不同,dlopen的RTLD_NOW立即加载所有的描述符号等,对应的是RTLD_LAZY,RTLD_GLOBAL表示该加载可以被之后加载的动态共享库使用,RTLD_LOCAL与之相反,具体可以man文档,testmain2.out是使用RTLD_LOCAL发现ans_whats_up调用whats_up时,找不到描述符号,而testmain3.out一切正常。另外运行3个程序还会发现调用test_init和test_finit的不同时间哦 testmain1.out在main之前被调用和main退出时被调用了,而其他2个则是在调用dlopen时被调用和dlclose时被调用

17

02/12

1读1写无锁循环队列实现

9:59 pm by livexmm. Filed under: C/C++,Code Play,LockFree

闲来无事想学习一下队列的无锁实现,队列的无锁实现有很多种方式,下面要介绍的是1读1写无锁循环队列
实现1读1写无锁循环队列其实也有几种方式,对于要做到无锁 以及循环队列,首先要注意几点1,如何实现无锁,通常的方式有2种1,去除资源竞争,通过原子操作代替锁操作(比加锁的竞争要小一些) 2.如何实现循环队列,常见的做法有2种 使用链表来做,使用线性表来做,这2种都有其适用场景,下面主要说的是线性表实现,所以主要我们要考虑线性表的问题,线性表循环队列主要问题在于判断队列空和队列满的问题上
我们来思考一下怎么解决这个问题,首先我们最容易想到的是数据结构书上做法,增加一个变量表示长度,简单有效,但是我们要实现一些1读1写的无锁循环队列,最好的情况是我们能把读写分离,没有共享资源,如果增加了一个变量表示长度,这就意为着我们增加了一个共享资源,这就会有竞争,这不是我们想看到的。让我们换个思路,循环队列我们做法一般就是index = (index+1) % N的方式来实现循环,而队列空和队列满难以区分的原因在于,起始标志和终止表示的状态环回,也就是Mod N的效果,如果我们本身的标志不进行环回,而是不断递增,那样的话,其实其实标志+终止标志的状态就会变成唯一状态,这样我们其实很好区分是空还是满,满就是起始等于终止,空就是起始比终止小N,是的,这是一种不错的做法,并且很容易实现,而且也没有特别大的问题,但是如果这个队列是被频繁操作的话那么,起始和终止标志将会变得越来越大,我们知道取模操作其实是比较消耗差的运算,当然如果把长度设定成2的幂次那么就可以改用位运算来解决这个问题,但是另外其实还有一个很假的隐患,就是起始标志和终止标志溢出。–||。
下面我要说的是另外一种相对比较巧妙的办法,其通过冗余1各单位的数据来实现上面相同的效果,关键点在于判断是否是满变成(终止+1)%N==开始,这样浪费一个单位的空间来和判断空区分开来,其实和数据结构书上的实现差不多了,只要修改一下判断满,去掉长度就可以了

#ifndef __RING_QUEUE__
#define __RING_QUEUE__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

union __type {
    char char_val;
    int int_val;
    float float_val;
    long long_val;
    double double_val;
    long long llong_val;
    void* buff;
};

struct __ring_queue {
    int __index_start;
    int __index_end;
    int __ring_size;
    union __type* __buff;
};

#define __CAS_TRY 3

#define __is_full(ringqueue) \
    (((ringqueue).__index_end + 1) % \
     (ringqueue).__ring_size == (ringqueue).__index_start)
#define __is_empty(ringqueue) \
    ((ringqueue).__index_start == (ringqueue).__index_end)

inline void __ring_queue_init(struct __ring_queue* ringqueue, int size) {
    if(ringqueue == NULL) {
        fprintf(stderr, "ringqueue == NULL\n");
        return;
    }

    ringqueue->__index_start = 0;
    ringqueue->__index_end = 0;
    ringqueue->__ring_size = size + 1;

    ringqueue->__buff = malloc(sizeof(union __type) * ringqueue->__ring_size);
    if(ringqueue->__buff == NULL) {
        perror(strerror(errno));
        ringqueue->__ring_size = 0;
    }
}

inline void __ring_queue_free(struct __ring_queue* ringqueue) {
    if(ringqueue != NULL && ringqueue->__buff != NULL) {
        free(ringqueue->__buff);
        ringqueue->__buff = NULL;
    }
    ringqueue->__index_start = 0;
    ringqueue->__index_end = 0;
    ringqueue->__ring_size = 0;
}

inline union __type __ring_queue_pop(struct __ring_queue* ringqueue) {
    union __type data = ringqueue->__buff[ringqueue->__index_start];
    ringqueue->__index_start = (ringqueue->__index_start + 1) % ringqueue->__ring_size;
    return data;
}

inline void __ring_queue_push(struct __ring_queue* ringqueue, union __type data) {
    ringqueue->__buff[ringqueue->__index_end] = data;
    ringqueue->__index_end = (ringqueue->__index_end + 1) % ringqueue->__ring_size;
}

inline int ring_queue_push_cas(struct __ring_queue* ringqueue, union __type data) {
    int i = 0;
    while(__is_full(*ringqueue)) {
        if (++i == __CAS_TRY) {
            return 0;
        }
    }
    __ring_queue_push(ringqueue, data);
    return 1;
}

inline int ring_queue_pop_cas(struct __ring_queue* ringqueue, union __type* data) {
    int i = 0;
    while(__is_empty(*ringqueue)) {
        if (++i == __CAS_TRY) {
            return 0;
        }
    }
    *data = __ring_queue_pop(ringqueue);
    return 1;
}

int main(int argc, char** argv) {
    struct __ring_queue ringqueue;
    __ring_queue_init(&ringqueue, 256);

    int i;
    union __type data = {0};
    for(i = 0; i < 257; i++) {
        if(__is_full(ringqueue)) {
            printf("%d, is full\n", i);
            break;
        }

        printf("%d, is not full\n", i);
        data.int_val = i + 1;
        __ring_queue_push(&ringqueue, data);
    }

    for(i = 0; i < 257; i++) {
        if(__is_empty(ringqueue)) {
            printf("%d, is empty\n", i);
            break;
        }

        printf("%d, %d\n", i, __ring_queue_pop(&ringqueue).int_val);
    }

    __ring_queue_free(&ringqueue);
    return 0;
}

#endif

30

12/11

2011结

5:51 am by livexmm. Filed under: Life

恍惚之间,又到了年末了,最近时间过的特别快,感觉一周都没干啥就又结束了。去年写年终总结的表示很顺畅,今年有点不知道写些啥,估计是最近的工作把自己搞傻了,都不知道在干啥,当然这几天比较空一些,就做做自己的事情。

还是要回顾一下去年定的目标,差不多今年也是按着去年的目标的,但还是有些许遗憾的。

回顾一下今年发生的事情,有点记不清楚了,但貌似发生了很多事情。盛大实习,毕业,离职,加入淘宝,误入技术保障-数据库技术,转核心系统-存储组。不想提这方面的细节了,情况太复杂,太乱了,之前也发过日志,这种事情看得越来越淡了。

技术平台:win32->android->linux

主语言:win32 C/C++ -> Java/Linux C -> Linux C/Java

涉及技术点:win32 sdk/桌面级应用 -> android sdk/ndk/移动设备应用 -> 存储/分布式.

这一年变化很快,从一个技术领域到另外一个技术领域,基本上都是重头开始,啥叫重头开始就是一开始啥都不懂,不懂到Java语法都不懂,Android怎 么写helloworld都不会,Linux怎么调试都不会,linux命令怎么敲都不会。唯一懂的只是我花时间进去早晚会懂的,我愿意在我感兴趣的事情 上花时间。感谢一年里遇到的所有相信我的人。

今年一年基本上是在公司里,虽然上半年还是学生,但在学校里也没待几天,现在比较后悔大学四年为啥没出去旅游一下啥的,大学四年就干了上课,吃饭,睡 觉,看书,写代码这几件事情,读大学的时候毫不后悔自己那时候干的事情,还觉得自己很了不起,觉得其他人在浪费生命,不知所谓。现在想来浪费生命和不知所 谓的是自己而已。大学过的像高中一般,当然高中要压抑很多。自己太急着想证明自己了,大一急着去图书馆,大二急着去实验室,大三急着实习,大四又急着各种 面试,又急着工作……人生太匆匆,完全不给自己看看沿路风景的机会,这完全是急着去死的行为。现在想着要是哪天我又想不开了又离职了,不要又急着去工作, 多上几周时间去其他地方看看。

工作是单调兴奋无聊的。单调的是每天就干类似的事情,甚至有些麻木了。兴奋是有意思的代码要写,这种代码总是偏少的,是可遇不可求的。无聊是当有意思的代码写完了,完全就是一种空虚的感觉,是一种迷茫的感觉,是一种跳脱的冲动。

生活?我没有生活,生活就是工作。

如果2012不是世界的尽头的话(必须不是啊,是的话亏大了),我想还是应该定几个目标:

技术:

1.linux更多的深入学习,打好基础,谁让现在在linux平台呢

2.学习更多的开源项目源码,看看别人怎么想的

3.更多的书籍和论文,要与时俱进

工作:

1.tair-rdb赶紧上线,哎,可惜不是我能说了算……

2.明年的双11,双12要看到我所开发的东西的身影

3.tair的运维

4.未知

生活:

1.有时间出去逛逛

2.不知道

28

12/11

MessagePack试玩

6:18 pm by livexmm. Filed under: Java

帮同学做一个简单的分布式系统,画了一下架构图,打算消息序列化采用MessagePack,早就听说MessagePack序列化的性能之出众。

上面图来自MessagePack的官网,是JSON的10倍,Protocol Buffers的4倍,正因为其霸道的性能在我脑海中留下的印象打算在该项目中引入进来,于是需要做一下技术评估
感觉比较麻烦的是官网上提供的maven地址里没有源码,没法调试看为啥错,于是去指定的github上拉了源码,maven打了包到本地的maven资源库里
打算先把官网上的Java代码试试,结果发现@Message这类东西没有,不知道是不是我版本不对,我下的是0.5.2-devel,后来改了一下代码如下

package com.distributed.common;

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.msgpack.MessagePack;
import org.msgpack.annotation.MessagePackMessage;

public class TestMessagePack {
	@MessagePackMessage
	public static class MyBaseMessage {
		public String d = "100";
		protected String e = "12";
		public MyBaseMessage() {
			d = "www";
		}
		public void setE(String tmp) {
			e = tmp;
		}
	}

	@MessagePackMessage
	public static class MyMessage extends MyBaseMessage {
		public String a = "10";
		public String b = "20";
		private String c = "12";
		//private Map<String, String> map = new HashMap<String, String>();

		public MyMessage() {
			super();
			a = "130";
		}

		@Override
		public boolean equals(Object obj) {
			if (obj instanceof MyMessage) {
				MyMessage tmp = (MyMessage)obj;
				if ((a == null && tmp.a == null) ||
						 (a != null && tmp.a != null && a.equals(tmp.a))) {
					if ((b == null && tmp.b == null) ||
							(b != null && tmp.b != null && b.equals(tmp.b))) {
						return true;
					}
				}
			}
			return false;
		}

		public void put(String key, String value) {
			map.put(key, value);
		}
	}

	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void test() {
		MyMessage src = new MyMessage();
		src.a = "100";
		src.setE("tmp");
		src.d = "dddd";
		//src.put("key", "value");
		// Serialize
		byte[] bytes = MessagePack.pack(src);
		// Deserialize
		MyMessage dst = MessagePack.unpack(bytes, MyMessage.class);
		assertEquals(src, dst);
	}

}

一切正常,毫无压力,但是当我加入HashMap之后明显解析不对了,网上查了一下MessagePack现在只能处理基本类型,向Map,List这些是不支持的,蛋疼了,然后更蛋疼的是MessagePack的序列化是基于成员顺序的,也就是序列化和反序列化的2头的参数顺序要相同,这…………怪不得性能和压缩比都那么高,只好放弃了。以前组长和我说起抽二手烟的事情,大概意思是只是听说和看看是远远不够的,那和抽二手烟无异,关键还是自己要去试试效果。

27

12/11

Linux Shell小试

7:17 pm by livexmm. Filed under: Linux,Shell
Tags: , ,

最近项目做性能测试,比较无奈,不是很喜欢这样的事情,整天不知道自己在干啥,不断的配置各个机器,就是一些重复工作,于是顺带学习一下shell的一些东西,原先只是知道有这个东西,没写过,学着学习了一下,一小点感触是shell本身应该是并不强大的,但是其是灵活的,因为其灵活性带来了功能的强大性,可以很方面在代码中调用其他程序(其实此时的程序已经无形中融化为shell的一部分了)。带其晦涩的语法真觉得不是啥好的,写写简单的还好,复杂的比较头大了 下面是我写的2段样例,主要作用是通过执行shell脚本直接修改不同服务器上的程序配置 然后启动这些程序 功能很简单 代码很很短

#事前要做ssh打通 利用sed的正则表达式匹配替换(其实这里用perl也很方便)
array[0]='10.232.4.17'
array[1]='10.232.4.18'

for ip in ${array[@]}; do
        echo $ip
        ssh $ip "cd /home/choutian.xmm/ycsb; \
                 if [ \"$1\" = \"get\" ]; then \
                        dotag='readproportion='; \
                 elif [ \"$1\" = \"put\" ]; then \
                        dotag='insertproportion='; \
                 elif [ \"$1\" = \"push\" ]; then \
                        dotag='pushproportion='; \
                 elif [ \"$1\" = \"pop\" ]; then \
                        dotag='popproportion='; \
                 fi; \
                 src=\${dotag}\".*\"; \
                 target=\${dotag}\"1\"; \
                 echo \$src; \
                 echo \$target; \
                 sed -e \"s/\$src/\$target/g\" workload_tair_org | \
                 sed -e \"s/^threadcount=.*/threadcount=$2/g\" | \
                 sed -e \"s/^recordcount=.*/recordcount=$3/g\" | \
                 sed -e \"s/^operationcount=.*/operationcount=$4/g\" > workload_tair"
done
#用于启动tair的dataserver 如果本身存在就要先把原来的dataserver干掉
echo "---start dataserver---"
ssh 10.232.4.20 "hostname; \
        tmp=\`ps -ef | grep tair_server | grep -v grep | awk '{print \$2}'\`; \
        if [ \"\$tmp\" ]; then \
                echo \$tmp; \
                kill \$tmp; \
        else \
                echo \"no process\"; \
        fi; \
        cd /home/choutian.xmm/tair_redis/tair_redis_bin; \
        pwd; \
        sleep 2; \
        rm -rf redis-lib.log; \
        rm -rf logs/*; \
        sbin/tair_server -f etc/dataserver.conf;"

echo "---touch group.conf---"
ssh 10.232.4.19 "hostname; touch /home/choutian.xmm/tair_redis_bin/etc/group.conf"

12

12/11

Code Play

12:42 pm by livexmm. Filed under: C/C++,Code Play
Tags:

昨天在微博上看到一条信息:
请教:有10万个离散的8Bytes,和另11万个离散的8bytes,两批中有10万个8bytes是相同的。如果要列出(写到txt中)其中不同的1万个8bytes,大概要花费多长时间?假设为普通PC Server的单颗4核CPU,3GHz频率。
并看了某人的的boost实现,并并且在我的东芝电脑笔记本(双核T2370 1.73GHz)上跑了一下,大概在0.15xxx秒,深感这个问题应该可以更快,于是在微博上回复了我的想法,今天看微博要求看代码,于是写了一个简单实现,大概在0.05xxx秒。下面代码有几点要注意:1.代码的执行效率有2点有关系1.哈希函数,好的哈希函数可以尽量使数据离散,我的哈希函数并不好,命中率大概维持在一半不到 2.哈希表的桶的数量,理论上哈希桶数为素数(非素数会有循环域问题,从而减小实际效果),桶数我选择了199999,实际上可以选择更大的性能会更好,但是是否可以选择放弃那么多内存是值得思量的 3.代码是随手写的,可能存在bug,但是即使有bug,也是修改分支而已,基本不影响执行效率 4.代码实现了2个功能求2个数组不同以及求相同,去掉相同性能更好,但提升不会太多 5.题目没有说没有重复的数据,所以我的生成数据里可能有重复数据,我没有去特地保证没有重复,即使没有重复也是一样的逻辑,性能也不会差多少 欢迎批评指教
[有人回复说代码有问题 确实有问题 太水了 现已经修正 写了个小程序对拍了一下没啥问题 如有错 欢迎继续批评指正]

#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#define V1_MAX 100000
#define V2_MAX 110000
#define HASH_MAX 199999
#define DIFF_MAX 110000

typedef struct hashNode {
    int count;//count smaller than 0, mean v2
    long long data;
    struct hashNode* next;
} hashNode;

typedef struct out_array {
    long long array[DIFF_MAX];
    int index;
} out_array;

long long v1[V1_MAX];
long long v2[V2_MAX];

hashNode hash[HASH_MAX];

out_array out_diff;
out_array out_same;//may have same number
out_array out_same_diff; //have no same number

void init();
void initV(long long* V, int maxsize);
int simpleHashFunc(long long number);
void doV1();
void doV2();
void dofree();
void play();
void begin();
void end();

double now() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + tv.tv_usec / 1e6;
}

void initV(long long* v, int maxsize) {
    int i, j;
    for(i = 0; i < maxsize; i++) {
        v[i] = 0;
        for(j = 0; j < 8; j++) {
            v[i] = (v[i]<<8) + rand() % 256;
        }
    }
}

void loadFromFile(char* filename, long long* v) {
    FILE* fp = fopen(filename, "r");
    if(fp == NULL) {
        return;
    }

    int index = 0;
    while(fscanf(fp, "%lld", &v[index]) != EOF) {
        index++;
    }
    fclose(fp);
}

void init() {
    srand(time(NULL));
    //loadFromFile("v1.txt", v1);
    //loadFromFile("v2.txt", v2);
    initV(v1, V1_MAX);
    initV(v2, V2_MAX);
}

inline int simpleHashFunc(long long number) {
    unsigned long long tmp = (unsigned long long)number;
    int index = tmp % HASH_MAX;
    return index;
}

void doV1() {
    int i;
    int index = 0;
    hashNode* p = NULL;
    hashNode* q = NULL;

    for(i = 0; i < V1_MAX; i++) {
        index = simpleHashFunc(v1[i]);
        p = &hash[index];
        while(p != NULL) {
            if(p->count == 0) {
                p->count = 1;
                p->data = v1[i];
                p->next = NULL;
                break;
            } else if(p->data == v1[i]) {
                p->count++;
                break;
            }
            q = p;
            p = p->next;
        }
        if(p == NULL) {
            void* tmp = malloc(sizeof(hashNode));
            if(tmp == NULL) {
                perror(strerror(errno));
                exit(0);
            }
            p = (hashNode*)tmp;
            p->count = 1;
            p->data = v1[i];
            p->next = NULL;
            q->next = p;
        }
    }
}

void doV2() {
    int i;
    int index = 0;
    hashNode* p = NULL;
    hashNode* q = NULL;

    for(i = 0; i < V2_MAX; i++) {
        index = simpleHashFunc(v2[i]);
        p = &hash[index];
        int flag = 0;
        while(p != NULL) {
            if(p->count == 0) {
                p->count = -1;
                p->data = v2[i];
                p->next = NULL;
                out_diff.array[out_diff.index] = v2[i];
                out_diff.index++;
                flag = 1;
                break;
            } else if(p->data == v2[i]) {
                if(p->count > 0) {
                    out_same.array[out_same.index] = v2[i];
                    out_same.index++;
                    if(p->count < 1000000) {
                        out_same_diff.array[out_same_diff.index] = v2[i];
                        out_same_diff.index++;
                        p->count += 1000000;
                    }
                } else {
                    p->count--;
                }
                break;
            }
            q = p;
            p = p->next;
        }
        if(p == NULL) {
            if (flag == 1) {
                assert(0);
            }
            void* tmp = malloc(sizeof(hashNode));
            if(tmp == NULL) {
                perror(strerror(errno));
                exit(0);
            }
            p = (hashNode*)tmp;
            p->count = -1;
            p->data = v2[i];
            p->next = NULL;
            q->next = p;

            out_diff.array[out_diff.index] = v2[i];
            out_diff.index++;
        }
    }
}

void dofree() {
    int i;
    int count = 0;
    hashNode *p = NULL;
    for(i = 0; i < HASH_MAX; i++) {
        p = hash[i].next;
        while(p != NULL) {
            hash[i].next = p->next;
            count++;
            free(p);
            p = hash[i].next;
        }
    }
}

void play() {
    out_diff.index = 0;
    out_same.index = 0;
    out_same_diff.index = 0;
    memset(hash, 0, sizeof(hash));

    double begin_time = now();
    doV1();
    doV2();
    double end_time = now();

    printf("time used: %lf\n", end_time - begin_time);
    printf("same: %d, same_diff: %d, diff %d\n", out_same.index,
            out_same_diff.index, out_diff.index);
}

void begin() {
    init();
    printf("begin-->\n");
}

void end() {
    printf("end-->\n");
    dofree();
    printf("over-->\n");
}

int main(int argc, char* argv[]) {
    begin();
    play();
    end();
    return 0;
}

28

11/11

linux多线程简单实例

12:26 pm by livexmm. Filed under: C/C++,Code Play,Linux

有同事今天发了个使用pthread时,线程被阻塞的报告。简单学习了一下,主要注意几点:
1.Linux中线程的资源释放必须要通过其他线程来释放或者设置detached,所以我们pthread_join了
2.在使用pthread_cancel以及锁等待时,需要注册cleanup_handler,为的是如果线程被取消,没有解锁,线程将继续等待下去,表现就是死在那里。
于是我写了段简单的代码,play了一下:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define THREAD_MAX  10

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void cleanup_handler(void* argv) {
    printf("thread %u has been clean, we should cleanup lock thread\n", (unsigned int)pthread_self());
    pthread_mutex_unlock(&mutex);
}

void* thread_func(void* argv) {
    pthread_mutex_lock(&mutex);

    pthread_cleanup_push(cleanup_handler, 1);
    pthread_cond_wait(&cond, &mutex);

    printf("thread %u do %d\n", (unsigned int)pthread_self(), (int)argv);
    pthread_cond_signal(&cond);

    pthread_mutex_unlock(&mutex);

    pthread_cleanup_pop(1);
    return 0;
}

int main(int argc, char* argv[]) {
    int i;
    pthread_t thread_array[THREAD_MAX];
    for(i = 0; i < THREAD_MAX; i++) {
        int flag = pthread_create(&thread_array[i], NULL, thread_func, (void*)i);
        if (flag != 0) {
            perror(strerror(errno));
            return 1;
        }
    }

    pthread_mutex_lock(&mutex);
    for(i = 0; i < THREAD_MAX; i++) {
        printf("main thread %d\n", (unsigned int)pthread_self());
    }
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);

    for(i = 0; i < THREAD_MAX; i++) {
        pthread_cancel(thread_array[i]);
    }

    for(i = 0; i < THREAD_MAX; i++) {
        pthread_join(thread_array[i], NULL);
    }

    printf("over!!\n");

    return 0;
}

26

11/11

Redis-Aof-Tool实现

12:11 am by livexmm. Filed under: Redis
Tags: , ,

公司有应用提向我提需求,希望使用redis来实现server的一些数据的缓存,而数据有这样的特点:
1.每天的数据是通过Hadoop集群计算+原本的基准数据
2.数据除了每天从Hadoop集群定时获取来的+原本的基准数据,不会有其他途径获得数据
3.基准数据目前在2G,增量数据现在还较少,但是该业务发展比较迅速,可能增量数据会增加很快
这意为可以每天可以做一次重新加载和更新,并且可以使用redis做持久化,并且没有必要担心可能因为当机会带来数据丢失而数据不一致,数据的全量数据就在持久化中,最后我选择使用aof来做这个事情,我们知道aof比较影响性能,但是这主要是发生在写入时,而这里并没有实时性的写入,一次写入一天都不用变,并且第二天的数据又是从hadoop集群重新计算得到的,这也就aof在这里不会成为性能瓶颈。另外主要的一点是我希望加载速度较快一些,加载数据有3中方式1.采用程序调用api进行插入,明显这性能会差不少。2.rdb快照加载,这种方式是性能相对比较高的,但是解析该协议相对要麻烦些3.aof日志加载,这种方式协议简单,加载性能又相对可以,当然当数据量到上几百G的时候,可能需要的时间会长不少,但是现在场景比较简单的,并且几年内并不可能长到那么大的数据量,总的来说是比较适用的。
下面主要讲一下redis的aof的格式,要注意一点aof存的是修改日志,想读取日志是不会存的也是没有必要存储。aof格式比较简单是属于文本格式的一种,其协议和redis推荐的网络传输协议是相同的(也就是redis的新网络协议),所以redis存aof格式化的话也是比较简单的可以直接把网络通信过来的字符串存入本地,当然有一些想setnx,lpushx是会被转换成set以及lpush的,这里不展开,之后有机会可以讲一下redis的aof实现,这里就不提了。格式如下
*CmdLen\r\n
$CmdNameLen\r\n
Cmd\r\n
$argv1Len\r\n
argv1\r\n
$argv2Len\r\n
argv2\r\n
…..
CmdLen指的是总过的参数+1(1是Cmd字符串)的个数,后面的XXXxLen表示的是参数字符串或者Command字符串长度

是的,redis的aof协议就是这么简单,所以我做了这个一个工具提供了hset,set,sadd,zadd,lpush,rpush,因为只需要这几个指令就可以完成把任意的数据转换成aof的格式

http://code.google.com/p/redis-aof-tool/

上面是该工具的地址 代码写的比较猥琐 欢迎拍砖

21

11/11

Nodejs入门学习

11:26 pm by livexmm. Filed under: Javascript,Nodejs

关注nodejs的发展已经有段时间,前几天闲着无聊,写了一段入门级代码,功能很简单start页面显示当前目录文件,upload可以上传文字,并跳转到show页面显示.

index.js为server的入口文件,做了很简单的事情,对请求处理函数做个映射,然后调用server的start方法

var server = require("./server");
var router = require("./router");
var requestHandler = require("./requestHandler");

var handler = {};
handler["/"] = requestHandler.start;
handler["/start"] = requestHandler.start;
handler["/upload"] = requestHandler.upload;
handler["/show"] = requestHandler.show;

server.start(router.route, handler);

server.js文件 创建httpserver,并且设置必要的回调函数

var http = require("http");
var url = require("url");

function start(route, handler) {
    function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        var postData = "";
        request.setEncoding("UTF-8");

        request.addListener("data", function(postDataChunk) {
            postData += postDataChunk;
            console.log("data chunk : " + postDataChunk);
        });

        request.addListener("end", function() {
            route(handler, pathname, response, postData);
        });
    }

    http.createServer(onRequest).listen(1234);
    console.log("server is start");
}

exports.start = start;

router.js文件 做的事情很简单做路由(不同的请求指派到不同的处理函数去),找不到对应的函数就向client返回404

function route(handler, pathname, response, postData) {
    if(typeof handler[pathname] === 'function') {
        handler[pathname](response, postData);
    } else {
        response.writeHead("404", {"Content-Type": "text/html"});
response.write("</pre>
<center>
<h1>404 Not Found</h1>
</center>
<pre>");
        response.end();
    }
}

exports.route = route;

requestHandler.js文件 主要是3个请求(start,upload,show)的处理函数。
start函数使用exec函数来获得执行shell命令获取当前目录的信息。
upload函数会从磁盘加载当前目录下的upload.html文件,然后扔给client
show函数就是显示接受到的数据

var fs = require("fs");
var path = require("path");
var querystring = require("querystring");
var exec = require("child_process").exec;

function start(response, postData) {
    console.log("here is start");
    exec("ls -lah", function(error, stdout, stderr) {
         console.log("here is in exec");
         if(error) {
            response.writeHead("500", {"Content-Type": "text/html"});
response.write("</pre>
<center>
<h1>500 Error</h1>
</center>
<pre>");
            response.end();
            return;
         }

         response.writeHead("200", {"Content-Type": "text/plain"});
         response.write(stdout);
         response.end();
   });
}

function upload(response, postData) {
    console.log("here is upload");
    var filename = path.join(process.cwd(), "/upload.html");
    fs.readFile(filename, function(err, data) {
        if(err) {
            response.writeHead("500", {"Content-Type": "text/html"});
response.write("</pre>
<center>
<h1>505 Error</h1>
</center>
<pre>");
            response.end();
            return;
        }

        response.writeHead("200", {"Content-Type": "text/html"});
        response.write(data);
        response.end();
    });
}

function show(response, postData) {
    console.log("here is show");
    if(postData == "") {
        response.writeHead("500", {"Content-Type": "text/html"});
response.write("</pre>
<center>
<h1>500 Error</h1>
</center>
<pre>");
        response.end();
    }

    response.writeHead("200", {"Content-Type": "text/plain"});
    response.write("You Send Data: " + querystring.parse(postData).text);
    response.end();
}

exports.start = start;
exports.upload = upload;
exports.show = show;

upload.html啥都没有 就一个表单


		

21

11/11

libev学习笔记(1)

10:34 pm by livexmm. Filed under: C/C++,Code Study,Linux

libev的代码很简练 但对于看他代码的人 简直就是噩梦 到处都是宏 宏还嵌套 于是我在看libev代码时 对其进行了还原 去掉了宏

struct ev_io {
int fd;
int events;
struct ev_watcher_list *list;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_io *w,int revents);
};

ev_io在触发EV_READ或者是EV_WRITE被调用

struct ev_timer {
ev_tstamp at;
ev_tstamp repeat;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_timer *w,int revents);
};

ev_timer在特定的时间调用,并周期性进行,其基于单调时钟
(PS:单调时钟:此时间来源会严格的线性递增,一般linux会使用系统正常运行时间来表示,也就是从开机开始算起) 触发事件EV_TIMEOUT

struct ev_periodic {
ev_tstamp offset;
ev_tstamp interval;
ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now);
ev_tstamp at;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_periodic *w,int revents);
};

ev_periodic在特定的时间调用,可能会在定期间隔反复调用,其基于UTC时间
(PS:UTC:协调时间 也就是从1970年1月1日00:00:00开始记时) 触发事件EV_PERIODIC

struct ev_signal {
int signum;
struct ev_watcher_list *next;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_signal *w,int revents);
};

ev_signal当接收到指定的信号时调用 触发事件EV_SIGNAL

struct ev_child {
int flag;
int pid;
int rpid;
int rstatus;
struct ev_watcher_list *next;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_child *w,int revents);
};

ev_child当接收到SIGCHLD信号并且waitpid表示了给出的pid时调用 触发EV_CHILD事件
其不支持优先级

struct ev_stat {
ev_timer timer;
ev_tstamp interval;
const char *path;
ev_statdata prev;
ev_statdata attr;
int wd;
struct ev_watcher_list *next;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_stat *w,int revents);
};

ev_stat当每次指定的路径状态数据发生改变时调用 触发EV_STAT

struct ev_idle {
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_idle *w,int revents);
};

ev_idle当啥事情都不需要做的时候调用,用来保持进程远离阻塞 触发EV_IDLE

struct ev_prepare {
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_prepare *w,int revents);
};

ev_prepare每次执行mainloop主循环,在主循环之前调用 触发EV_PREPARE;

struct ev_check {
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_check *w,int revents);
};

ev_check每次执行mainloop主循环,在主循环之后调用 触发EV_CHECK

struct ev_fork {
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop,struct ev_fork *w,int revents);
};

ev_fok在fork行为被检测到,并且在检测子进程之前调用 触发EV_FORK;

struct ev_cleanup {
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop,struct ev_cheanup *w,int revents);
};

ev_cleanup在主循被销毁之后调用 触发EV_CLEANUP

struct ev_embed {
struct ev_loop* other;
ev_io io;
ev_prepare prepare;
ev_check check;
ev_timer timer;
ev_periodic periodic;
ev_idle idle;
ev_fork fork;
ev_cleanup cleanup;
};

ev_embed用于将一个事件循环嵌套到另一个中,当事件循环处理事件的时候被调用

struct ev_async {
sig_atomic_t volatile sent;
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop *loop, struct ev_async *w, int revents);
};

ev_async当ev_async_send通过watcher调用时调用,触发EV_ASYNC

union ev_any_watcher {
struct ev_watcher w;
struct ev_watcher_list wl;
struct ev_io io;
struct ev_timer timer;
struct ev_periodic periodic;
struct ev_signal signal;
struct ev_child child;
#if EV_STAT_ENABLE
struct ev_stat stat;
#endif
#if EV_IDLE_ENABLE
struct ev_idle idle;
#endif
struct ev_prepare prepare;
struct ev_check check;
#if EV_FORK_ENABLE
struct ev_fork fork;
#endif
#if EV_CLEANUP_ENABLE
struct ev_cleanup cleanup;
#endif
#if EV_EMBED_ENABLE
struct ev_embed embed;
#endif
#if EV_ASYNC_ENABLE
struct ev_async async;
#endif
};

该结构的存在用以强制类似结构的布局

struct ev_watcher {
int active;
int pending;
int priority;
void* data;
void (*cb)(struct ev_loop* loop, struct ev_watcher *w, int revent);
}

struct ev_watcher_list {
struct ev_wather_list *next;
int active;
int pending;
int prioirty;
void* data;
void (*cb)(struct ev_loop* loop, struct ev_watcher_list *w,int revent);
};
static unsigned int ev_linux_version(void)

通过使用utsname结构以及uname函数获取linux版本号

static void ev_printerr(const char *msg)

输出错误新方法,实现很简单用write直接往STDERR写数据

void ev_set_syserr_cb(void (*cb)(const char *msg))

设置系统错误控制方法回调

static void ev_syserr(const char *msg)

系统错误处理函数,首先判断msg是否为空,msg==NULL填充为”(libev) system error”,然后如果系统错误控制回调不为空 也就是通过上面方法设置的,就调用该控制回调 否则的话就格式化输出 并中断程序运行

static void* ev_realloc_emul(void *ptr, long size)

作者分装的一个realloc,主要为了屏蔽不同平台的差异,比如 有些系统(如openBSD)不支持realloc(x,0)这种行为,于是作者进行了判定如果是含有__GLIBC__宏 那么就可以之间诶用realloc 否则的话当size为0则进行free操作

static void *(*alloc)(void *ptr, long size) = ev_realloc_emul;

定义了一个变量,并赋值,啥变量 一个指向返回值是void* 参数是void*,long的函数指针 不过看着总觉得不爽 习惯于typedef的写法,另一方面也就是说alloc等价与ev_realloc_emul哦

void ev_set_allocator(void *(*cb)(void* ptr,long size))

设置alloc函数的实现,我们在上面的语句看到alloc的默认实现是啥,通过该函数可以改变其实现

inline_speed void* ev_realloc(void* ptr, long size)

一个realloc的实现调用的alloc,比之增加错误日志输出 有意思的是这里有一个叫做inline_speed的宏,libev的作者在libev中大量使用了,使人看代码相当不爽的说

#if EV_FEATURE_CODE
#define inline_speed static inline
#else
#define inline_speed static noinline
#endif

这里又一把宏定义,不过都是见名知义,EV_FEATURE_CODE默认情况是非0,也就是inline_speed默认为static inline

#define ev_malloc(size) ev_realloc(0,(size))
#define ev_free(ptr)m ev_realloc((ptr),0)

我们看到ev_malloc以及ev_free其实也是通过ev_realloc实现的

typedef struct{
ev_watcher_list* head; //监听者链表
unsigned char events; //监听的事件
unsigned char reify;//状态位 用来表示具体是EV_ANFD_REIFY还是EV_IOFDSET
unsigned char emask;//epoll用来保存内核mask的值
unsigned char unused;//同名字
#if EV_USE_EPOLL
unsigned int egen;
#endif
#if EV_SELECT_ISWINSOCKET || EV_USE_IOCP
SOCKET handle
#endif
#if EV_USE_IOCP
OVERLAPPED or,ow;
#endif
} ANFD;

文件描述符信息结构

typedef struct {
ev_watcher* w;
int events;
} ANPENDING;

指定等待事件的监听者结构

typedef struct {
ev_watcher_list* head;
} ANFS;

每个inotify-id对应的哈希表的每个节点的结构

typedef struct {
ev_tstamp at;
ev_watcher_time* w;
} ANHE;

堆结构的节点

struct ev_loop {
ev_tstamp ev_rt_now;
}
ev_tstamp ev_time(void)

获取当前时间如果设置了EV_USE_REALTIME会考虑是否使用clock_gettime纳秒(have_realtiem如果为非负就使用 clock_gettime了) 否则使用gettimeofday 注意这里的时间都是和当前设置的时间是有关系的 也就是说如果我把系统时间修改了 可能得到过去的时间
另外这里用了一个宏expect_true 其原型是__builtin_expect((a) != 0, 1)也就是告诉编译器绝大多数情况下a != 0是满足的 方便编译器进行优化 同理相反的还有expect_false

inline_size ev_tstamp get_clock(void)

这个函数优先是使用CLOCK_MONOTONIC时间 也就是单调时间不会被修改减小 然后达不到要求 则调用ev_time来获取时间 inline_size就是 static inline

ev_tstamp ev_now(struct ev_loop *loop)

获取当前时间时间 也就是loop->ev_rt_now

void ev_sleep(ev_tstamp delay)

如果可以使用nanosleep则使用nanosleep 如果在window下使用Sleep,否则使用select实现

inline_size int array_next(int elem, int cur, int cnt)

用来生成内存对齐大小的实际需要开辟的内存大

static ninline void* array_realloc(int elem, void *base, int *cur, int cnt)

开辟或重新非配一定大小的内存块,该方法会调用上面的函数进行内存对齐大小 所以返回后的大小不一定会和想开辟的相等

array_init_zero(base,count)

顾名思义初始化为0,通过memset实现

array_needsize(type,base,cur,cnt,init)

分配需要大小的内存 就是分配出内存 并初始化为0

array_free(stem,idx)

释放内存

Older Posts »