Jan 10
因为在那个触摸屏上打字太痛苦了,于是决定安装一个openssh-server。
查看了一下/etc/apt/sources.list,内容只有一行
引用
deb http://ports.ubuntu.com/ karmic main universe restricted multiverse
原来是karmic,9.10了哇,真是与时俱进。直接用的官方源。
# sudo passwd root 随便给root设置一个简单的password
# su - 切换用户为root
#apt-get update 发现住处那个ooxx的长城宽带,连上去的速度大约是100B/s以内。很爽。

# apt-get update 带到公司再来,这已经是今天了=。=
# apt-get install openssh-server
openssh-server 已经是最新的版本了。
共升级了 0 个软件包,新安装了 0 个软件包, 要卸载 0 个软件包,有 80 个软件未被升级。

&*@$ 居然已经安装了,抑郁。
# /etc/init.d/ssh start
* Starting OpenBSD Secure Shell server sshd               [ OK ]

然后在台式机上打开SecureCRT连上去,用户名密码就用root的那个,然后看到这个提示符,表示登录成功啦
root@SmartQ:~#

于是就不用忍受终端下用触摸屏打字的痛苦了..

p.s. 顺便安装了一些东西:gcc g++ build-essential vim ctags cscope,然后写了个hello smartq,没问题,嗯。以后应该会有用的,哇哈哈。另外,发现机器上已经有perl和python了,真是好东西哇好东西。
再p.s. apt安装的时候会先下载安装包,会放在/etc/cache/apt/archieves,如果确定不会删了再装,可以把它们都删掉 #apt-get clean,或者可以备份到卡上,免得下次下载很痛苦。


update 1 @ 2010-01-17

1. 更新源用国内的这个,速度很快
deb http://ubuntu.srt.cn/ubuntu-ports/ karmic main restricted universe multiverse

2. 安装个lrzsz,然后用SecureCRT连上去以后就很方便了
# sudo apt-get install lrzsz
# sz 文件名
可以下载到电脑上
# rz -e 回车
在电脑上选择文件,传输上去
Jan 10

入手智器SmartQ V7 不指定

felix021 @ 2010-1-10 14:31 [IT » 硬件] 评论(4) , 引用(0) , 阅读(7660) | Via 本站原创
昨天到中关村去,鼎好A2574试用了3个小时的SmartQ V7,然后终于决定买下来,一个V7,一个布袋,送一个烂耳机,一共1530。其间还有些纠结,反正没有买到可用的USB网卡,也没有买到想买的USB充电线,小抑郁。回来后在当当上买了一个KingMax的SDHC, 16GB, class6, 230。也就是总共花了接近1800。

折腾了很久,Ubuntu很慢,除去这个问题的话 用起来还算比较happy,此外就是屏幕不够大,那个键盘就占了好大的空间,很囧。之后试着看能不能把它转过来。WinCE软件不少,折腾起来阻力,而且全是玩软件,太无聊了。Android一点都不好玩。

后面慢慢贴一些“研究成果”出来grin
Jan 7

apache, mod_cgi 不指定

felix021 @ 2010-1-7 20:40 [IT » 网络] 评论(1) , 引用(0) , 阅读(6960) | Via 本站原创
编译apache的时候默认应该是有mod_cgi的,如果不确定,可以加上--enable_cgi。

在httpd.conf里面增加(或修改,因为可能已经有这个东西了):
引用
ScriptAlias /cgi-bin/ /某路径/cgi-bin/
如果你修改了“某路径”,那么后面对应的Directory一节也应该对应修改
引用
<Directory "/某路径/cgi-bin">                                                  
    AllowOverride None
    Options FollowSymLinks #如果不放心,这里还可以加上一个ExecCgi选项。
    Order allow,deny
    Allow from all
</Directory>

下面是一个简单的C编写的cgi程序,gcc -o printall simple_cgi.c,把printall放在cgi-bin目录下面。然后访问:http://localhost/cgi-bin/printall,可以看到输出。特别注意一下,cgi不仅要打HTTP实体,还要打HTTP头信息。如果你实在懒得写,在实体前面加上一个回车就行了:) 否则你会看到apache的error_log里输出诸如"malformed header from script. Bad header="这样的错误信息。
#include <stdio.h>
#include <stdlib.h>

extern char ** environ;

int main ()
{
    int i;
    puts("Content-Type: text/plain\n\n");
    for (i = 0; environ[i] != NULL; i++) {
        printf ("%s\n", environ[i]);
    }

    if (strncmp("POST", getenv("REQUEST_METHOD"), 4) == 0) {
        char buf[1025];
        while (1) {
            fgets(buf, 1024, stdin);
            printf( "%s", buf);
            if (feof(stdin)) {
                break;
            }
        }
    }
    return 0;
}

特别点出一下,GET请求的query_string是在environ["QUERY_STRING"]里面,而POST请求的输入,是从stdin中读取。
Jan 4
在发现有atoi这个函数之后的一段时间里(converts a string to an integer, cppreference.com),每当我需要把integer转换成字符串的时候,我就会想当然地写上itoa,直到编译器告诉我,这个函数不存在。反复几次以后,我终于记住了没有itoa,记住了应该用sprintf(现在想想,是应该用snprintf才对)。直到今天我才意识到自己从来就没有仔细想过,为什么有atoi,却没有itoa。也许还是自己求知欲太低了。

原因其实很简单,简单地说,就是因为不好处理返回值。atoi, atof, atol等函数之所以可以在C标准库中存在,可以列出来的有两个必要条件:一,需求使然;二,返回值类型是基本类型,可以使用匿名变量传递。itoa之所以不在C标准库中,我觉得并不是没有需求,而是因为返回值是字符串,也就是char数组,处理起来比较纠结。

在C里面,字符串始终是个硬伤。如果非要设计这么一个函数,有三种可选的办法:

一,使用一个static的char数组(貌似printf就是这么做的吧)。这有2个问题,1是数组的长度是固定的(当然,简单处理的话可以认为int最多就那么几位,只要初始化某个足够的长度肯定就OK),2是每次调用会覆盖上次的结果,特别地,会导致非线程安全。

二,每次调用,在函数内部malloc一段空间,写入,返回该空间的首地址。存在问题是,分配的空间函数内无法自行释放,需要调用者安排一个free,而这个free是非常容易被忽略的,这就存在了潜在的内存泄漏风险。

三,要求传入一个分配好空间的指针,函数将转换结果写入。这样实际上就是sprintf的做法,重复造轮子。而且还要考虑内存越界访问的问题,再增加一个参数n,然后这就是snprintf做的事情....

综合起来的结果就是:太麻烦了,不实现。

最后,推荐云风的一篇文章,这里探讨了与以上内容比较相关的”一个 C 接口设计的问题“
http://blog.codingnow.com/2009/01/c_interface.html
Dec 29
注: 以下内容稍有处理,隐去一些不适合放上来的内容。

felix021 10:14:18
我昨天跟同学(即sandy)有讨论了一下,他说,我举的例子属于“非本质性复杂”,就是说,是语言层面可以解决的,通过取舍,或者修正历史遗留问题解决,但是框架方面的,比如机制和策略的分离,这个属于本质性的复杂,不是语言层面可以解决的

zja 10:34:01
我觉得做出这个选择的最重要标准是系统的规模。
这句说的挺本质,
写的不错,
另外多发现一些现有框架的优点,
尽可能多的找出设计的的矛盾对立面
比如开放性,封闭性
开放性:当有新的需求到来的时候,能不能快速适应
封闭性:框架严格戒定了我能做什么,我不能做的开发都自行实现
两都又各有优缺点:开放的框架适应性强,封闭的框架清楚,明了,稳定

felix021 10:43:07
嗯 我们的框架非常强大 这个是肯定的

zja 10:43:15
实际类似这样的矛盾对立面尽可能多的了解到的话,
设计上就会提高了,想的面多了,
不存在怎么样做是最完美的,
只能说针对现有的情况,且实很好的解决了问题

felix021 10:43:22
只是我在使用过程中遇到一些困扰

felix021 10:43:39
这种感觉很像是我在用 C++的时候的那种感觉

felix021 10:44:17
仔细想想其实这是不可调和的矛盾,但是还是觉得有些别扭

zja 10:45:42
玩程序就像练武,
一开始不要总武当不好,少林不好,xx也不好,
不如深入的喜欢一门,比如武当,
当你对这一门武艺精通到一定程度,(内功)
那么这时候才是你该跳出来的时候,
其它武功哪地方好,应该熔合进来,
往往这时候,跳出来是最难的了。


felix021 10:46:03
这个比喻好~~受教

zja 10:47:16
如果没有专一的对一门武艺的深入研究,连架子都拉的不对,
内功就更不用提了,
那么根本没有理解道这门武艺一剑封喉的点,
框架有时候也可以这么理解,
分久必合,合久必分,

felix021 10:48:38
嗯~我内功还欠缺的很,这些是这实习几个月的感受。
以前对框架性的东西接触的少,呵呵。

zja 10:48:48
当你使一个框架的时候,总会觉得其它方式更好,
因为他的优点总是被乎略,缺点总是很容易被看到,
草总是远处的绿,
因此要更深入的去理解它的时候,
你要看穿这些东西,
比如这个大框架,比如...框架,我们都很容易发现它的缺点,
但我觉得多去发现它的优点,本质的东西掌握了一定层面,
可能再去想想它的缺点(跳出来)会更有意义,

felix021 10:51:39
嗯 学习这框架之后的确是感受到他们的强大 也是用了一段时间以后才发现有一些不合意的地方

zja 10:52:22
这东西就是一个往返的过程,
这说明进步了,
但我觉得可能还不够,可能过段时候你会又感觉到它的好,
再过段时候你又会感觉到他的不好,
就是多层面多角度的去看问题,
当然,这要你我们慢慢积累的经验,
往往许多东西走到一定层面,就出会现回归,
比如某server的接口拆分,
现在看起来当初合的有很多道理了,
但当初想拆的时候,同样有N多理由需要拆,
这就是一组矛盾,要辨证的去看,
我觉得没有绝对的错,也没有绝对的对,
看应用,看需求,看规模,

felix021 10:56:56
嗯。需求的变化,规模的增长,带来太多不确定性,不是可控的,只能在变化中学习,不断摸索不变的东西积累起来,最后才能以不变应万变。

zja 10:58:36
是的,
康神的一句话,现在记得很清楚,
基本原则不能违背,
基本原则遵守的好的,敌人再绕,总在我们手掌心,
但基本原则打破了,我们发现一个露洞修一个,发现一个修一个,总也修不完,

felix021 10:59:40
嗯 我该再好好回顾设计模式相关的内容 现在再去看 应该会有更深的体会

zja 11:00:14
多关注矛盾很有意义,
比如大小表问题,
比如分表与不分表问题,
比如缓存与一致性问题,等等,
看现有的如何很好的解决的,
这时候你才会想,我遇到这样的矛盾很纠结的时候,这是不是一个很好的参考点,

felix021 11:01:07
嗯 这样的矛盾是本质上的复杂性,如果能想清楚,就能在实际的设计、开发过程中做出最好的权衡

zja 11:01:37
是的,
其实再多想一步,
编程是这样,工作,生活也是这样,

felix021 11:03:29
嗯 我一直觉得程序和生活之间还是有很多相通的地方的
~多谢jiuan了,以上所说的内容我会再好好想想 :)
Dec 28
一个完美的框架,应该是一个实现了对修改关闭,对扩展开放的框架。需要修改,意味这这个框架仍然存在瑕疵;不适合扩展,那就没有被称为框架所需的内涵。当然,完美的框架是不存在的,也没有一个框架能够满足所有的情况,所以需要根据具体的情况来做出选择。

我觉得做出这个选择的最重要标准是系统的规模。

对于一个小型系统,也许是一次性的(没有多少扩展需求),或者代码比较少(修改起来比较容易),那么框架的存在就不一定是必要的了。不论是自己设计一个框架,抑或是学习并采用一个现有的框架,都需要耗费比较多的时间精力,对于一个小型系统而言,很可能得不偿失。当然,不需要框架并不意味着可以随意开发(随意开发的过程是痛苦的,越往后越明显,felix深有感受...),高内聚低耦合等基本的设计原则还是必须遵从的,这样能够使开发过程更简单。更严格一点说,一个良好的设计本身就带有一些框架的性质。

对于一个中型系统,如果规模虽然稍大,但是仍然可以在少数几个人的掌握之下,那么一个强大的框架对开发是有相当助益的。在这种情况下,学习框架的开销是可以接受的,一次投入多次产出:在掌握了框架以后,每当需要增加新的功能的时候,只需要按照框架的规则写一个模块并插入到系统中,多余的事情都由框架来完成,非常轻松。

对于一个大型系统,其规模已经大到不是几个人可以掌握的了,那么在开发之初对框架的选择是至关重要的。因为当系统大到一定规模再更换框架,在很多情况下是不现实的,所以在开发之前就必须有足够的考虑。一个强大的框架是必须的,但我觉得这还不是全部:这个框架还必须简单。俗话说计划赶不上变化,当初设计得再好的功能,逐渐也可能会不适应后来的需求(PM一句话,RD两行泪=.=),以至于成为整个系统的累赘。所以框架还必须要简单(无为而治)——这个简单不是说框架需要提供少的功能。引用讲述Unix设计哲学的一段话:
引用
The distinction between mechanism and policy is one of the best ideas behind the Unix design. Most programming problems can indeed be split into two parts: “what capabilities are to be provided” (the mechanism) and “how those capabilities can be used” (the policy). If the two issues are addressed by different parts of the program, or even by different programs altogether, the software package is much easier to develop and to adapt to particular needs.
框架就应该是提供mechanism(机制)的部分,而策略,就应该是具体的某个功能。这样一个框架就简单了,当你用这个框架进行开发的时候,你是在使用框架提供的机制来实现你的策略,你可以把握策略的实现,而不用担心在什么地方框架带来了难以察觉的干扰。

这些是非常理想化的选择,但是实际中又存在许多难以避免的问题。

其一,温水煮青蛙
这个寓言大家都耳熟能详了,虽然其真实性有待验证,但是其寓意还是很有警示意义的。一个系统的规模并不一定是刚开始的时候就可以知道的。比如创业的小公司,刚开始可能只是设计一个小型或者中型的系统,直到某一天发现系统负载或者是其扩展性已经或者将在可见的未来无法满足要求。这时候为了发展只能进行底层的重构。

其二,机制和策略的矛盾
Unix哲学考虑得很好,程序应该分成机制和策略两块,但是将这二者完全划分清楚未必是好的,甚至是不可能的。如果框架完全不干涉策略的实现,那么意味着每一个模块的开发都会有更多的工作量。如果框架过多地干涉策略,那么意味着框架过于复杂,掌握框架的代价更高。所以这里存在需要权衡的地方,而权衡的标准,还是的得考虑系统的规模和复杂性。于是又回到上一个问题了。

其三,...
需求的不确定、某个语言的局限这样一些具体的问题,也许不应该放在这里,但是在实际的开发和维护过程中,他们又的确会困扰开发者。甚至让人头疼。

----

说了这么多,感觉有点虚,因为没有什么特别具体的例子。公司里的项目不适合拿来讨论,但是正好前几天跟同事聊到一个例子,觉得比较适合放在这里说说。

这个例子就是C++,和C。最近一段时间我对C++有一些排斥,所以很难保证我下面将要说的内容的中立性,有任何问题欢迎探讨。

我觉得C++本质上就是C的一个框架。C++包装了C语言,提供了一些方便开发的特性,比如对面向对象的支持,比如对泛型的支持。根据上面提到的命名方式,我们可以认为这些特性就是这个"C框架"提供的机制,而C++程序员开发相应的程序,就是使用这些机制来完成其特定的策略。另一方面,C++不仅提供了机制,还在一定程度上影响了策略的实现。

比如说,在C++中,类的成员函数是否具有virtual属性,决定了这个类(的这个成员函数)是否可以实现多态。当然,这可以看作是一种机制的实现,但是它不可避免地影响了具体的策略。更特别的是,如果一个被继承的类的析构函数不具有virtual属性的话,可能程序在运行过程中会出现内存泄漏。

比如说,当你编写一个inline函数的时候,你可能期望它总是被inline,但是可能由于某些你不知道的限制,实际上inline属性并没有生效,它还是作为一个函数被反复调用。

再比如说,当你使用一个模板的时候,你可能不会考虑到这些模板对内存的占用。但实际上,模板的每一次特例化,都会使得几乎相同的函数在内存中存在两份拷贝(也许这个例子有些牵强)。另一个模板的例子是在特例化的时候没有特别注意类型,比如说make_pair(1, 2.0)实际上创建了一个pair<int, float>,而不是pair<float, float>。

这些是多少都可以算做是这个C框架不仅提供机制,还影响策略的例子。我相信还有很多类似的例子是我不知道的,但是糟糕的是,要掌握这个C框架的代价太大。这也就是我近来对C++产生排斥感的主要原因(另一个原因是我担心C++用多了,以后写代码会越来越懒~~~)。

说了这些不是想说C++是多么不堪、不能使用,去年暑假有很长一段时间我在仔细学习STL,其中有很多很伟大的设计。从这个角度来说,对C++我是心存敬畏的。从框架的角度来看,C++实际上是一个很强大的C框架,很适合快速开发,比如写一个acm程序,我肯定用C++,好好用上STL。做一个小型的系统,我也许也会选用C++。我只是觉得,这个强大的框架做得不够简单,如果要考虑大型系统的开发,务必要仔细权衡。

---

最后,使用一个很著名的设计原则来结束这篇日志:

KISS: Keep It Simple, Stupid.
Dec 28
注:本文只是简单介绍这三个东西并对比一下其功能、差异,不讨论这些东西是否有存在的必要以及优劣。
· goto
· setjmp, longjmp
· try-catch

一、看看基本的使用
1. goto
这个比较简单,比较容易理解,只要设置一个行标就行了:
例子:
int main ()
{
    int i = 0, sum;
    for (i = 0; i < 100; ++i) {
        sum += i;
        if (sum > 1000) goto JMP1;
    }
JMP1:
    printf("%d\n", i);
    return 0;
}


2. setjmp, longjmp
goto用起来是简单,但是存在一个先天缺陷:只能在同一个函数内使用。
有时候我们写一个代码,有两三层的函数嵌套,想要返回的时候就比较囧。
这时候用setjmp和longjmp就很happy。
例子:
#include <setjmp.h>
jmp_buf jmpbuf1;

void bar() {
    printf("Hi, I'm bar!\n");
    longjmp(jmpbuf1, 1);
}

void foo() {
    printf("Hi, I'm foo!\n");
    bar();
    printf("Should never come here\n");
}

int main ()
{
    int i = 0;
    i = setjmp(jmpbuf1);
    if (i == 0) { //setjmp第一次某个jmp_buf的时候返回0
        foo();
    }
    else { //否则返回longjmp给出的值
        printf("i = %d\n", i);
    }
    return 0;
}

输出是:
Hi, I'm foo!
Hi, I'm bar!
i = 1

3. try-catch (在这里算是歪用了,呵呵)
这个是c++提供的语言特性,可以用于捕获throw语句抛出的异常,不仅可以在函数内使用,也可以跨函数~~
例子:
void bar() {
    printf("Hi, I'm bar!\n");
    throw 1;                                                                    
}

void foo() {
    printf("Hi, I'm foo!\n");
    bar();
    printf("Should never come here\n");
}

int main ()
{
    try{
        foo();
    }  
    catch(int i) {
        printf("i = %d\n", i);
    }  
    return 0;
}

输出是:
Hi, I'm foo!
Hi, I'm bar!
i = 1


二、简单对比一下:
· goto,使用简单方便,看起来比其他两个更容易一点(有行标),但是只能在函数内跳转;
· setjmp和longjmp,稍微麻烦点,需要带个参数(jmp_buf,全局变量,或者传参),好处是可以跨函数跳转,且可以根据setjmp的返回值得知从何处跳转。还有一个小缺陷就是,因为需要先执行setjmp以后才可以跳转,所以可以跳转的地方有一定限制,也使得代码看起来有点不够清晰。
· try-catch,C++有C没有,看起来结构比较清晰,不需要带额外的参数,抛出不同的值(和类型)方便判断来源。

三、重点考察一个问题:
如果有一个class,比如
class T
{
    public:
        ~T() { printf("I'm dead >_<\n"); }
};
那么使用goto、setjmp或try-catch的时候是否存在问题?

    if (1) {
        T t1;
        goto JMP1;
    }
JMP1: ;
有输出I'm dead >_<
    jmp_buf jmpbuf1;
    if (setjmp(jmpbuf1) == 0) {
        T t1;                                                                  
        longjmp(jmpbuf1, 1);
    }  
    else {
        ;  
    }  
无输出
    try {
        T t1;                                                                  
        throw 1;
    }
    catch(int i) {
        ;
    }
有输出I'm dead >_<

所以在使用这三个东西的时候,一定要特别注意,建议在C++里头不使用setjmp,以免查错无门...
Dec 26
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#define P(a, b) printf(#b ": %" #a "\n", b)
#define Ps(a, c, b) P(a, (c)->b)
#define alloc(name, type, n) type *name = (type *) malloc(sizeof(type) * (n))
#define allocs(name, type, n) alloc(name, struct type, (n))

int main ()
{
    time_t t1 = time(NULL);
    P(ld, t1);

    struct timeval tv1;
    gettimeofday(&tv1, NULL);
    Ps(ld, &tv1, tv_sec);
    Ps(ld, &tv1, tv_usec);

    /*
     * //t1 += 8 * 3600;
     * struct tm *tm1 = gmtime(&t1); //标准时间
     */
    struct tm *tm1 = localtime(&t1); //本地时间
    Ps(d, tm1, tm_sec);
    Ps(d, tm1, tm_min);
    Ps(d, tm1, tm_hour);
    Ps(d, tm1, tm_mday);
    Ps(d, tm1, tm_mon+1); //0-based
    Ps(d, tm1, tm_year+1900); //从1900年开始, 0-based

    allocs(tm2, tm, 1);
    tm2->tm_sec = 0;
    tm2->tm_min = 0;
    tm2->tm_hour= 0;
    tm2->tm_mday= 1;
    tm2->tm_mon = 0;
    tm2->tm_year= 70;
    tm2->tm_isdst = 0;
    time_t t2 = mktime(tm2);
    //t2 += 3600 * 8; //mktime是本地时间
    P(ld, t2);

    P(s, asctime(tm1)); //标准时间
    P(s, ctime(&t1)); //本地时间

    alloc(buf, char, 100);

    strftime(buf, 100, "%Y-%m-%d %H:%M:%S", tm1); //标准时间
    P(s, buf);

    strftime(buf, 100, "%F %T", tm1); //标准时间, 格式和上面的一样
    P(s, buf);
    return 0;
}
分页: 36/99 第一页 上页 31 32 33 34 35 36 37 38 39 40 下页 最后页 [ 显示模式: 摘要 | 列表 ]