不指定 类别: 电脑 » 程序设计 | felix021 @ 2012-1-12 21:58 | 评论(0) | 阅读(154)
  对于没有GC的语言来说,这实在是最让人头疼的事情了,毕竟内存泄漏是最难处理的问题(之一?),对于一个后台server,即使只是一个小小的泄漏,日积月累,也会导致灾难性的后果。有个传闻说的是,某公司的某下载软件的某后台server,由于有个无法定位的内存泄漏问题,导致服务的内存占用不断增加,以至于只能每隔一段时间重启之。

  有人说,C/C++程序员有一半的工作量是花在处理内存泄漏上面,但是很遗憾,内存泄漏仍然屡见不鲜。一旦出现泄漏,能做的事情不多,上述处理方式是消极做法之一,有效,但治标不治本。积极一点的,也不外乎这两个:一是看代码,反复看代码,请别人看代码,请别人反复看代码;或是借助valgrind之类的工具来跟踪内存的分配/释放(而且并不适用于所有程序,例如某些程序寄希望于在其终止时让OS来释放那些只需申请一次且无需释放的内存)。一些额外的测试工作也许能帮助缩小查看代码的范围,但也只能这样了。

  既是如此,在程序运行之前,就应该先把好关。

  对于C语言,这确实是个比较痛苦的事情。毕竟C语言只是汇编的高级语言封装,语言本身提供的能力很有限。

  假定有一个函数申请了多次内存,那么每次遇到错误需要退出的时候,为了避免内存泄漏,必须将其之前申请的所有内存都释放。所以你也许会看到或者写出过这样蛋疼的程序:
void func(){
    void *a = malloc(sizeof(A));
    if (NULL == a) {
        return;
    }

    void *b = malloc(sizeof(B));
    if (NULL == b) {
        free(a);
        return;
    }

    void *c = malloc(sizeof(C));
    if (NULL == c) {
        free(a);
        free(b);
        return;
    }
    ......
}
  有效,但是不靠谱。当这个函数长达数百行、有多处申请内存的时候,其可维护性是相当低的。当然,使用 alloca 这个非标准的内存分配函数可以在某些情况下解决问题,但是如果申请的内存较大(栈空间不够)、或者分配到的内存被用于较复杂的结构(比如还包含其他资源的指针)、资源不是内存(比如文件指针、锁等同样需要在生命周期结束被释放的资源),alloca就无能为力了。

  于是万恶的goto出场了。为了解决上面的问题,引入goto可以使得每个资源只需要写一份对应的释放代码,例如:
void func(){
    void *a = malloc(sizeof(A));
    if (NULL == a) goto wtf;

    void *b = malloc(sizeof(B));
    if (NULL == b) goto wtf;

    void *c = malloc(sizeof(C));
    if (NULL == c) goto wtf;

    ......

wtf:
    if (a != NULL) free(a);
    if (b != NULL) free(b);
    if (c != NULL) free(c);
}
  看起来很棒对不对?但是实际上并不能通过编译,gcc会提示类似这样的错误:
引用
cross.c:14: error: jump to label ‘wtf’
cross.c:9: error:  from here
cross.c:11: error:  crosses initialization of ‘void* c’
  什么意思呢?假定在第一步,给 a 分配内存的时候失败了,那么还没来得及去定义 b 并给其初始化赋值,就跳转到了wtf这儿,而在wtf下面的第二行,却引用了 b 这个变量,对于编译器而言,这便无法处理了。正确的代码应该是:
void func(){
    void *a = NULL, *b = NULL, *c = NULL;

    a = malloc(sizeof(A));
    if (NULL == a) goto wtf;

    b = malloc(sizeof(B));
    if (NULL == b) goto wtf;

    c = malloc(sizeof(C));
    if (NULL == c) goto wtf;

    ......

wtf:
    if (a != NULL) free(a);
    if (b != NULL) free(b);
    if (c != NULL) free(c);
}
  这样一来便要求所有在 goto 之后被用到的变量都必须在第一个goto之前定义,并赋初值。这就类似c89/pascal的做法了,强制要求所有变量在函数的开头定义,失去了变量就近定义的便捷性和一些其他的好处(sandy的说法是“局部性”,但是窃以为变量的就近定义跟局部性关系不大,更多的是在C++中,对象的就近定义可以在一些情况下避免不必要的初始化,并且可能需要之前的一些处理结果)。这儿有个更复杂的例子,作者指出,在驱动/linux内核中大量使用了这种方式来释放资源。注意,稍有不同的是,这个例子有多种资源,函数末尾有多个label,按照资源申请顺序的倒序释放资源(为什么呢,看代码吧~)

  对变量就近定义的好坏见仁见智了,但是goto毕竟不是个好东西,所以看过内核代码的同学可能会发现另外一种替代性的结构:do-while(0) 。乍一看这个结构似乎没有意义,有点奇怪,但是却很好用,很适合用来消除goto语句,例如上面的代码可以这么做:
void func(){
    void *a = NULL, *b = NULL, *c = NULL;

    do {
        a = malloc(sizeof(A));
        if (NULL == a) break;

        b = malloc(sizeof(B));
        if (NULL == b) break;

        c = malloc(sizeof(C));
        if (NULL == c) break;

        ......

    } while (0);

wtf:
    if (a != NULL) free(a);
    if (b != NULL) free(b);
    if (c != NULL) free(c);
}
  既消除了“不法分子”,也达到了避免冗余的目的。对于这个结构,其实还有更多的好处,详见这里

  但是do-while(0)和goto一样,不是万金油,对于很多较复杂的情况也不能很好的解决,甚至会使得程序更加晦涩难懂。对于do-while(0),如果在这个结构内还有一个循环,循环里面出错想要跳出do-while(0),break就不奏效了(至于为什么,你懂的),这时代码怎么写都恶心,只能羡慕Java里面的break label语法了;而对于比较复杂的资源,比如上文中申请到的内存是 a->b->c 这样嵌套的,那么如何安排内存释放代码,又要让人头疼了。合理的使用goto/do-while(0),将过长的代码拆分成多个函数等都可以起到一定的帮助。

  只是很可惜,C语言的能力大概就只能走到这里了。想走得更远,就得借助C++来完成了。虽然C++没有gc,但是由于其OO的特性,使得RAII的实现变得可能。

  所谓RAII,即 Resource Acquizition Is  Initialization。很晦涩吧?其实具体实现很简单:把资源封装成一个类,在其构造函数中分配,在析构函数中释放。当需要使用的时候,在栈上初始化一个对象,当这个对象生命周期结束的时候,其析构函数会被调用,自动完成资源的释放。对于前面提到的例子,可以把A/B/C封装成一个class,对应的a/b/c就是实例化得到的三个对象,当func函数结束的时候,abc对应的内存就会被释放。同样的方法也适用于锁、互斥量、文件指针等其他类型的资源。下面这段代码以pthread_mutex为例,演示了RAII的使用:
class Mutexer {
    private:
        pthread_mutex_t *mutex;
    public:
        Mutexer(pthread_mutex_t *m) { mutex = m; }
        ~Mutexer() { Unlock(); }
        Lock() { pthread_mutex_lock(mutex); }
        Unlock() { pthread_mutex_unlock(mutex); }
};

//Global mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void func() {
    Mutexer mtx(&mutex);
    mtx::lock();
    if (sth. failed) {
        return;
    }
}

  不过程序中因为各种原因常需要使用 new 来分配资源(内存、对象等),这样对应的指针还是得在其生命周期结束的时候被释放,总不能为每一个指针再封装一个struct吧。幸而C++的泛型在这里又为RAII提供了绝佳的方法。实际上在第一版STL里面就包含了一个 auto_ptr 容器,实例化一个auto_ptr的时候可以赋予一个任意类型指针ptr(但是必须是使用new获得的,特别注意:new[]分配的不行),auto_ptr对象将ptr包装起来,并重载了 * 和 -> 两个操作符,使得该对象能像指针一样被使用,并且在该对象被生命周期的时候,其析构函数会delete ptr。 下面是auto_ptr的一个简单实现和使用:
template <typename T>
class x_ptr
{
    private:
        T* x;
    public:
        typedef T ele_type;
        explicit x_ptr(T* _x): x(_x) {}
        ~x_ptr() { delete x; }

        T & operator * () { return *x; }
        T * operator ->() const { return x; }
};

void func(){
    x_ptr<int> p(new int);
    *p = 3;
}
  代码的最后无需显式调用 delete 需释放分配的那个int,却照样避免了内存的泄漏。

  既然说到auto_ptr,为什么不用它来写例子呢?因为auto_ptr的某些特性导致其有大坑,在很多地方不受待见,以至于在 c++11 标准里,auto_ptr被废弃了,因此不建议在项目中使用它了。有兴趣的同学可以去翻看《C++标准程序库》对auto_ptr的介绍。

  本来计划写到这里要告一段落了,但是上面的 x_ptr 有坑,无奈只好继续……为什么说有坑呢?举两个例子:
void func1() {
    x_ptr<int> p(new int), q(new int);
    *p = 1;
    *q = 2;
    p = q;
}

void func2() {
    x_ptr<int> p(new int[10]);
}
  在 func1 中,由于进行了拷贝(其实拷贝构造也一样),导致 p 对应的那块空间会被泄漏,而 q 对应的那块空间会被释放2次;在 func2 中,x_ptr试图用delete去释放由new[]分配的内存空间,其结果是未定义的(比如不是基本元素而是某个class,程序可能会直接崩溃)。

  针对func1的问题,可以通过私有化其拷贝函数、拷贝构造函数来禁止x_ptr的拷贝,代码如下
template <typename T>
class x_ptr
{
    private:
        T* x;
        x_ptr(const x_ptr&);
        x_ptr& operator= (const x_ptr& v);
    public:
        typedef T ele_type;
        explicit x_ptr(T* _x): x(_x) {}
        ~x_ptr() { delete x; };

        T & operator * () { return *x; }
        T * operator ->() const { return x; }
};

  而针对func2的问题,解决方法呢,要么是写一个x_ptr_arr,使用delete[]来处理;要么是在x_ptr的构造函数里加一个flag,用来指定是否是new[]分配的,当然,为了方便,可以设置一个默认值false.....

  补充一句,这里的x_ptr其实是boost::scoped_ptr的缩水版了,有兴趣的同学可以自行Google了解更多,关于内存泄漏的话题,这篇大概就说这么多了吧。

  最后,感谢Sandy同学的 C++中利用RAII在stack上管理资源I ,本篇有多处参考该文。希望他能抽出时间把 II 给写完吧 :P
新浪微博分享微博分享 | 人人分享 | 电脑 » 程序设计 | 引用(0) |
不指定 类别: 杂碎 | felix021 @ 2012-1-3 18:27 | 评论(0) | 阅读(171)
项目如果被需求拖延 只要给我一台计算机
把键盘当风琴 如果你能够开心
抄起手柄 带你升级
谁教给我这一套 无效的算法
神也不能阻挡 你想离开的心

为什么完成需求 是那么容易
为什么束手无策啊 我和你的爱情
为什么我能写C 也会用Ruby
为什么我却没办法 长驱直入 你的心

曾经你赞美我手指 打DOTA多给力 日日夜夜贴身保护你
最凶狠的怪兽 也不能与我为敌
那为何害怕 你的泪滴
我给了我这一幕难堪的结局
谁要这样码农 连自己也救不起

为什么完成需求 是那么容易
为什么束手无策啊 我和你的爱情
为什么我能写C 也会用Ruby
为什么我却没办法 长驱直入 你的心

喔...  喔...
喔...  喔...

为什么完成需求 是那么容易
为什么束手无策啊 我和你的爱情
为什么做完项目 终于拿到奖金
为什么我只能够 眼看着爱燃烧成 灰烬

