2013-10-13

buffalo LS-WXL NAS 部署笔记

从上大学开始到现在所积累的照片和资料现在一直是用一台希捷的3.5寸外置移动硬盘保存着。每次拿出来拷东西的时候,咔咔的读盘声总让人不免担心,万一这块硬盘哪天抽风坏了怎么办。这些数据中包含了无数过往的努力和回忆,一旦丢失,代价实在是难以估量。

组建一套家用的NAS是解决目前的窘境的最好选择。但是群晖之类的NAS不带硬盘动辄上千实在是有点贵,心怀一颗屌丝之心,始终不忍下手。用旧PC改装一个,家中实在是没有地方可以容纳。上个月无聊逛易讯时发现Buffalo LS-WXL NAS正在特价,399购入一台,貌似一个月过后到现在还是最低价,占到便宜的感觉十分开心 :)虽然便宜了点,做工和群晖的高富帅没法比,但是两个硬盘槽位,支持RAID 0/RAID 1,linux系统可以破解,支持Web Access,功能足矣。

光有腹中空空的NAS当然不行,还得往它肚子里塞上硬盘才能工作起来,对比各项参数和口碑,西数的NAS专用红盘是最好的选择,但是价格嘛。。。 最终决定下来,还是入了两块西数的2T绿盘,可靠性差了点,但是有RAID 1,两块互为备份,可靠性还是有保证的。易讯绿盘无货,京东现货入手。

考虑到家里特殊的网络拓扑结构,NAS附近有小米盒子,电视机,还有一个Raspberry PI需要接入网络,装修时墙上只留了一个网口,这么多设备需要一个交换机来互联并接入网络,还需添置一个交换机。家里有一个百兆的TP-Link交换机,但是今后如果想用小米盒子直接samba访问NAS放高清电影的话,百兆网络就难当大任了。交换机这个东西属于耐用品,一般都不容易坏,上淘宝上淘一个二手实用的即可。上上下下比较了一番,Netgear GS105是一个不错的选择,铁壳,千兆,成色不错的货挺多的,找了一个靠谱的商家,加配原装电源一个¥99搞定。两天后到手,成色果然不错,9成新有余,拿在手中沉甸甸的,淘宝掌柜诚不欺我也~

万事俱备,只欠部署。安装之前,来一张全家福先~

buffalo nas

(更多…)

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盘,安装程序会自动搜索并安装网卡驱动。

(更多…)

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

(更多…)

2007-10-07

manpages索引更新

从debian sarge 转到 etch后,就发现man东西少了很多,基本的函数,系统调用都没有相应的man手册页。猜想可能是某些和开发相关的包没有装。后来在Synaptic中搜索documentaion,一项一项找,最后发现是manpages-dev没有装。

apt-get install manpages-dev

基本的系统调用、库函数等就都有了。但man -k 和man -f却依然找不到这些函数。猜想可能是存在某些索引之类的东东。可是在网上没有找到在哪里更新索引的指导。man自身的手册页实在太长,看不下去。后来也就将就了。

ubuntu和debian的情况一样,同样默认不安装manpages-dev,无奈,只能忍受。

以前小时候找不到东西经常会很生气,而且越气急败坏越找不到。往往一个小东西搞得一天心情都很差。后来妈妈劝导我,找不到就放一放,过一段时间它会自己出来的。且不论这个理论是否正确,对于调节情绪来说还是相当有用的,况且事实证明最终那些找不到的小东西真会在日后的某一天中被偶然发现。所谓”众里寻她千百度,蓦然回首,她在灯火阑珊处”是也。

而且,我想,那一刻,任何人都会怀有一份惊喜与开心的。

前些日子偶然看到一博客,顺着他的文章一篇篇看下去 — 忽然,ho ho,the answer is Here!

http://blog.verycd.com/yoyopub/showentry=34994

原来manpage的索引由mandb命令管理,在安装了新的manpage文件后,只需

mandb -c
[/basj]

更新一下索引即可。

again,thanks for sharing。

–update–

日前发现manpages-dev包中没有posix相关的内容,包括pthread等的说明,找了一下,这些东西在manpages-posix和manpages-posix-dev包中,装上再重新更新一下索引就可以查阅了。

–EOF–

