Jan
12
对于没有GC的语言来说,这实在是最让人头疼的事情了,毕竟内存泄漏是最难处理的问题(之一?),对于一个后台server,即使只是一个小小的泄漏,日积月累,也会导致灾难性的后果。有个传闻说的是,某公司的某下载软件的某后台server,由于有个无法定位的内存泄漏问题,导致服务的内存占用不断增加,以至于只能每隔一段时间重启之。
有人说,C/C++程序员有一半的工作量是花在处理内存泄漏上面,但是很遗憾,内存泄漏仍然屡见不鲜。一旦出现泄漏,能做的事情不多,上述处理方式是消极做法之一,有效,但治标不治本。积极一点的,也不外乎这两个:一是看代码,反复看代码,请别人看代码,请别人反复看代码;或是借助valgrind之类的工具来跟踪内存的分配/释放(而且并不适用于所有程序,例如某些程序寄希望于在其终止时让OS来释放那些只需申请一次且无需释放的内存)。一些额外的测试工作也许能帮助缩小查看代码的范围,但也只能这样了。
既是如此,在程序运行之前,就应该先把好关。
对于C语言,这确实是个比较痛苦的事情。毕竟C语言只是汇编的高级语言封装,语言本身提供的能力很有限。
假定有一个函数申请了多次内存,那么每次遇到错误需要退出的时候,为了避免内存泄漏,必须将其之前申请的所有内存都释放。所以你也许会看到或者写出过这样蛋疼的程序:
于是万恶的goto出场了。为了解决上面的问题,引入goto可以使得每个资源只需要写一份对应的释放代码,例如: 什么意思呢?假定在第一步,给 a 分配内存的时候失败了,那么还没来得及去定义 b 并给其初始化赋值,就跳转到了wtf这儿,而在wtf下面的第二行,却引用了 b 这个变量,对于编译器而言,这便无法处理了。正确的代码应该是:
对变量就近定义的好坏见仁见智了,但是goto毕竟不是个好东西,所以看过内核代码的同学可能会发现另外一种替代性的结构:do-while(0) 。乍一看这个结构似乎没有意义,有点奇怪,但是却很好用,很适合用来消除goto语句,例如上面的代码可以这么做:
但是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的使用:
不过程序中因为各种原因常需要使用 new 来分配资源(内存、对象等),这样对应的指针还是得在其生命周期结束的时候被释放,总不能为每一个指针再封装一个struct吧。幸而C++的泛型在这里又为RAII提供了绝佳的方法。实际上在第一版STL里面就包含了一个 auto_ptr 容器,实例化一个auto_ptr的时候可以赋予一个任意类型指针ptr(但是必须是使用new获得的,特别注意:new[]分配的不行),auto_ptr对象将ptr包装起来,并重载了 * 和 -> 两个操作符,使得该对象能像指针一样被使用,并且在该对象被生命周期的时候,其析构函数会delete ptr。 下面是auto_ptr的一个简单实现和使用:
既然说到auto_ptr,为什么不用它来写例子呢?因为auto_ptr的某些特性导致其有大坑,在很多地方不受待见,以至于在 c++11 标准里,auto_ptr被废弃了,因此不建议在项目中使用它了。有兴趣的同学可以去翻看《C++标准程序库》对auto_ptr的介绍。
本来计划写到这里要告一段落了,但是上面的 x_ptr 有坑,无奈只好继续……为什么说有坑呢?举两个例子:
针对func1的问题,可以通过私有化其拷贝函数、拷贝构造函数来禁止x_ptr的拷贝,代码如下
而针对func2的问题,解决方法呢,要么是写一个x_ptr_arr,使用delete[]来处理;要么是在x_ptr的构造函数里加一个flag,用来指定是否是new[]分配的,当然,为了方便,可以设置一个默认值false.....
补充一句,这里的x_ptr其实是boost::scoped_ptr的缩水版了,有兴趣的同学可以自行Google了解更多,关于内存泄漏的话题,这篇大概就说这么多了吧。
最后,感谢Sandy同学的 C++中利用RAII在stack上管理资源I ,本篇有多处参考该文。希望他能抽出时间把 II 给写完吧 :P
有人说,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就无能为力了。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;
}
......
}
于是万恶的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会提示类似这样的错误: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);
}
引用
cross.c:14: error: jump to label ‘wtf’
cross.c:9: error: from here
cross.c:11: error: crosses initialization of ‘void* c’
cross.c:9: error: from here
cross.c:11: error: crosses initialization of ‘void* c’
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,按照资源申请顺序的倒序释放资源(为什么呢,看代码吧~)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毕竟不是个好东西,所以看过内核代码的同学可能会发现另外一种替代性的结构: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);
}
既消除了“不法分子”,也达到了避免冗余的目的。对于这个结构,其实还有更多的好处,详见这里。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;
}
}
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,却照样避免了内存的泄漏。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;
}
既然说到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,程序可能会直接崩溃)。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的问题,可以通过私有化其拷贝函数、拷贝构造函数来禁止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; }
};
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
Dec
23
这程序写了好几次了,干脆贴出来吧~附上exe。
下载文件 (已下载 1831 次)
#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;
}
#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;
}

