Jul 6
前一阵更新了Virtualbox到3.2.4,因为7788的原因,虚拟机配置丢失,于是重新建立,再用vboxmanage设置NAT端口转发。再次启动虚拟机,提示无法启动:
引用
Configuration error: Failed to get the "MAC" value.
VBox status code: -2103 (VERR_CFGM_VALUE_NOT_FOUND).

搜了一下,在这个页面:http://forums.virtualbox.org/viewtopic.php?t=7175 的回复中看到解答。

原来的vbox都是使用PCNET作为虚拟网卡,而3.2.4新建虚拟机的时候,虚拟的则是Intel的网卡。因此原先用于设置NAT的命令:
引用
VBoxManage setextradata "Ubuntu" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/Protocol" TCP
VBoxManage setextradata "Ubuntu" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/GuestPort" 2222
VBoxManage setextradata "Ubuntu" "VBoxInternal/Devices/pcnet/0/LUN#0/Config/guestssh/HostPort" 22

就不能再使用pcnet了。

然后再一查文档,发现3.2.4里头vboxmanage已经不用setextradata来设置Port Forwarding了,而是改成更简洁易懂的:
引用
VBoxManage modifyvm "Ubuntu" --natpf1 "guestssh,tcp,,2222,,22"
vboxmanage 修改vm配置 虚拟机名(Ubuntu) nat_port_forwarding(第1个网卡) "端口转发名(guestssh),tcp,宿主机IP(略),宿主机端口2222,虚拟机IP(略),虚拟机端口22"


p.s. 端口转发的信息是存放在虚拟机的xml配置文件文件里了,需要重启(或休眠->恢复)以后才能重新载入
Jun 28
对字符指针数组使用qsort排序时,strcmp强制类型转换后不能直接用于qsort, 需要进一步的纠结的。包装……
p.s. 对于字符串数组char a[100][100]; 就可以用strcmp。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef int (*cmp_func)(const void *, const void *);

int strcmp_for_qsort(const void *a, const void *b)
{
    char *x = *(char **)a, *y = *(char **)b;
    return strcmp(x, y);
}

void quicksort(void *p, int num, unsigned size, cmp_func cmp)
{
    int i, j;
    char *pos, *t = (char *) malloc(size);
    if (t == NULL) exit(1);
    for (i = 0; i < num; i++)
    {
        for (j = 0, pos = (char *)p; j < num - 1; j++, pos += size)
        {
            if (cmp(pos, pos + size) > 0)
            {
                memcpy(t, pos, size);
                memcpy(pos, pos + size, size);
                memcpy(pos + size, t, size);
            }
        }
    }
    free(t);
}

int main()
{
    char *pos[5] = {"a", "c", "e", "b", "d"};
    //qsort(pos, 5, sizeof(pos[0]), strcmp_for_qsort); // in stdlib
    quicksort(pos, 5, sizeof(pos[0]), strcmp_for_qsort); //fake
    for (int i = 0; i < 5; i++)
        puts(pos[i]);
    return 0;
}
Jun 27

技术无用论 不指定

felix021 @ 2010-6-27 04:07 [IT » 其他] 评论(6) , 引用(0) , 阅读(7047) | Via 本站原创
也许我有点悲观,但是在我心里对“成功”的尚未明晰的公式里,“技术”这个变量的权重减少了。当然,这个技术指的是计算机专业技能,而这里的成功,也算是很概念比较宽泛、理解比较狭隘的成功,比如说,有较大的影响力,有自己的事业,赚很多钱(最次)。

记得刚入学的时候,向04级的江学姐(当时她大三)取经,她说,信息安全专业有几个方向,比如网络攻防,密码学……那时虽然啥都不懂,但是还是看过些把黑客描绘得天花乱坠的一些文章,甚至我还存着些看不懂的黑客秘籍。于是我能感觉到自己是两眼放光地说,我对网络攻防很有兴趣。那时懵懵懂懂的,对那些能在网络上呼风唤雨的技术牛人无限葱白。转眼四年过去了,我发现我现在对网络攻防一点兴趣都没有。

作为一个计算机专业本科毕业生(我居然已经不是学生了!),在学校里摸打滚爬了三年多,上课,自学,混社团,接项目,又到公司去实习过半年,还跟几位曾经在IT公司打拼过的朋友保持或紧或松的联系,让我对技术有了新的理解。

