2012-08-31

debian @ x210i 安装笔记

家里现在有两台thinkpad,一台是我的服役5年,状态尚好的T60,另外一台是老婆毕业后新买的x201i。x201i轻巧便携,但无奈屏幕太小,和T60的高分辨率方屏一比,是在是木有太多吸引力。老婆有时在家加班,也霸占着我的T60,x210i基本就闲下来了。

家里有一台电脑闲着,而且是x系 列的thinkpad,我的心思不由得开始活络起来了 🙂

说服了老婆之后,就开始忙活起来了,换硬盘,导数据,下映像,刻盘。没错,我要把小i改造成我的linux工作站,发行版嘛,自然是Debian莫属啦。下面把安装配置过程记录如下自己备忘

一、安装

1、net install on wifi 问题

安装debian,最方便自由的方式,还是选择netinst镜像。但是如此一来的话,需要在安装过程中就有网络的支持。

debian 从squeeze开始支持在net install的过程中使用wifi来连接网络,但是在安装前,首先要解决驱动问题:x210i使用的intel的wifi芯片,是没有开源的实现的。因此讲究纯净的debian在安装光盘中并没有附带其驱动,需要额外下载。

其实debian已经在其apt源中添加了上述驱动包,可以到这里下载:

http://packages.debian.org/sid/all/firmware-iwlwifi/download

下载完成后,使用7-zip将deb包解压,得到下面这些文件:

/lib/firmware/iwlwifi-1000-3.ucode
/lib/firmware/iwlwifi-3945-1.ucode
/lib/firmware/iwlwifi-3945-2.ucode
/lib/firmware/iwlwifi-4965-1.ucode
/lib/firmware/iwlwifi-4965-2.ucode
/lib/firmware/iwlwifi-5000-1.ucode
/lib/firmware/iwlwifi-5000-2.ucode
/lib/firmware/iwlwifi-5150-2.ucode
/lib/firmware/iwlwifi-6000-4.ucode
/lib/firmware/iwlwifi-6000g2a-5.ucode
/lib/firmware/iwlwifi-6000g2b-5.ucode
/lib/firmware/iwlwifi-6050-4.ucode
/lib/firmware/iwlwifi-6050-5.ucode