项目如果被需求拖延 谁来接手我的烂摊子
新浪微博分享微博分享 | 人人分享 | 杂碎 | 引用(0) |
不指定 类别: 电脑 » 程序设计 | felix021 @ 2011-12-23 14:38 | 评论(0) | 阅读(320)
这程序写了好几次了,干脆贴出来吧~附上exe。
#include <stdio.h>
#include <stdlib.h>

char str[65536];

int main() {
    int i;
    for (i = 0; i < 65536; i++) str[i] = '1';
    str[65535] = '\0';
    i = 0;
    while (1) {
        i++;
        if (i % 3000 == 0) {
            sleep(1);
        }
        puts(str);
    }
    return 0;
}
下载文件 (已下载 48 次)

新浪微博分享微博分享 | 人人分享 | 电脑 » 程序设计 | 引用(0) |
不指定 类别: 电脑 » 数据库 | felix021 @ 2011-12-13 14:27 | 评论(0) | 阅读(458)
一个好的解决方法是使用mysql_ping。在使用mysql_real_connect连接数据库之后,再使用mysql_options( &mysql, MYSQL_OPT_RECONNECT, … ) 来设置为自动重连。这样当mysql连接丢失的时候,使用mysql_ping能够自动重连数据库。如果是在mysql 5.1.6之前,那么则应在每次执行完real_connect 之后执行mysql_options( &mysql, MYSQL_OPT_RECONNECT, … ) ,如果是mysql 5.1.6+,则在connect之前执行一次就够了。

示例代码:
mysql_init() ...
mysql_real_connect()...
char value = 1;
mysql_options(&mysql, MYSQL_OPT_RECONNECT, (char *)&value);

新浪微博分享微博分享 | 人人分享 | 电脑 » 数据库 | 引用(0) |
不指定 类别: 电脑 » 操作系统 | felix021 @ 2011-11-23 23:08 | 评论(0) | 阅读(735)
按照某帖子里的说法,禁用掉WMPNetworkSvc(Windows Media Player Netwroking SharingService)即可正常使用sysprep部署了。

好久没发这么短的Blog了,简直就是微博了,OVER。
新浪微博分享微博分享 | 人人分享 | 电脑 » 操作系统 | 引用(0) |
不指定 类别: 电脑 » 硬件 | felix021 @ 2011-11-22 23:18 | 评论(0) | 阅读(707)
贱板 GA-880GM-USB3 rev3.1 很不幸买的时候没有注意,只有SATA II。怀揣Hitachi 7k1000,垂涎M4性能良久,无奈囊中羞涩。

最近看到×讯的Corsair Vengeance 4G*2特价299,于是入了一对,恰逢×迅搞活动,拿到了1000-100的优惠券,M4 64G=725-100,价格就还不错,于是于是一狠心,跟别人凑单买了这个M4 64G。

因为是在公司收到货的,所以提前带了个USB2.0的移动硬盘盒,装上测试了一下,惨不忍睹,读取30写入20,连我的S102 16GB都不如(35/25),但是读写延时还是很给力了,分别是0.1/0.2ms。大概扫了一下确认全盘读取没有问题,于是等回家再测。

亏得先前买了个2.5=>3.5的架子(把移动硬盘卸下来装上去了),于是到家以后马上把M4架上去折磨。AS SSD Benchmark一测,不对,分数才165,坑爹啊!后发现原来不知道啥时候把BIOS里的SATA改成了Native IDE(但分区是4K对齐的)。于是sysprep了一把(否则重启会蓝屏),然后把BIOS改回AHCI,正常了,418分,0002固件,中规中矩,貌似大家64G@SATA2都是这个分数吧。

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

新浪微博分享微博分享 | 人人分享 | 电脑 » 硬件 | 引用(0) |
不指定 类别: 杂碎 | felix021 @ 2011-11-21 20:06 | 评论(0) | 阅读(592)
上次是支付宝,这次是淘宝,贴两次投诉记录。

==== 2011-04-28 ====
云客服X793: 你好,有什么可以帮您的吗?
felix021: 能不能不要往我的手机里发广告
云客服X793: 您好,请您详细说一下您的问题好吗?
felix021: 收到广告短信,要我上021.tmall.com 烦
云客服X793: 这您可以再淘宝上设置的
felix021: 怎么设置 帐号管理,删除手机号?
云客服X793: 登陆淘宝——进入账号管理 订阅管理
felix021: “您还没有订阅任何内容!”
云客服X793: 或者网站提醒
felix021: 网站提醒里 跟手机有关的全都没勾
felix021: 请问, 我还可以怎么办?
云客服X793: 拿手机短信设置呢? 也是在网站提醒那里的
felix021: 噢 网站提醒里的那个吧,没订阅 点开后提示 “输入手机号码,申请开通” 如果我把绑定的手机号码删掉,是不是就不会收到你们的垃圾短信了
云客服X793: 那您确认是淘宝发的垃圾短信?
felix021: 不然谁让我上021.tmall.com啊 难道是拍拍? 发短信的号码是 10659020511107101
云客服X793: 这样啊,您可以取消关于聚划算的关注
felix021: 那又是个什么东西,我啥时候关注过了
云客服X793: 这是淘宝团购的项目
felix021: 那我还是第一次听说淘宝开团购了 没订阅
云客服X793: 额~那这应该就是淘宝的推广了
felix021: 麻烦您给处理一下吧
云客服X793: 您以后应该不会再收到了
felix021: 非常感谢。
云客服X793: 恩,我们会处理的 我会向上反应的

==== 2011-11-21 ====
云客服X582: 您好,有什么可以帮助您的吗?
felix021: 你好 你们能不能不要再给我发垃圾短信了 我投诉好几次了还在发还在发
云客服X582: 您好,请您详细描述一下您的问题好吗?
felix021: 收到垃圾短信 投诉了还在发 不够详细吗 我在淘宝设置里面已经特意把绑定的手机号给解绑了 你们还在发,你们还能不能更无耻一点?
云客服X582: 您是指什么 信息呢
felix021: 我不管是什么信息,淘宝的短信我一条都不想收到。 我知道你们后台有屏蔽发短信的东西,不要罗嗦
云客服X582: 亲,不好意思,这个我们这里核实不了哦,建议您联系一下热线
felix021: 一个淘宝连800 400都没有 你们还是叫冷线比较好
云客服X582: 亲,或者您可以先说明一下是一些什么 信息么,我这里需要先核实一下是不是淘宝网发的哦
felix021: 10659057600126107 感恩节来临,打开淘宝安卓客户端,……
felix021: 嗯 说不定是拍拍发的我冤枉你们了。上次021.tmall.com的垃圾短信说不定也是拍拍发了 再上次的我就不记得了,反正我已经投诉2次了 都没能解决,你说我是不是去找拍拍的客服更靠谱?
云客服X582 (2011-11-21 20:00:25)  亲,稍等
系统提示 (2011-11-21 20:04:29) 由于您超时没有提问,系统已退出人工客服。

新浪微博分享微博分享 | 人人分享 | 杂碎 | 引用(0) |
不指定 类别: 电脑 » 网络 | felix021 @ 2011-11-20 01:35 | 评论(0) | 阅读(778)
  上次说了,OpenVZ解决方案的VPS内核没有ppp模块, 不能搞PPTP;不过还好,很多服务商都提供tun(有些需要找客服发ticket,有些在管理后台就可以开,比如BurstNet的就在管理后台开),可以搞OpenVPN。据说OpenVPN效率比PPTP要高,不过PPTP的好处是各种OS直接集成(Windows7,iOS,Android……),而OpenVPN就相对折腾一点。

  闲话不说了,进入正题。

==== 首先坑的总结,下面特别注意下 ====

1. iptables规则执行完要输入完然后check一下是否规则已经添加
2. 21端口不靠谱
3. tcp协议不靠谱
4. 配置Server的时候不要一路按回车!有些是问[y/n]需要输入 y 的!

新浪微博分享微博分享 | 人人分享 | 电脑 » 网络 | 引用(0) |
分页: 1/82 第一页 1 2 3 4 5 6 7 8 9 10 下页 最后页 [ 显示模式: 摘要 | 列表 ]