技术是什么?这个问题可真是既宽泛又无趣,简直就像“傻逼有什么特征”。也许我可以给个对得上大多数人胃口的、更具体的、不怎么准备的回答:写得一手好代码,有扎实的算法和数据结构功底;开发得出像样的软件或游戏;能管理各种服务器,尤其是熟悉各种网络协议、设备;能用PS设计出精美的图片,能用Flash做出很酷的动画;熟知那些我看来很枯燥的理论,比如图形学或者密码学,发得出几篇论文……这里说到的各个方面每个都和我身边的某个人对应。他们都是大家熟知的牛人(相比之下,我就是个四不像)。

然而在现在的我看来,装B一点的说,我觉得以上都属于较低层次的技术,一个仅仅为吃饭准备的铁饭碗,与其他专业相比没什么值得炫耀的。倒是最牛的那些人,他们可以创造出新的数据结构和算法,比如KMP;他们可以设计(更重要的在于设计)出牛X的系统,比如Google的三驾马车;他们可以不再在某个机器的层次上纠结,甚至设计新的互联网,比如IPv6;他们可以在理论的路上走得更远,但是又不脱离实际。无论什么时候,我对这些牛人的葱白都不会真的减少,因为他们能把这当作自己的追求,实现自己的成功。但是我,也许在未来的某个时候,不会再朝着他们的方向前进。

点击在新窗口中浏览此图片

《程序员的幸福生活之路》这张图片虽是恶搞,却也不乏辛酸的自嘲。在高中捣腾电脑的那阵,就已然听说程序员这活儿,吃的是青春饭,到了三十多岁吃不动了,就会被年轻人挤出来。所以网上传的那段子——“你程序员吧?" "你才程序员呢,你全家都程序员!"——可谓道尽老程序员的苦。当然,如果从二十来岁入行,到了三十多岁还在程序员的位子上原地打转,那也只能说明这人能力不行,优胜劣汰是必然的。然而就算能往上走呢?

能往上走,爬到金字塔的顶端,自然可谓成功。但是很多人都忽略了这前提,能吗?大中公司,诸如百度腾讯等,从一个底层程序员(职位美名为工程师)干起,想要爬到金字塔的中间都是比较困难的事情,何况就算爬到了,也不过是”赚还算比较多的钱“的层次;小公司,爬上去容易,但是大多数小公司无法负担远大的未来。由于信息源的缺失,以及难免的下意识信息过滤,大多数人都只看到成功的辉煌,而几乎没人关注失败者落寞的背影。所谓一将功成万骨枯,金字塔的顶端就P大的地方,想跟着别人从下面爬上去,没准早被别人踹死了。

所以有很多人觉得,爬上别人的金字塔,不如自己造一座,就像不胜数的硅谷梦创造的奇迹。这让我想起电影《雀圣》里面的一句话:都说香港遍地黄金,你知道黄金下面埋着多少尸骸!我们看到了google,看到了facebook,看到了twitter,却看不到被世纪之交那场泡沫淹没的那无数尸骸。那么多成功的典型,引着人们前仆后继。所幸的是,互联网的一波波新潮,带来无限的机会,团购,Dropbox,foursquare等新星的崛起又给人带来无限的希望。顺便说一句,facebook, twitter, dropbox, foursquare和大部分的google服务都被墙了,在目前的体制内想搞出点啥伟大的东西还真没啥希望。

于是话题终于转到了创业。一谈到创业,技术突然就变成很次要的因素了。这里有两篇文章(非常值得一看):《Dropbox 和 Xobni 如何在兩年內從 0 成長到 100 萬會員》  http://feedproxy.google.com/~r/MrJamie/~3/xUGw-48yJII/ 《Dropbox 和 Xobni 在兩年內突破百萬會員的七個關鍵》 http://feedproxy.google.com/~r/MrJamie/~3/RlRwIp_WDX8/ 。里面提到了两年前就让我大流口水的segway车。两年过去了,它离我还是那么遥远,就像摩托罗拉的铱星系统,把他们看成艺术品更合适。这两篇文章讲得很细,但是几乎就没有提起技术相关的事情,甚至还有"反技术"的倾向(Fake It Till You Make It)。其中点出了创业的三大要素:”優秀的團隊、正確的市場和非常好的執行力“。

这让我想起前一段时间参与的、由Ye发起的一个创业型的小项目,抄的是Aardvark的模式。Ye的社会关系比较丰富,找到了在创新工厂的人(Chen)推荐,希望能够拿到创新工厂的投资。由于项目本身还很不成熟,所以最后黄了,但是Ye、Feli他俩与Chen的聊天也透露出了很多信息。创新工厂对一个项目的评价标准并不是技术含量,而是前面提到的:团队、市场、执行力。尤其是在Web2.0的概念下,很难做出他人无法剽窃的核心竞争力了,这时团队和执行力几乎是成功的基本保障了。否则像马蜂一样扑面而来的400多家团购网站,会让人想死……

另一方面,前面顺便说到的那一句,是因为我对国内环境的失望。当年ZF没能意识到互联网的”危害“,放任自流,互联网才能有如此发展。现在发现网络的问题了,对政治监管不断加强加固加猛,忽视行业规范运作,反而放任国家队的进攻……所以本来就高风险的IT创业在国内更加难以捉摸——廉颇老矣,尚能"饭否"?

巴菲特的赚钱哲学是“投资而非投机”。曾有人问巴菲特为什么不投科技类的产业,巴菲特的答案是:“好的公司、不好的公司,是我花半天时间就能够看出来的,而那些花了半天时间看不出来的【比如科技类公司】,我也不会强迫自己花半个多月时间去看。我不会强迫自己花费很多时间在一个我不能“Figure out”的公司上。”在我看来,“投资”和”投机“,只是处理资金、时间等资源时冒险的程度不同。投资是将其投入低风险的领域,而投机则相反。从另一个角度来看,回报和风险又是成正相关关系的,所以这里头的关系还是有点纠缠,看来不是那么容易能说清的,暂且忽略。由于科技类公司通常都是高风险高回报的,更倾向于投机,因而巴菲特选择避开。而对于创业者,创业本身就是一种投资/投机,所以这就有个抉择:是选择科技类(如IT)的高风险高回报,还是选择实业型的稳扎稳打?

所以当我问起一个工作了好几年的朋友将来的打算时,他说,攒够资本,做实业。我想,这决定也许与年龄有关,年轻时冲劲比较大,有足够的时间资本,缺的是资金,只要能拉到,轰轰烈烈的干几年,即使失败也无妨;随着岁月逐渐流逝,梦想淡出,冲动渐冷,还是攒些养家养老的钱比较稳妥。

于我,"Niche First, World Later"正是我一直以来推崇的解决方案。下一步,加入magnetjoy这个创业型的团队,希望接下来三个月的实习能让我对以上提到的内容有新的、更进一步的认识。
Jun 8
我的邀请注册链接: https://db.tt/5PWtd8Vc

最大化Dropbox的免费空间(4G+)

虽然早就听说Dropbox这个同步应用,但是一直没有用上。根据
引用
GFW 三定律
GFW 第一定律:只要是 “用户产生内容”(User-generated content, UGC) 的国外网站都会被和谐。
GFW 第二定律:只要是被和谐的网站,国内一定会有个克隆版。
GFW 第三定律:没有被和谐的网站一定不是同类竞争者中最出色的。
Dropbox于近日被GFW掐掉,于是我决定开始使用它。

简单地说,Dropbox在单纯网络硬盘之上提供了数个很有创意的功能。包括:
a. 2G免费空间,介绍一个用户增加250M,上限是8G(或者10G?没理解)。
b. 自动同步。由于有个客户端,可以在多个电脑、手机之间自动同步文件,非常方便。
c. 照片文件夹可以作为WEB相册访问
d. 共享文件夹允许你和多个朋友一起共享、交流文件,尤其特别是小文档,比邮箱方便快捷,比IM传文件靠谱。
e. 有一个公开目录,可以生成直接的下载链接。

由于第一定律导致了它被墙的必然,但是第二定律暂时还没生效,因此,希望使用第三定律界定的这个优秀服务还是得绕个弯。所谓“奇伟瑰怪非常之观,常在於险远而人之所罕至”,所以,找个翻墙工具吧!

推荐使用 shadowsocks 来创建一个 socks5 代理,创建好以后安装Dropbox,在 preference -> network 选项卡填入代理(注意代理类型为 socks5),就可以畅享Dropbox了! (注:2018年更新,现在 ssh 和 openvpn、pptpvpn 都不好用了,shadowsocks 还行)

【创建socks代理】

创建 socks5 代理最简单是购买一个;如果有一定动手能力的话,推荐自己搭建:

1. 推荐购买一个国外的 VPS,例如 搬瓦工 年付 19.99 美元的机型:https://clients.hostmybytes.com/aff.php?aff=1042&gid=55,操作系统推荐使用Ubuntu 16.04 (配合下面说明)。购买后可以得到服务器的 ip 和 root 帐号登录密码。