准备一个u盘(注意要是fat 32格式的,ntfs格式的貌似不会被识别,将上面这些文件拷贝至u盘根目录下。当安装界面提示需要第三方媒介提供iwlwifi*等文件的时候,插入u盘,安装程序会自动搜索并安装网卡驱动。

(更多…)

2012-03-26

The “dollar” in awk

首先贴一个简单的awk命令,猜一下,这个命令执行之后的输出会是什么?

$ echo "" | awk '{OFS=","; $x = 1; $y = 1; $y = $x; $y += 1; print $x, $y;}'

理想中,输出应该是这个样子的:

1,2

但现实中,却是这个样子的:

2,2

这是怎么回事?难道awk的赋值不是值传递(assignment by value),而是引用传递(assigned by reference)?困惑了好久,先RTFM后STFW两招杀手锏下来也毫无结果,人家压根就没提awk还有值传递和引用传递的区别 T_T

盯着这个表达式看了好久,忽然恍然大悟,awk和php还有bash是不一样的,变量前面不需要加”$”引用,把”$”去掉后,一切正常:

$ echo "" | awk '{OFS=","; x = 1; y = 1; y = x; y += 1; print x, y;}' 
1,2

那,加上”$”之后原本的变量的意义又变成什么了呢?为什么有这样看起来像引用传递的结果?

原来在awk中,”$”表示对字段(Field)的引用,诸如$0表示整条记录(Record),$1表示记录中的第一个字段,$2表示第二个,以此类推下去。当然,awk允许非常量类型的字段引用。$n也是可以的,如果n=1,则$n == $1

至此,再来看本文第一个例子,其实可以翻译如下:

$ echo "" | awk '{OFS=","; $0 = 1; $0 = 1; $0 = $0; $0 += 1; print $0, $0;}' 
2,2

在脚本执行期间,x与y的值一直都是未初始化的0,因此,对于$x, $y的操作都是对$0的操作,所以看起来就像是引用一样。

终于搞明白了,可以松口气了,但是花费了近两个小时的时间。

吐槽这些语言语法混杂容易搞混之余,也只能怪自己学艺不精犯下此等低级错误 =。= 记录在此,以敬效尤

2009-05-02

构建Linux下的函数库编译方案

就快离开学校了,最近打算把大学这几年积累下来的代码重构一下,写成类似于ACE那种形式的C++代码库,方便调用。也算是留给学弟学妹们的礼物。

在整理过程中遇到许多问题,感觉都颇有启发性。尤其是构建编译方案的过程,几乎让我重新学习和认识了make工具,收益匪浅。下面就把这个过程和盘托出,权当笔记,也希望对大家有用。

一:初始编译方案:

目录树:

|-- Makefile
|-- README
|-- doc
|   |-- CHANGES
|   |-- COPYING
|   |-- CREDITS
|   |-- INSTALL
|   `-- TODO
|-- inc
|   |-- Exception.h
|   |-- HashTable.h
|   |-- MessageQueue.h
|   |-- Mutex.h
|   `-- Semaphore.h
|-- lib
|-- mks
|   `-- linux.mk
|-- obj
|-- sample
`-- src
    |-- Exception.cpp
    |-- HashTable.tpl
    |-- MessageQueue.cpp
    |-- Mutex.cpp
    `-- Semaphore.cpp

(更多…)

2009-04-01

Working PHP and Oracle under Redhat EL 5

众所周知,PHP对MySQL的接口做得很好,LAMP名声在外,但是对于其他数据库的支持貌似就差了点。以前试着用过一次PHP的SQLServer接口(mssql),感受是做得相当粗糙。但是考虑到SQLServer是windows阵营的,应用也仅限于windows操作系统,也就情有可原了。但是对于Oracle这个重量级&跨平台通吃的家伙,PHP的支持又怎样呢?

手册中有两个关于Oracle的函数库。其中一个名为Oracle Functions,是比较旧的哪个版本,已经废弃。第二个为OCI8 函数库,是当前使用的版本。基本上实现了OCI的功能(包括事务与LOB操作等),看起来还不错(没用过,因此性能有待考证)。

顾名思义,OCI8函数库使用OCI实现,而使用OCI是必须要安装Oracle提供的客户端的。虽然Oracle的客户端是免费软件,但并不是自由软件,所以一般的发行版在打包的时候不会包含进来。因此,需要自己动手配置一下才能使用。

下面的步骤在Redhat EL 5上测试通过,在其他发行版上应该也是大同小异,写在这里以供参考。

1、安装配置Oracle客户端

a) 下载Oracle Instant Client

下载basic,sdk,sqlplus三个包

        instantclient-basic-linux32-11.1.0.7.zip
        instantclient-sdk-linux32-11.1.0.7.zip
        instantclient-sqlplus-linux32-11.1.0.7.zip

b) 安装并配置

按照Oracle的传统,放在/opt下

        # mkdir /opt/oracle
        # unzip instantclient-basic-linux32-11.1.0.7.zip
        # unzip instantclient-sdk-linux32-11.1.0.7.zip
        # unzip instantclient-sqlplus-linux32-11.1.0.7.zip

解压完后都在一同个目录instantclient_11_1中

        # mv instantclient_11_1  /opt/oracle/instantclient

(更多…)

2008-01-02

生成windows远程桌面rdp配置文件

今天研究了一下windows远程桌面客户端,就是system32下面的mstsc.exe。

一向讲究易用性的微软在mstsc.exe中提供了一个将主机地址,用户名、密码等连接所需信息保存到缀名为.rdp的文件中,以便将来快速打开的功能 。

现在我想要做的是,如何自己生成这个.rdp文件,让我自己的程序更方便地调用mstsc.exe?

首先需要做的就是分析.rdp文件的格式。

用文本编辑器打开.rdp文件,可以发现rdp文件的内容以是文本形式的,而且大部分都是明文,只有密码这个字段,明显是经过加密的。

所以,自制rdp文件的任务的关键就是要生成这个密码。

查了一下,mstsc.exe使用的加解密算法分别是CryptProtectData和CryptUnProtectData两个windows API。

知道了这个的话就好办了,直接照着MSDN上CryptProtectData的示例改就行了。

下面给出代码:

#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>

void main()
{

    DATA_BLOB DataIn;
    DATA_BLOB DataOut;
    // mstsc.exe中使用的是unicode,所以必须做宽字符转换
    BYTE *pbDataInput =(BYTE *)L"freedom";
    DWORD cbDataInput = wcslen(L"freedom")*sizeof(wchar_t);

    DataIn.pbData = pbDataInput;
    DataIn.cbData = cbDataInput;

    FILE *fp;

    if(CryptProtectData(
        &DataIn,
        L"psw",                                // A description string
                                            // to be included with the
                                            // encrypted data.
        NULL,                               // Optional entropy not used.
        NULL,                               // Reserved.
        NULL,                               // Pass NULL for the
                                            // prompt structure.
        0,
        &DataOut))
    {
        printf("The encryption phase worked.\n");

        fp = fopen("password.txt","w");

        int count=0;
        while ( count <= (int)DataOut.cbData ){
            // 因为一个unsigned int 占32位
            // 转换成成16进制要占两位
            // 所以这里需要用%02
            fprintf(fp,"%02X",DataOut.pbData[count]);
            count++;
        }

        fclose(fp);

    }
    else
    {
        printf("Encryption error using CryptProtectData.\n");
        exit(1);
    }
}

编译时需要指定连接crypt32.lib库,所以一个最简单的编译命令为

cl rdp.cpp crypt32.lib

意外的是,这个程序生成的加密字符串比mstsc.exe生成的要短很多,后面一部分也不一样。但既然是用CryptProtectData加密的,自然也应该能用CryptUnProtectData来解密。实验了一下,果真如此。
回头再想想,微软这么做也是有道理的。因为直接存储CryptProtectData加密过的数据的话,任何人都能使用CryptUnProtectData恢复出明文,加密也就失去了意义。但如果不公开加密算法,那这个rdp文件对于非微软内部的程序员来说,灵活性就变成了0。现在的处理方式既保证了密码的安全性(某种程度上的),也为程序员留下了扩展开发的余地,一个小程序的生命力也由此得到了增强。

见微知著,从一个小东西里面,也可以学到很多。

–EOF–

2007-11-12

pthread in C++

Linux下的编程一直是C语言的天下,但老是用C感觉写的很乏味。用面向对象方法编程,听着都倍有面子。于是决定先在的这个项目用C++来写。虽然不一定能“以C++的思想”来写C++,少会有C++的样子。

但是问题来了:我需要在程序中动态创建一个线程,而pthread不接受C++类的成员函数作为参数。

原因也很简单,类成员是在类被实例化成为对象后才存在的,即在编译时是不存在的,编译器无法取得函数的确切入口地址,自然无法通过编译。

照这个分析,如果把要调用的类成员函数声明为静态的,pthread_create就可以找到函数的地址了。但这样一来的话,由于类中的静态函数无法调用类的非静态成员。线程函数的功能就受到了很大限制,也就没有比要将其放在类中了。

最容易想到的解决方案就是写个C函数,然后再C函数中调用C++对象的成员函数。

比如类为

class foo(){

    public :
        thread();

}

class *f;

void *bar(void *arg){

    f->thread();       

    return NULL;
}

int  main(){

    .........
    f=new foo();
    
    pthread_create(&tid,&tattr,bar,NULL);

}

显然这种发法太笨了,而且对象只能是全局的。

注意到线程函数bar可以有一个任意类型的指针作为参数,那我们何不将对象通过这个指针将对象变为bar的一个参数,从而让我们的程序好看一些。

class foo(){

    public :
        thread();

}


void *bar(void *args){

    foo *f=(foo *)args;

    f.thread();       

    return NULL;
}

int  main(){

    .........
    foo *f=new foo();
    pthread_create(&tid,&tattr,bar,f);

}

如果把上述两种方法结合起来即对象中的静态函数+通过指针参数传递对象,那又会怎么样呢?

class foo(){

    public :
        int thread();

        static void *wrapper(void *args){
            foo *f=static_cast<foo *>(args);
            f->thread();
            return NULL;
        }

}

int  main(){

    .........
    foo *f=new foo();
    pthread_create(&tid,&tattr,foo::wrapper,&f);

}

现在,强迫症患者,是不是觉得整洁清楚很多了呢?

–Reference–
http://www.osix.net/modules/article/?id=450

–EOF–

2007-10-23

typedef howto

函数指针的声明可能是C语言中最复杂的声明了。

比如标准C中信号捕捉函数signal的实际声明如下:

void (* signal(int sig, void(*func)(int)))(int);

它的含义是:

signal是一个函数,其参数为整型 int sig以及一个指向函数的指针,这个被指向的函数接受一个整型的参数并返回值void。同时signal函数本身的返回值同他的一个参数一样,也是一个指向接受一个整型参数并返回void的函数指针。

horrible !

但我们在手册中看到的接口声明并非如此,而是非常简洁易读的如下形式:

sighandler_t signal(int signum, sighandler_t handler);

这是因为使用了typedef的缘故

typedef void (*sighandler_t)(int);

这样就可以用简洁的sighandler_t代表一复杂的函数指针了。

由此可见typedef的魔力。

经常用到typedef的地方还有结构体的声明,使用typedef可以少敲不少字。

typedef struct {
    int bar;
}foo,*pfoo; 

foo var;
pfoo pvar;
pvar = &var;

如果结构体中有指向自身的指针(这很常见,不是么?),上面的方法就不好用了 ,但我们另有解决方案:

typedef struct foo foo;

struct foo{
    int bar;
    foo * pbar;
};

foo var;

细心发现,typedef对于未存在的类型也能指定别名。

一个小陷阱:typedef与define的区别。

“typedef和宏文本替换之间存在一个关键性的区别……把typedef看作一中彻底的‘封装类型’ -- 在声明它后不能在向里面添加别的东西” --- C专家编程

#define foo int
unsigned foo i; // this is OK

typedef int bar
unsigned bar j; // illegal!

其次typedef可以保证声明中所有变量都是同一种类型。

例如:

#define foo int * 
typedef  int * bar;

foo i,j; // 等同于 int * i,j; 则i为指针,j为整型

bar m,n; // m,n都是指向整型的指针

结论: 使用define可能会引起潜在的错误,所以,还是使用typedef吧。

适度地使用typedef可以使程序简明清晰,不过,滥用typedef也是不好的,非常不好。我尤其不喜欢用typede给指向结构体指针取别名,谁希望与同一个结构体相关的变量出现好几个在字面上看起来不相关的名字?

–EOF–