Dec
13
@2012.12.31 更多细节参见这篇.
根据官方文档:
也就是说,不管是哪个版本,希望启用MYSQL_OPT_RECONNECT,都应该调用mysql_options()明确设置它。由于一个BUG,5.0.19之前必须在mysql_real_connect()之后设置,而更新的版本则可以在mysql_real_connect之前设置。
这里的坑是,在没有设置了RECONNECT时,mysql_ping()并不会自动重连。
因此最完整的解决方法是:
1. 在使用mysql_real_connect连接数据库之后,再使用mysql_options( &mysql, MYSQL_OPT_RECONNECT, … ) 来设置为自动重连。这样当mysql连接丢失的时候,使用mysql_ping能够自动重连数据库。
示例代码:
2. 在执行查询的之前使用mysql_ping()确保自动重连。(但是这个是必要的吗?找不到相应的说明。。。)
UPDATE@2012.12.30:原来之前有很多误解(认为是5.1.6之后就不需要设置MYSQL_OPT_RECONNECT),多谢@Zind同学提醒,这篇基本上是重写了。。。
根据官方文档:
引用
MYSQL_OPT_RECONNECT (argument type: my_bool *)
Enable or disable automatic reconnection to the server if the connection is found to have been lost. Reconnect has been off by default since MySQL 5.0.3; this option is new in 5.0.13 and provides a way to set reconnection behavior explicitly.
#如果连接丢失,启用或禁用自动重连到Serve,。自5.0.3之后,重连默认被禁用。这个选项是5.0.13新增的,提供了明确设置重连行为的选项。
Note: mysql_real_connect() incorrectly reset the MYSQL_OPT_RECONNECT option to its default value before MySQL 5.0.19. Therefore, prior to that version, if you want reconnect to be enabled for each connection, you must call mysql_options() with the MYSQL_OPT_RECONNECT option after each call to mysql_real_connect(). This is not necessary as of 5.0.19: Call mysql_options() only before mysql_real_connect() as usual.
#注意:mysql_real_connect() 在5.0.19之前错误地重置了MYSQL_OPT_RECONNECT选项为默认值(不启用),因此,在5.0.19之前,如果你希望为连接启用重连,你必须在mysql_real_connect()之后调用mysql_options来设置MYSQL_OPT_RECONNECT。5.0.19之后就不需要这样了,只需要(跟其他选项一样)在mysql_real_connect之前调用即可。
Enable or disable automatic reconnection to the server if the connection is found to have been lost. Reconnect has been off by default since MySQL 5.0.3; this option is new in 5.0.13 and provides a way to set reconnection behavior explicitly.
#如果连接丢失,启用或禁用自动重连到Serve,。自5.0.3之后,重连默认被禁用。这个选项是5.0.13新增的,提供了明确设置重连行为的选项。
Note: mysql_real_connect() incorrectly reset the MYSQL_OPT_RECONNECT option to its default value before MySQL 5.0.19. Therefore, prior to that version, if you want reconnect to be enabled for each connection, you must call mysql_options() with the MYSQL_OPT_RECONNECT option after each call to mysql_real_connect(). This is not necessary as of 5.0.19: Call mysql_options() only before mysql_real_connect() as usual.
#注意:mysql_real_connect() 在5.0.19之前错误地重置了MYSQL_OPT_RECONNECT选项为默认值(不启用),因此,在5.0.19之前,如果你希望为连接启用重连,你必须在mysql_real_connect()之后调用mysql_options来设置MYSQL_OPT_RECONNECT。5.0.19之后就不需要这样了,只需要(跟其他选项一样)在mysql_real_connect之前调用即可。
也就是说,不管是哪个版本,希望启用MYSQL_OPT_RECONNECT,都应该调用mysql_options()明确设置它。由于一个BUG,5.0.19之前必须在mysql_real_connect()之后设置,而更新的版本则可以在mysql_real_connect之前设置。
这里的坑是,在没有设置了RECONNECT时,mysql_ping()并不会自动重连。
因此最完整的解决方法是:
1. 在使用mysql_real_connect连接数据库之后,再使用mysql_options( &mysql, MYSQL_OPT_RECONNECT, … ) 来设置为自动重连。这样当mysql连接丢失的时候,使用mysql_ping能够自动重连数据库。
示例代码:
mysql_init() ...
mysql_real_connect()...
char value = 1;
mysql_options(&mysql, MYSQL_OPT_RECONNECT, (char *)&value);
mysql_real_connect()...
char value = 1;
mysql_options(&mysql, MYSQL_OPT_RECONNECT, (char *)&value);
2. 在执行查询的之前使用mysql_ping()确保自动重连。(但是这个是必要的吗?找不到相应的说明。。。)
UPDATE@2012.12.30:原来之前有很多误解(认为是5.1.6之后就不需要设置MYSQL_OPT_RECONNECT),多谢@Zind同学提醒,这篇基本上是重写了。。。
Nov
23
按照某帖子里的说法,禁用掉WMPNetworkSvc(Windows Media Player Netwroking SharingService)即可正常使用sysprep部署了。
好久没发这么短的Blog了,简直就是微博了,OVER。
好久没发这么短的Blog了,简直就是微博了,OVER。
Nov
22
贱板 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都是这个分数吧。