2. 用 ssh 客户端(windows下推荐Tunnelier,下载地址:https://share.weiyun.com/5a3IPmg ),使用 root 密码登录服务器。

3. 安装 shadowsocks 服务端,执行命令:sudo apt-get install -y screen shadowsocks

4. 启动 shadowsocks 服务端,执行命令(记得替换你的密码):screen -dmS ss ssserver -k 密码 -m aes-256-cfb -p 8388
注:screen -ls 如果能看到一条 ss 说明正常启动了。稳妥起见可以在crontab添加一个任务 "* * * * * screen -dmS ss ssserver -k 密码 -m aes-256-cfb -p 8388"

5. 下载并打开 shadowsocks 客户端:
下载文件 (已下载 872 次)


6. 双击通知区域窗口区的纸飞机图标,将服务器的 IP 和端口(8388)、密码填入,就可以在本机启动一个 socks5 代理了。

====
我的邀请注册链接: https://db.tt/5PWtd8Vc

最大化Dropbox的免费空间(4G+)
Jun 4

ssh翻墙路由器打造计划 不指定

felix021 @ 2010-6-4 19:19 [IT » 硬件] 评论(2) , 引用(0) , 阅读(13653) | Via 本站原创
去年2月买的那个ASUS WL-520gu屈居普通路由器的角色已经很久了。由于今年由于各种失窃攒了众多RP,只剩下路由器可以折腾了,于是把它再翻出来。前一阵看到GFW Blog上总是在转载基于openvpn的翻墙路由器相关文章,于是也想搞一个自己的。但是vpn太贵了,ssh倒是正好,配合firefox的auto proxy或者chrome的proxy switchy,效果非常赞。OK,废话少说,切入正题。

需要的硬件:
1. ASUS wl-520gu,带有USB口的开源无线路由器,4Mflash,16Mram,200MHz brcm5354cpu。在 http://www.nslu2-linux.org/wiki/Optware/HomePage 提到的其他应该也OK。
2. 一个普通的U盘。256MB以上。

需要的软件:
1. Linux,貌似绕不过去,总得有的。
2. tomato固件
3. optware.rar

具体步骤:

1. 刷上tomato-1.23.8619-ND-USB-简体中文版.trx,可以在这里下载: http://dl.dbank.com/c0orlm04oj
刷好后在系统管理中开启ssh服务,从第四步开始需要ssh到服务器上操作。

2. 将U盘格式化成ext2或者ext3格式。这个在linux下很容易了,如果u盘是sdb,那么mkfs.ext3 /dev/sdb1就行。windows下可以用paragon partition manager来格式化,在这里下载 http://dl.dbank.com/c0ngokvdku

3. 下载optware.rar,将里面的tar.gz完整地解压到U盘的根目录下。
http://dl.dbank.com/c0zajdjmmp

4. ssh到路由器里头去挂载U盘的相关目录。
在wl-520gu下面U盘默认挂在/mnt/disc0_1,这个可以通过df命令查看到。
然后执行以下命令(就是rar包里的setting.sh的内容)
mount -o bind /mnt/disc0_1/opt /opt
mount -o bind /mnt/disc0_1/home /home

ln -sf /mnt/disc0_1/etc/profile /etc
ln -sf /mnt/disc0_1/etc/hosts /etc
ln -sf /mnt/disc0_1/etc/ipkg.conf /etc

5. 执行一些命令
/opt/bin/ipkg update
/opt/bin/ipkg install openssh
安装完毕以后执行
openssh-ssh -D 0.0.0.0:7070 ssh主机 -l ssh帐户
输入密码后即可连接上并开启sock5 proxy。
p.s. 不要直接输入ipkg,因为tomato固件里的ipkg不太对头,optware里面的才能用。

6. 让它更好用一些
/opt/bin/ipkg install keychain
ssh-keygen -t rsa
回车几下,会生成
/root/.ssh/id_rsa.pub
将此文件中的内容copy到ssh主机里 ~/.ssh/authorized_keys 文件中,下次就不用输入密码了。

可以用“screen -dm openssh-ssh -D 0.0.0.0:7070 ssh主机 -l ssh帐户”来进行连接,这样就不必一直开着连上路由器的终端了。

如果在 系统设置->脚本管理 的“开机脚本”里头加入
((sleep 60; screen -dm openssh-ssh -D 0.0.0.0:7070 felix021@qa.felix021.com;)&);
那么只要路由器开机联网后就能自动连上。

---------------分割线----------------

目前来说用的很好,嗯。不用在本机开着ssh了,很方便。因为ssh绑定的是0.0.0.0:7070,所以理论上只要知道路由器的IP,就可以在任何能连上路由器的地方使用这个proxy。

最后,希望这篇文章不要被发到GFW Blog上,我不想我的博客被墙=。=
May 28

布隆过滤器(bloom_filter) 不指定

felix021 @ 2010-5-28 17:39 [IT » 程序设计] 评论(1) , 引用(0) , 阅读(8595) | Via 本站原创
布隆过滤器的详细介绍和典型用途,可参见
谷歌黑板报(数学之美):http://www.google.cn/ggblog/googlechinablog/2007/07/bloom-filter_7469.html
Wikipedia:http://en.wikipedia.org/wiki/Bloom_filter
中文版详细说明: http://www.cnblogs.com/allensun/archive/2011/02/16/1956532.html

下面是一个简单的布隆过滤器的C/C++实现,以及使用例程。使用sdbmhash字符串hash方法来进行hash。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned int jshash(const char *s, unsigned size);
unsigned int sdbmhash(const char *s, unsigned size);

/* ------------- bloom types and funcs --------------- */
const unsigned char masks[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

typedef unsigned (*hash_func_ptr)(const char *buffer, unsigned size);
struct __bloom_filter
{
    unsigned n;
    unsigned size;
    unsigned char *bits;
    hash_func_ptr hash;
};
typedef struct __bloom_filter* bloom_filter;

bloom_filter bloom_init (unsigned n, hash_func_ptr hash);
int bloom_insert(bloom_filter b, void *data, unsigned size);
int bloom_check(bloom_filter b, void *data, unsigned size);
void bloom_destroy(bloom_filter b);
/* ------------- end of bloom types and funcs --------------- */

int main()
{
    const int size = 655371;
    bloom_filter b1 = bloom_init(size, sdbmhash);
    for (int i = 0; i < size / 2; i += 2)
    {
        if (!bloom_insert(b1, &i, sizeof(i)))
        {
            fprintf(stderr, "err insert %d\n", i);
            exit(1);
        }
    }
    printf("insert ok\n");

    int cnt = 0;
    for (int i = 0; i < size / 2; i++)
    {
        if (bloom_check(b1, &i, sizeof(i)))
        {
            if (i & 1)
            {
                //printf("i = %d should not be checked, tolerable.\n", i);
                cnt++;
            }
        }
        else
        {
            if (!(i & 1))
            {
                printf("i = %d should be checked! BUG!\n", i);
            }
        }
    }
    printf("cnt = %d\n", cnt);
    return 0;
}

bloom_filter bloom_init (unsigned n, hash_func_ptr hash)
{
    bloom_filter b = (bloom_filter)malloc(sizeof(__bloom_filter));
    if (b == NULL)
    {
        fprintf(stderr, "bloom_init: err malloc bloom_filter\n");
        return NULL;
    }

    b->n    = n;
    b->size = (n + 7) / 8;
    b->hash = hash;

    b->bits = (unsigned char *)malloc(b->size);
    memset(b->bits, 0, b->size);
    if (b->bits == NULL)
    {
        fprintf(stderr, "bloom_init: err malloc bits\n");
        return NULL;
    }
    return b;
}

int bloom_insert(bloom_filter b, void *data, unsigned size)
{
    unsigned h = b->hash((const char *)data, size) % (b->n);
    unsigned idx = h / 8;
    if (idx >= b->size)
    {
        fprintf(stderr, "bloom_insert: hash value overflow\n");
        return 0;
    }
    b->bits[idx] |= masks[h % 8];
    //printf("h = %2d, idx = %2d, bit = %2d\n", h, idx, h % 8);
    return 1;
}

int bloom_check(bloom_filter b, void *data, unsigned size)
{
    unsigned h = b->hash((const char *)data, size) % (b->n);
    unsigned idx = h / 8;
    if (idx >= b->size)
    {
        fprintf(stderr, "bloom_insert: hash value overflow\n");
        exit(1);
    }
    return !!(b->bits[idx] & masks[h % 8]);
}

void bloom_destroy(bloom_filter b)
{
    if (b != NULL)
    {
        if (b->bits != NULL)
            free(b->bits);
        free(b);
    }
}

//-----------------------------------------------

unsigned int jshash(const char *s, unsigned size)
{
    int hash = 1315423911;
    unsigned len = 0;
    while (len < size)
    {
        hash ^= (hash << 5) + s[len] + (hash >> 2);
        len++;
    }
    return (hash & 0x7fffffffl);
}

unsigned int sdbmhash(const char *s, unsigned size)
{
    int hash = 0;
    unsigned len = 0;
    while (len < size)
    {
        hash = (hash << 6) + (hash << 16) - hash + s[len];
        len++;
    }
    return (hash & 0x7fffffffl);
}
May 14
epoll相关代码出自“man epoll”。发现在网上找相关代码还有点麻烦。

p.s. 推荐 apt-get source libevent 找找里面的 epoll.c 文件里的相关实现。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#define SERVPORT 9527 /*服务器监听端口号 */
#define BACKLOG 10 /* 最大同时连接请求数 */

void setnonblocking(int sock)
{
    int opts;
    opts = fcntl(sock,F_GETFL);
    if(opts<0)
    {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }
    opts = opts|O_NONBLOCK;
    if(fcntl(sock,F_SETFL,opts)<0)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    } 
}

void do_use_fd(int client_fd)
{
    const char str[] = "God bless you!\n";
    if (send(client_fd,  str,  sizeof(str),  0) == -1)
        perror("send");
    close(client_fd);
}

int main()
{
    int sockfd;
    struct sockaddr_in my_addr;
    struct sockaddr_in remote_addr;
    if ((sockfd = socket(AF_INET,  SOCK_STREAM,  0)) == -1) {
        perror("socket");
        exit(1);
    }
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(SERVPORT);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(my_addr.sin_zero), 8);
    if (bind(sockfd,  (struct sockaddr *)&my_addr,  sizeof(struct sockaddr)) == -1) {
        perror("bind");
        exit(1);
    }
    printf("bind ok\n");
    if (listen(sockfd,  BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }
    printf("listen ok\b");
    size_t sin_size = sizeof(struct sockaddr_in);

#define MAX_EVENTS 10
    struct epoll_event ev, events[MAX_EVENTS];
    int conn_sock, nfds, epollfd;

    epollfd = epoll_create(10);
    if (epollfd == -1) {
        perror("epoll_create");
        exit(EXIT_FAILURE);
    }
    printf("epoll_create\n");

    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
        perror("epoll_ctl: sockfd");
        exit(EXIT_FAILURE);
    }
    printf("epoll_ctl ok\n");

    for (;;) {
        printf("start epoll_wait\n");
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            perror("epoll_pwait");
            exit(EXIT_FAILURE);
        }
        printf("epoll_wait returns, nfds = %d\n", nfds);

        for (int n = 0; n < nfds; ++n) {
            if (events[n].data.fd == sockfd) {
                conn_sock = accept(sockfd,
                        (struct sockaddr *) &remote_addr, &sin_size);
                if (conn_sock == -1) {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
                setnonblocking(conn_sock);
                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = conn_sock;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                            &ev) == -1) {
                    perror("epoll_ctl: conn_sock");
                    exit(EXIT_FAILURE);
                }
            } else {
                do_use_fd(events[n].data.fd);
            }
        }
    }
}


这里有另一个很详细的说明: https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/
May 10

zz 大数据量问题-面试 不指定

felix021 @ 2010-5-10 23:15 [IT » 程序设计] 评论(3) , 引用(0) , 阅读(6797) | Via 本站原创
boluor推荐给我的。难得有这么完整地总结大规模数据处理题目的文章,一定要收藏下来。

zz from http://www.douban.com/note/52434859/

1.Bloom filter

适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集

基本原理及要点:
对于原理来说很简单,位数组+k个独立hash函数。将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。

还有一个比较重要的问题,如何根据输入元素个数n,确定位数组m的大小及hash函数个数。当hash函数个数k=(ln2)*(m/n)时错误率最小。在错误率不大于E的情况下,m至少要等于n*lg(1/E)才能表示任意n个元素的集合。但m还应该更大些,因为还要保证bit数组里至少一半为 0,则m应该>=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2为底的对数)。

举个例子我们假设错误率为0.01,则此时m应大概是n的13倍。这样k大概是8个。

注意这里m与n的单位不同,m是bit为单位,而n则是以元素个数为单位(准确的说是不同元素的个数)。通常单个元素的长度都是有很多bit的。所以使用bloom filter内存上通常都是节省的。

扩展:
Bloom filter将集合中的元素映射到位数组中,用k(k为哈希函数个数)个映射位是否全1表示元素在不在这个集合中。Counting bloom filter(CBF)将位数组中的每一位扩展为一个counter,从而支持了元素的删除操作。Spectral Bloom Filter(SBF)将其与集合元素的出现次数关联。SBF采用counter中的最小值来近似表示元素的出现频率。

问题实例:给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?

根据这个问题我们来计算下内存的占用,4G=2^32大概是40亿*8大概是340亿,n=50亿,如果按出错率0.01算需要的大概是650亿个bit。现在可用的是340亿,相差并不多,这样可能会使出错率上升些。另外如果这些urlip是一一对应的,就可以转换成ip,则大大简单了。

2.Hashing

适用范围:快速查找,删除的基本数据结构,通常需要总数据量可以放入内存

基本原理及要点:
hash函数选择,针对字符串,整数,排列,具体相应的hash方法。
碰撞处理,一种是open hashing,也称为拉链法;另一种就是closed hashing,也称开地址法,opened addressing。

扩展:
d-left hashing中的d是多个的意思,我们先简化这个问题,看一看2-left hashing。2-left hashing指的是将一个哈希表分成长度相等的两半,分别叫做T1和T2,给T1和T2分别配备一个哈希函数,h1和h2。在存储一个新的key时,同时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]。这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,哪一个位置已经存储的(有碰撞的)key比较多,然后将新key存储在负载少的位置。如果两边一样多,比如两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1子表中,2-left也由此而来。在查找一个key时,必须进行两次hash,同时查找两个位置。

问题实例:
1).海量日志数据,提取出某日访问百度次数最多的那个IP。

IP的数目还是有限的,最多2^32个,所以可以考虑使用hash将ip直接存入内存,然后进行统计。

3.bit-map

适用范围:可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下

基本原理及要点:使用bit数组来表示某些元素是否存在,比如8位电话号码

扩展:bloom filter可以看做是对bit-map的扩展

问题实例:

1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。

8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。

2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上。或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map。

4.堆

适用范围:海量数据前n大,并且n比较小,堆可以放入内存

基本原理及要点:最大堆求前n小,最小堆求前n大。方法,比如求前n小,我们比较当前元素与最大堆里的最大元素,如果它小于最大元素,则应该替换那个最大元素。这样最后得到的n个元素就是最小的n个。适合大数据量,求前n小,n的大小比较小的情况,这样可以扫描一遍即可得到所有的前n元素,效率很高。

扩展:双堆,一个最大堆与一个最小堆结合,可以用来维护中位数。

问题实例:
1)100w个数中找最大的前100个数。

用一个100个元素大小的最小堆即可。

5.双层桶划分

适用范围:第k大,中位数,不重复或重复的数字

基本原理及要点:因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行。可以通过多次缩小,双层只是一个例子。

扩展:

问题实例:
1).2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

有点像鸽巢原理,整数个数为2^32,也就是,我们可以将这2^32个数,划分为2^8个区域(比如用单个文件代表一个区域),然后将数据分离到不同的区域,然后不同的区域在利用bitmap就可以直接解决了。也就是说只要有足够的磁盘空间,就可以很方便的解决。

2).5亿个int找它们的中位数。

这个例子比上面那个更明显。首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数。然后第二次扫描我们只统计落在这个区域中的那些数就可以了。

实际上,如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度。即可以先将int64分成2^24个区域,然后确定区域的第几大数,在将该区域分成2^20个子区域,然后确定是子区域的第几大数,然后子区域里的数的个数只有2^20,就可以直接利用direct addr table进行统计了。

6.数据库索引

适用范围:大数据量的增删改查

基本原理及要点:利用数据的设计实现方法,对海量数据的增删改查进行处理。
扩展:
问题实例:


7.倒排索引(Inverted index)

适用范围:搜索引擎,关键字查询

基本原理及要点:为何叫倒排索引?一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。

以英文为例,下面是要被索引的文本:
T0 = "it is what it is"
T1 = "what is it"
T2 = "it is a banana"
我们就能得到下面的反向文件索引:
"a":      {2}
"banana": {2}
"is":     {0, 1, 2}
"it":     {0, 1, 2}
"what":   {0, 1}
检索的条件"what", "is" 和 "it" 将对应集合的交集。

正向索引开发出来用来存储每个文档的单词的列表。正向索引的查询往往满足每个文档有序频繁的全文查询和每个单词在校验文档中的验证这样的查询。在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。也就是说文档指向了它包含的那些单词,而反向索引则是单词指向了包含它的文档,很容易看到这个反向的关系。

扩展:

问题实例:文档检索系统,查询那些文件包含了某单词,比如常见的学术论文的关键字搜索。

8.外排序

适用范围:大数据的排序,去重

基本原理及要点:外排序的归并方法,置换选择 败者树原理,最优归并树

扩展:

问题实例:
1).有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M。返回频数最高的100个词。

这个数据具有很明显的特点,词的大小为16个字节,但是内存只有1m做hash有些不够,所以可以用来排序。内存可以当输入缓冲区使用。

9.trie树

适用范围:数据量大,重复多,但是数据种类小可以放入内存

基本原理及要点:实现方式,节点孩子的表示方式

扩展:压缩实现。

问题实例:
1).有10个文件,每个文件1G,每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。要你按照query的频度排序 。

2).1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串。请问怎么设计和实现?

3).寻找热门查询:查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个,每个不超过255字节。

10.分布式处理 mapreduce

适用范围:数据量大,但是数据种类小可以放入内存

基本原理及要点:将数据交给不同的机器去处理,数据划分,结果归约。

扩展:

问题实例:

1).The canonical example application of MapReduce is a process to count the appearances of

each different word in a set of documents:
void map(String name, String document):
  // name: document name
  // document: document contents
  for each word w in document:
    EmitIntermediate(w, 1);

void reduce(String word, Iterator partialCounts):
  // key: a word
  // values: a list of aggregated partial counts
  int result = 0;
  for each v in partialCounts:
    result += ParseInt(v);
  Emit(result);
Here, each document is split in words, and each word is counted initially with a "1" value by

the Map function, using the word as the result key. The framework puts together all the pairs

with the same key and feeds them to the same call to Reduce, thus this function just needs to

sum all of its input values to find the total appearances of that word.

2).海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。

3).一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到N^2个数的中数(median)?


经典问题分析

上千万or亿数据(有重复),统计其中出现次数最多的前N个数据,分两种情况:可一次读入内存,不可一次读入。

可用思路:trie树+堆,数据库索引,划分子集分别统计,hash,分布式计算,近似统计,外排序

所谓的是否能一次读入内存,实际上应该指去除重复后的数据量。如果去重后数据可以放入内存,我们可以为数据建立字典,比如通过 map,hashmap,trie,然后直接进行统计即可。当然在更新每条数据的出现次数的时候,我们可以利用一个堆来维护出现次数最多的前N个数据,当然这样导致维护次数增加,不如完全统计后在求前N大效率高。

如果数据无法放入内存。一方面我们可以考虑上面的字典方法能否被改进以适应这种情形,可以做的改变就是将字典存放到硬盘上,而不是内存,这可以参考数据库的存储方法。

当然还有更好的方法,就是可以采用分布式计算,基本上就是map-reduce过程,首先可以根据数据值或者把数据hash(md5)后的值,将数据按照范围划分到不同的机子,最好可以让数据划分后可以一次读入内存,这样不同的机子负责处理各种的数值范围,实际上就是map。得到结果后,各个机子只需拿出各自的出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是reduce过程。

实际上可能想直接将数据均分到不同的机子上进行处理,这样是无法得到正确的解的。因为一个数据可能被均分到不同的机子上,而另一个则可能完全聚集到一个机子上,同时还可能存在具有相同数目的数据。比如我们要找出现次数最多的前100个,我们将1000万的数据分布到10台机器上,找到每台出现次数最多的前 100个,归并之后这样不能保证找到真正的第100个,因为比如出现次数最多的第100个可能有1万个,但是它被分到了10台机子,这样在每台上只有1千个,假设这些机子排名在1000个之前的那些都是单独分布在一台机子上的,比如有1001个,这样本来具有1万个的这个就会被淘汰,即使我们让每台机子选出出现次数最多的1000个再归并,仍然会出错,因为可能存在大量个数为1001个的发生聚集。因此不能将数据随便均分到不同机子上,而是要根据hash 后的值将它们映射到不同的机子上处理,让不同的机器处理一个数值范围。

而外排序的方法会消耗大量的IO,效率不会很高。而上面的分布式方法,也可以用于单机版本,也就是将总的数据根据值的范围,划分成多个不同的子文件,然后逐个处理。处理完毕之后再对这些单词的及其出现频率进行一个归并。实际上就可以利用一个外排序的归并过程。

另外还可以考虑近似计算,也就是我们可以通过结合自然语言属性,只将那些真正实际中出现最多的那些词作为一个字典,使得这个规模可以放入内存。
分页: 35/103 第一页 上页 30 31 32 33 34 35 36 37 38 39 下页 最后页 [ 显示模式: 摘要 | 列表 ]