2007-10-03

Linux下瞬时cpu使用率的统计

top是linux下查看cpu使用率最常见的命令,功能很强大,使用也很方便,是系统管理的好帮手。

在bash脚本中集成top命令获取cpu使用率很简单,但是如果我们想要在C代码中获取当前cpu占用率,又该如何处理呢?

首先要了解一下top的工作原理

1、linux系统通过/proc/stat伪文件系统来输出当前的cpu使用情况,而top正是通过读取此文件中的数据来实现cpu使用情况的统计。
2、cpu占用率并不是一个瞬时概念,而是一个短时间内的统计值,因此无法通过瞬时计算得出,而必须通过一定时间差内的统计来实现。

stat的文件结构

stat文件的前几行为CPU使用信息描述。
具体有多少行取决于系统有多少块CPU,但行中的字段都是相同的。第一行以字符串’CPU’开头,为系统总的CPU使用情况。其余行以’CPU’+CPUID开头,用以分别描述单个CPU。

第一行内容如下:

cpu 15718 197 2574 136003 3881 690 531 0

数字的单位为USER_HZ(1/100ths of a second on most architectures)。
第一个字符串为CPU标识,其后四个数字依次为在用户态消耗的时间片,在低优先级的用户态消耗的时间片,内核态消耗的时间片以及空闲时间片。

2.4内核中,以上即为CPU使用信息的全部内容。但在2.6内核中,这一行还多出了三个额外的数据。分别为 iowait,irq,softirq等操作所耗费的时间片。

了解了以上信息后,我们就可以开始准备编码了~ 不过,最好还是有个参考~

参照busybox的top源代码,我们实现自己的cpu统计程序如下:

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>

#define SMLBUFSIZ 256

typedef unsigned long long ul_t;

typedef struct cpu_t {
    ul_t u, n, s, i, w, x, y, z; // as represented in /proc/stat
    ul_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display
} cpu_t;

cpu_t *cpus_refresh (cpu_t *cpus)
{
    static FILE *fp = NULL;
    int i;
    int num;
    char buf[SMLBUFSIZ];

    if (!fp) {
        if (!(fp = fopen("/proc/stat", "r")))
            fprintf(stderr,"Failed /proc/stat open: %s", strerror(errno));
    }
    rewind(fp);
    fflush(fp);

    // first value the last slot with the cpu summary line
    if (!fgets(buf, sizeof(buf), fp)) fprintf(stderr,"failed /proc/stat read");

    cpus->x = 0;
    cpus->y = 0;
    cpus->z = 0;
    num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
            &cpus->u,
            &cpus->n,
            &cpus->s,
            &cpus->i,
            &cpus->w,
            &cpus->x,
            &cpus->y,
            &cpus->z
            );
    if (num < 4)
        fprintf(stderr,"failed /proc/stat read");

    return cpus;
}

static void format_output (cpu_t *cpu, const char *pfx)
{
#define TRIMz(x)  ((tz = (long long)(x)) < 0 ? 0 : tz)
    long long u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
    float scale;

    u_frme = cpu->u - cpu->u_sav;
    s_frme = cpu->s - cpu->s_sav;
    n_frme = cpu->n - cpu->n_sav;
    i_frme = TRIMz(cpu->i - cpu->i_sav);
    w_frme = cpu->w - cpu->w_sav;
    x_frme = cpu->x - cpu->x_sav;
    y_frme = cpu->y - cpu->y_sav;
    z_frme = cpu->z - cpu->z_sav;
    tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
    if (tot_frme < 1) tot_frme = 1;
    scale = 100.0 / (float)tot_frme;

    fprintf(stderr,"%s: %.2f%%us,   %.2f%%sy,   %.2f%%ni,   %.2f%%id,   %.2f%%wa,   %.2f%%hi,   %.2f%%si,   %.2f%%st\n",
                pfx,
                (float)u_frme * scale,
                (float)s_frme * scale,
                (float)n_frme * scale,
                (float)i_frme * scale,
                (float)w_frme * scale,
                (float)x_frme * scale,
                (float)y_frme * scale,
                (float)z_frme * scale);
    
    cpu->u_sav = cpu->u;
    cpu->s_sav = cpu->s;
    cpu->n_sav = cpu->n;
    cpu->i_sav = cpu->i;
    cpu->w_sav = cpu->w;
    cpu->x_sav = cpu->x;
    cpu->y_sav = cpu->y;
    cpu->z_sav = cpu->z;

#undef TRIMz
}

int main(){

    cpu_t * cpu;
    cpu =(cpu_t *)malloc(sizeof(cpu_t));
    memset(cpu,0,sizeof(cpu_t));


    while(1){
        cpus_refresh(cpu);
        format_output(cpu,"Cpu usage:  ");
        sleep(1);
    }

}

需要注意的是,无论用什么方法,我们都无法获取到系统瞬时的CPU使用率,而只能通过近期的使用情况来推测。

在这个程序中,第一次调用cpus_refresh时返回的并不是当前的CPU使用率,而是从系统启动以来至今为止的CPU使用率,这并不能反映当前的CPU使用情况。第二次调用计算差值后得到的是这一秒内CPU的使用率,用它来度量当前瞬时的使用率还是合理的。

–EOF-

2007-09-27

nanosleep精度问题分析

最近有个同学在做流量模拟,发包时需要精确控制发包间隔,即要用到高精度的延时。

首先想到的便是nanosleep系统调用,毕竟从接口上看,它可以达到纳秒级的精度(1*10^-9)。

然而实际上nanosleep的解析度远没有达到预期,把时间间隔设地再小,精度也始终只有一微秒左右。

WHY?

READ THE FUCKING MANUAM

原来,2.6内核中nanosleep实现基于内核中的普通定时器机制,最大解析度也只有1/HZs,所以nanpsleep暂停的最小单位即是1ms,考虑到进程的切换与函数调用消耗的时间,实际的最小单位可能会在10ms左右甚至更长。

如果是这样,那为何内核还要提供这样一个名不副实的函数呢?
原来在2.4内核中,当把进程调度策略设置成实时级别的,诸如SCHED_FIFO或SCHED_RR,则对于较小的延迟时间,内核将采用忙等的方式进行计时处理。由此提供的解析精度可以达到2ms以下。

尝试了一下,实际解析度的确升高了,效果不错。

#include   <stdio.h>
#include   <time.h>
#include   <sys/time.h>
#include   <sys/types.h>
#include   <unistd.h>
#include   <sched.h>

#define BILLION  1000000L

void do_sleep(int looptimes,int sleeptimes){
    
    int i,j;
    struct timespec tv={0,1};
    struct timeval start, stop;
    long accum;

    for(i=0;i<looptimes;i++){
        gettimeofday(&start,NULL);

        for(j=0; j<sleeptimes; j++){
            nanosleep(&tv,NULL);
        }

        gettimeofday(&stop,NULL);
        accum = ( stop.tv_sec - start.tv_sec )*BILLION + ( stop.tv_usec - start.tv_usec );

        fprintf(stderr,"%ld\t", accum/sleeptimes );
    }
    
    fprintf(stderr,"\n");
}

int main( int argc, char** argv )
{
    struct sched_param sp;
        
    sp.sched_priority=0;
    sched_setscheduler(0,SCHED_FIFO,&sp);

    fprintf(stderr,"Schedule priority: %d\n",sp.sched_priority);
        
    do_sleep(10,100);

    sp.sched_priority=99;
    sched_setscheduler(0,SCHED_FIFO,&sp);
    
    fprintf(stderr,"Schedule priority: %d\n",sp.sched_priority);
    
    do_sleep(10,100);
    
    return 0;
}

实验环境:
kernel version: Linux version 2.4.20-8smp (bhcompile@stripples.devel.redhat.com) (gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)) #1 SMP Thu Mar 13 16:43:01 EST 2003

–EOF–

2007-03-26

当signal遇上exec

一般来说,多进程环境下的Linux程序,子进程是继承父进程的信号处理方式的。也就是说,如果在父进程中为某一个信号指定了处理函数,那么子进程在收到这个信号时同样会调用这个处理函数。

举例如下:

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>

void handler(int signum,siginfo_t *info,void *myact)
{
    fprintf(stderr,"receive signal %d\n", signum);
    fprintf(stderr,"Signal is sended by %d as reason : %d on error: %d\n"
                    , info->si_pid,info->si_code,info->si_errno);
}

int main(int argc,char**argv)
{
    struct sigaction act;

    sigemptyset(&act.sa_mask);

    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=handler;

    if(sigaction(SIGUSR1,&act,NULL) < 0)
    {
        printf("install sigal error\n");
    }

    pid_t pid=fork();
    if(pid == 0)
    {
        for(int i=0;i<100;i++)
        {
            if(i==5)
            {
                fprintf(stderr,"Send SIGUSR1 signal......\n");
                kill(getpid(),SIGUSR1);
            }
            else
            {
                fprintf(stderr,"Sleeping......\n");
                sleep(1);
            }
        }
        return 0;
    }

    fprintf(stderr,"Child Process id is : %d\n",pid);

    wait(NULL);

    return 0;
}

但很多情况下需要在fork之后在子进程中调用exec族函数来执行一个新的程序。那这种情况下,父子进程对于信号处理的继承关系又是怎样的呢。

将上面的程序改动一下。

main.cpp:

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>

void handler(int signum,siginfo_t *info,void *myact)
{
    fprintf(stderr,"receive signal %d\n", signum);
    fprintf(stderr,"Signal is sended by %d as reason : %d on error: %d\n"
                    ,info->si_pid,info->si_code,info->si_errno);
}

int main(int argc,char**argv)
{
    struct sigaction act;

    sigemptyset(&act.sa_mask);

    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=handler;

    if(sigaction(SIGUSR2,&act,NULL) < 0)
    {
        printf("install sigal error\n");
    }

    pid_t pid=fork();

    if(pid == 0)
    {
        execlp("/root/test/sigusr","./sigusr",NULL);
        return 0;
    }

    fprintf(stderr,"Child Process id is : %d\n",pid);

    wait(NULL);

    return 0;
}

将原来子进程中的语句放到一个新的文件中,并将其编译成sigusr可执行文件 :

sigusr.cpp:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>

int main(){

    for(int i=0;i<100;i++)
    {
        if(i==5)
        {
            fprintf(stderr,"Send SIGUSR1 signal......\n");
            kill(getpid(),SIGUSR1);
        }
        else
        {
            fprintf(stderr,"Sleeping......\n");
            sleep(1);
        }
    }
}

编译执行main.cpp可以看到,子进程打印了五条sleeping……语句后就退出了,并没有调用父进程指定的信号处理函数。

分析:

1、fork生成的子进程中继承了父进程所有的数据,当然包括信号处理函数的入口地址等信息,所以当子进程继承父进程的信号处理方式不会出现问题。

2、fork后调用exec后发生了什么呢?

open group上的exec的文档是这么说的 :

The exec functions replace the current process image with a new process image. The new image is constructed from a regular, executable file called the new process image file. There is no return from a successful exec, because the calling process image is overlaid by the new process image.

exec 后,系统用参数指定的可执行文件的映像将原由子进程完全替换掉,只留下进程号等一小部分信息不变,这样的话在exec后子进程中已经不存在父进程中的数 据,子进程也无法得知父进程信号处理函数的入口地址。在这种情况下,继承父进程的信号处理机制也就无从谈起了,因为这是不可能的。
但有一个例外情况,那就 是如果在父进程中将对某个信号的处理方式设置为忽略(SIG_IGN),那么exec后的子进程会继承父进程的设置,在子进程中忽略这个信号。因为设置为 忽略的话就不涉及到寻找处理函数入口地址了,这是可行的。

Open Group对此的描述如下:

Signals set to the default action (SIG_DFL) in the calling process image are set to the default action in the new process image. Signals set to be ignored (SIG_IGN) by the calling process image are set to be ignored by the new process image. Signals set to be caught by the calling process image are set to the default action in the new process image. After a successful call to any of the exec functions, alternate signal stacks are not preserved and the SA_ONSTACK flag is cleared for all signals.

再引申一下,还有一种情况需要注意,如果在父进程中调用了atexit()函数,那么在exec后的子进程中同样会失效。原理和前文所述的信号设置是一样的 🙂

–EOF–

参考文档:
Open Group The Single UNIX ® Specification, Version 2,exec:
http://www.opengroup.org/onlinepubs/007908799/xsh/exec.html