最近看到×讯的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都是这个分数吧。
Nov
20
上次说了,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 的!
闲话不说了,进入正题。
==== 首先坑的总结,下面特别注意下 ====
1. iptables规则执行完要输入完然后check一下是否规则已经添加
2. 21端口不靠谱
3. tcp协议不靠谱
4. 配置Server的时候不要一路按回车!有些是问[y/n]需要输入 y 的!
Nov
16
[UPDATE 11.20] 昨天发现通用PE工具箱出了2.0版本,更新一下,体积增加了一点,并且把GHOST换成了11.5版,这样SSD对齐就不愁了。有需要的同学自己选择吧,下载地址: http://dl.dbank.com/c09zz78k5g
[UPDATE 11.19] 发现无忧论坛有人做了个神器 fbinst和对应的GUI fbinstool,可以把ISO写进去,类似量产的CDROM。
可能很多人不知道U盘其实可以有多个分区的。在U盘的主控与PC通讯的时候,有一个Bit,叫做Removable Media Bit(简称RMB哟),让操作系统知道,这个设备是不是“可移动设备”。对于U盘,这个Bit一般是被设置成1,而移动硬盘则设置成0。所以U盘的分区在我的电脑中被分类到“可移动磁盘”,移动硬盘的分区却是在“硬盘”列表里。Windows有一个强制的规定,如果一个存储设备是可移动磁盘,那么这个设备只能分一个区;即使有多个分区,也只能给第一个分区分配盘符。很霸道吧。据说是因为早期版本的Windows创建对齐的的分区不是xx对齐的,使得闪存的读取效率低,才加了这个规定。反正我觉得不太合理。
题外话1,Linux没有这个限制,可以任意挂载U盘上的分区,所以安卓论坛上会建议某些人把tf卡分出一个区,格式化成ext2,挂载到/data,美名曰app2ext,这样就不需要app2sd,安装到卡上的程序更稳定。
题外话2,很多U盘(但不是所有U盘)的量产工具支持将这个RMB设置为0,这样U盘会被操作系统认为是移动硬盘,就可以分区了;甚至还可以加载一个ISO文件把它当作一个USB光驱来启动系统,详情可搜索 ChipGenius 或者 UpanTool 。
言归正传,手头的U903和S102是IS902主控的,量产工具不太靠谱(第一次买的U903就被我量产挂了,让X东返修,结果它直接给我退货了),只好老老实实当作可移动磁盘用。尽管只能有一个盘符,也还是有些东西可以挖的。折腾了半天,在此推出Felix021版 PE.SATA.U2,功能嘛,就是把网上流传的 《通用PE工具箱v1.9.6》塞进U盘的第二个分区。这个PE工具箱很好用,支持大部分SATA驱动,包含很多好工具,而且还支持安装Vista/ Win7 /2008到硬盘。
好处有3: 1.跟U盘的正常数据区分开来,不会误删,甚至格式化U盘分区都无所谓;2.PE是单独的分区,而且永远不会被Windows载入,不用担心病毒感染;3.(相比于量产+ISO的U盘)不会有多余的分区;4.对不起我数学不好。
下载地址: http://dl.dbank.com/c02b4urxuu ,使用说明在安装包里有了。
注意:1. 并不是所有U盘都支持启动,很多读卡器不支持; 2. 不保证在所有机器上都能启动; 3. 不保证所有SATA都能支持,不支持的话请手动改BIOS设置成IDE模式; 4. 喝完水忘了T_T。
下面是具体实现过程,记录一下,以后如果它有更新,能记得怎么再来一次。。。伸手党可以不用浪费时间了。
[UPDATE 11.19] 发现无忧论坛有人做了个神器 fbinst和对应的GUI fbinstool,可以把ISO写进去,类似量产的CDROM。
可能很多人不知道U盘其实可以有多个分区的。在U盘的主控与PC通讯的时候,有一个Bit,叫做Removable Media Bit(简称RMB哟),让操作系统知道,这个设备是不是“可移动设备”。对于U盘,这个Bit一般是被设置成1,而移动硬盘则设置成0。所以U盘的分区在我的电脑中被分类到“可移动磁盘”,移动硬盘的分区却是在“硬盘”列表里。Windows有一个强制的规定,如果一个存储设备是可移动磁盘,那么这个设备只能分一个区;即使有多个分区,也只能给第一个分区分配盘符。很霸道吧。据说是因为早期版本的Windows创建对齐的的分区不是xx对齐的,使得闪存的读取效率低,才加了这个规定。反正我觉得不太合理。
题外话1,Linux没有这个限制,可以任意挂载U盘上的分区,所以安卓论坛上会建议某些人把tf卡分出一个区,格式化成ext2,挂载到/data,美名曰app2ext,这样就不需要app2sd,安装到卡上的程序更稳定。
题外话2,很多U盘(但不是所有U盘)的量产工具支持将这个RMB设置为0,这样U盘会被操作系统认为是移动硬盘,就可以分区了;甚至还可以加载一个ISO文件把它当作一个USB光驱来启动系统,详情可搜索 ChipGenius 或者 UpanTool 。
言归正传,手头的U903和S102是IS902主控的,量产工具不太靠谱(第一次买的U903就被我量产挂了,让X东返修,结果它直接给我退货了),只好老老实实当作可移动磁盘用。尽管只能有一个盘符,也还是有些东西可以挖的。折腾了半天,在此推出Felix021版 PE.SATA.U2,功能嘛,就是把网上流传的 《通用PE工具箱v1.9.6》塞进U盘的第二个分区。这个PE工具箱很好用,支持大部分SATA驱动,包含很多好工具,而且还支持安装Vista/ Win7 /2008到硬盘。
好处有3: 1.跟U盘的正常数据区分开来,不会误删,甚至格式化U盘分区都无所谓;2.PE是单独的分区,而且永远不会被Windows载入,不用担心病毒感染;3.(相比于量产+ISO的U盘)不会有多余的分区;4.对不起我数学不好。
下载地址: http://dl.dbank.com/c02b4urxuu ,使用说明在安装包里有了。
注意:1. 并不是所有U盘都支持启动,很多读卡器不支持; 2. 不保证在所有机器上都能启动; 3. 不保证所有SATA都能支持,不支持的话请手动改BIOS设置成IDE模式; 4. 喝完水忘了T_T。
下面是具体实现过程,记录一下,以后如果它有更新,能记得怎么再来一次。。。伸手党可以不用浪费时间了。
Nov
15
坎坷经历就不说了,反正最后是¥79买的郎科U903 8GB,128买的威刚S102 16GB,直接上图。
Windows 7 32bit Ultimate, X3 450 3.2G, DDR3 1600 4GB.
测试:ATTO Disk Benchmark, 0.5~8192KB, 256MB读写速度;HDTune存取时间/突发传输速率
详情如下:
Windows 7 32bit Ultimate, X3 450 3.2G, DDR3 1600 4GB.
测试:ATTO Disk Benchmark, 0.5~8192KB, 256MB读写速度;HDTune存取时间/突发传输速率
详情如下: