2009年12月27日 星期日

Linux Modules(7.2)- tasklet


Tasklet和timer類似(基本上都是運作在Softirqs上面),但是不同於timer會在特定時間執行,tasklet會在下一次interrupt來臨時執行。Tasklet有兩種implement,分別為TASKLET_SOFTIRQ和HI_SOFTIRQ,這兩種的差別在於HI_SOFTIRQ筆TASKLET_SOFTIRQ早執行。另外Tasklet只在註冊的CPU上面執行,而且註冊的tasklet同一時間只會被某個CPU執行。

您可以dynamically或statically的建立tasklet,
DECLARE_TASKLET(task, func, data);
DECLARE_TASKLET_DISABLED(task, func, data);

tasklet_init(task, func, data);

宣告後,還必須呼叫tasklet_schedule(task)才會被執行,但如果是用
DECLARE_TASKLET_DISABLED()宣告成disabled狀態,那就還必須用tasklet_enable()將其狀態設成enabled才能被執行。您也可以透過tasklet_disabled() disabled某個tasklet。tasklet_kill()可以保證tasklet不會被schedule,如果已經在執行,就會等它執行結束。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>

MODULE_LICENSE("GPL");

static void f(unsigned long name);

// create tasklet statically
static DECLARE_TASKLET(t1, f, (unsigned long)"t1");
static DECLARE_TASKLET_DISABLED(t2, f, (unsigned long)"t2");

static struct tasklet_struct *t3;

static void f(unsigned long name)
{
    printk("%s(): on cpu %d\n", (char*)name, smp_processor_id());
}

static void f3(unsigned long name)
{
    static u32 c = 0;
    tasklet_schedule(t3);
    if (!(c++ % 2000000)) { // 每隔2000000次呼叫就印出訊息
        printk("%s(): on cpu %d\n", (char*)name, smp_processor_id());
    }
}

static int __init init_modules(void)
{
    // create tasklet dynamically
    t3 = kzalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
    tasklet_init(t3, f3, (unsigned long)"t3");

    tasklet_schedule(&t1);
    tasklet_schedule(&t2);
    tasklet_schedule(t3);
    tasklet_enable(&t2); // 沒有enable就不會被啟動
    return 0;
}

static void __exit exit_modules(void)
{
    // remove module就應該要確保tasklet有被移除
    tasklet_kill(&t1);
    tasklet_kill(&t2);
    tasklet_kill(t3);
}

module_init(init_modules);
module_exit(exit_modules);


Based on Kernel Version:2.6.35

參考資料:
Linux Kernel Development 3rd.
Linux Device Driver 3rd, http://www.makelinux.net/ldd3/chp-7-sect-5.shtml



Linux Kernel(7.1)- timer


有時候我們希望能在某個時間點執行某些動作,這時候便可以使用timer,在使用timer有些規矩必須被遵守。因為不是user-space來喚起,所以不允許存取user-space,current也就沒有意義。不能休眠,也不准schedule()或者任何有可能休眠的動作都不准。
struct timer_list {
 struct list_head entry;
 unsigned long expires;

 void (*function)(unsigned long);
 unsigned long data;

 struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
 void *start_site;
 char start_comm[16];
 int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
 struct lockdep_map lockdep_map;
#endif
};

timer_list必須初始化之後才能使用,您可以選擇init_timer()或TIMER_INITIALIZER(),接著就可以設定expires/callback function/data(參數),並且使用add_timer()將其加入timer中,或者使用del_timer()移除pending中的timer,也可以使用mod_timer()修改或者重新設定timer。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");

struct timer_list brook_timer;
static void callback(unsigned long);
struct data {
    int count;
};
static struct data data;

static void callback(unsigned long data)
{
    struct data *dp = (struct data*) data;
    printk("%s(): %d\n", __FUNCTION__, dp->count++);
    mod_timer(&brook_timer, jiffies + 5 * HZ);
}

static int __init init_modules(void)
{
    init_timer(&brook_timer);
    brook_timer.expires = jiffies + 5 * HZ;
    brook_timer.function = &callback;
    brook_timer.data = (unsigned long) &data;
    add_timer(&brook_timer);
    return 0;
}

static void __exit exit_modules(void)
{
    del_timer(&brook_timer);
}

module_init(init_modules);
module_exit(exit_modules);


kernel timer最短的間隔是1個jiffies,而且會受到硬體中斷,和其他非同步事件的干擾,所以不適合非常精密的應用。

Linux Kernel(7)- timing


kernel會定期產生timer interrupt,HZ定義每秒產生timer interrupt的次數,定義在linux/param.h,根據平台的不同從50~1200不等。
而jiffies每當發生一次timer interrupt就會遞增一次,jiffies定義於linux/jiffies.h,所以簡單的說,jiffies就等於1/HZ,不管在64bit或32bit上的機器,Linux kernel都使用64位元版的jiffies_64,而jiffies其實是jiffies_64的低32位元版,除了讀取外,我們都不應該直接修改jiffies/jiffies_64。
kernel提供幾組macro來比較時間的先後,time_after()/timer_before()/time_after_eq()/time_before_eq()。
/*
 * These inlines deal with timer wrapping correctly. You are 
 * strongly encouraged to use them
 * 1. Because people otherwise forget
 * 2. Because if the timer wrap changes in future you won't have to
 *    alter your driver code.
 *
 * time_after(a,b) returns true if the time a is after time b.
 *
 * Do this with "<0" and ">=0" to only test the sign of the result. A
 * good compiler would generate better code (and a really good compiler
 * wouldn't care). Gcc is currently neither.
 */
#define time_after(a,b)  \
 (typecheck(unsigned long, a) && \
  typecheck(unsigned long, b) && \
  ((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)

#define time_after_eq(a,b) \
 (typecheck(unsigned long, a) && \
  typecheck(unsigned long, b) && \
  ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)

另外,kernel中有兩種時間的structure,struct timeval和struct timespec。
#ifndef _STRUCT_TIMESPEC
#define _STRUCT_TIMESPEC
struct timespec {
 __kernel_time_t tv_sec;   /* seconds */
 long  tv_nsec;  /* nanoseconds */
};
#endif

struct timeval {
 __kernel_time_t  tv_sec;  /* seconds */
 __kernel_suseconds_t tv_usec; /* microseconds */
};
早期以timeval為主,後來因為精密度的需求,有了timespec的誕生。kernel也提供了和jiffies的轉換函數。更多的轉換可以參考linux/jiffies.h
unsigned long timespec_to_jiffies(const struct timespec *value);
void jiffies_to_timespec(const unsigned long jiffies,
    struct timespec *value);
unsigned long timeval_to_jiffies(const struct timeval *value);
void jiffies_to_timeval(const unsigned long jiffies,
          struct timeval *value);


Linux Kernel(3.1)- procfs之vector方式寫入


相信很多人有讀寫過/proc/sys/kernel/printk來控制printk的level,於是乎我就仿照了kernel/sysctl.c的do_proc_dointvec()寫了一個這樣的code,我的write_proc_t就是在做do_proc_dointvec()當中的write。kernel因為沒有豐富的library,所以作這樣的事情得小繞一下。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>

MODULE_LICENSE("GPL");

static int int_vec[] = {1, 1, 1};

static int write_proc(struct file *file, const char __user *buf,
                       unsigned long count, void *data)
{
    int *i, vleft, neg, left = count;
    char __user *s = buf;
    char tmpbuf[128], *p;
    size_t len;
    unsigned long ulval;

    i = int_vec;
    vleft = sizeof(int_vec)/sizeof(int_vec[0]);

    for(;left && vleft--; i++) {
        while(left) {
            char c;
            if (get_user(c, s)) {
                return -EFAULT;
            }
            if (!isspace(c)) {
                break;
            }
            left--;
            s++;
        }
        if (!left) {
            break;
        }
        neg = 0;
        len = left;
        if (len > sizeof(tmpbuf) - 1) {
            len = sizeof(tmpbuf) - 1;
        }
        if (copy_from_user(tmpbuf, s, len)) {
            return -EFAULT;
        }
        tmpbuf[len] = 0;
        p = tmpbuf;
        if (*p == '-' && left > 1) {
            neg = 1;
            p++;
        }
        if (*p < '0' || *p > '9') {
            break;
        }
        ulval = simple_strtoul(p, &p, 0);
        len = p - tmpbuf;
        if ((len < left) && *p && !isspace(*p)) {
            break;
        }
        *i = neg ? -ulval : ulval;
        s += len;
        left -= len;
    }
    return count;
}

static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    int *i, vleft;
    char *p;

    i = (int *) int_vec;
    vleft = sizeof(int_vec)/sizeof(int_vec[0]);

    for (p = page, i = int_vec; vleft--; i++) {
        p += sprintf(p, "%d\t", *i);
    }
    *(p++) = '\n';
    *eof = 1;
    return (p - page);
}

static int __init init_modules(void)
{

    struct proc_dir_entry *ent;

    ent = create_proc_entry("brook_vec", S_IFREG | S_IRWXU, NULL);
    if (!ent) {
        printk("create proc child failed\n");
    } else {
        ent->write_proc = write_proc;
        ent->read_proc = read_proc;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    remove_proc_entry("brook_vec", NULL);
}

module_init(init_modules);
module_exit(exit_modules);



2009年12月26日 星期六

kernel探索之linux/typecheck.h


#ifndef TYPECHECK_H_INCLUDED
#define TYPECHECK_H_INCLUDED

/*
 * Check at compile time that something is of a particular type.
 * Always evaluates to 1 so you may use it easily in comparisons.
 */
#define typecheck(type,x) \
({ type __dummy; \
 typeof(x) __dummy2; \
 (void)(&__dummy == &__dummy2); \
 1; \
})

/*
 * Check at compile time that 'function' is a certain type, or is a pointer
 * to that type (needs to use typedef for the function type.)
 */
#define typecheck_fn(type,function) \
({ typeof(type) __tmp = function; \
 (void)__tmp; \
})

#endif  /* TYPECHECK_H_INCLUDED */

typecheck(type, x)用於檢查x是不是"type"的資料型態,如果不是,compiler會出現warning: comparison of distinct pointer types lacks a cast提醒programmer。

typecheck_fn(type, function)用於檢查"function"是不是和"type"有相同的資料型態,如果不是,compiler會出現warning: initialization from incompatible pointer type提醒programmer。


2009年12月24日 星期四

Linux Kernel(6)- miscdev


有時候我們需要寫一些"小的驅動程式",而早期的UNIX/Linux需要註冊major/minor number,即便可能只需要1個minor number,往往卻佔住major number底下的所有minor number,於是在Linux 2.0有了miscellaneous character drivers的誕生,misc driver使用major number 10,然後使用者如果需要這樣的"小驅動程式",便可以指明minor number即可。

使用misc device必須include <linux/miscdevice.h>,裡面包含了許多的官方的minor number,您可以挑選您適合的minor number,裡面也包含了兩個API,misc_register()/misc_deregister()。
一般您只要填好struct miscdevice的內容,再使用misc_register()進行註冊,或者使用misc_deregister()進行移除即可。
struct miscdevice  {
 int minor;
 const char *name;
 const struct file_operations *fops;
 struct list_head list;
 struct device *parent;
 struct device *this_device;
 const char *devnode;
};
 minor:就是您要註冊的minor number。
 name:device的name。
 fops:file operations。
 其他的就不用理會了。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
MODULE_LICENSE("GPL");

#define DEV_BUFSIZE 1024

static int dev_open(struct inode*, struct file*);
static int dev_release(struct inode*, struct file*);
static ssize_t dev_read(struct file*, char __user*, size_t, loff_t*);
static ssize_t dev_write(struct file*, const char __user *, size_t, loff_t*);
static void __exit exit_modules(void);

static struct file_operations dev_fops = {
    .owner = THIS_MODULE,
    .open = dev_open,
    .release = dev_release,
    .read = dev_read,
    .write = dev_write,
};

static struct miscdevice brook_miscdev = {
    .minor      = 11,
    .name       = "brook_dev",
    .fops       = &dev_fops,
};

static int
dev_open(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

static int
dev_release(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

static ssize_t
dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    printk("%s():\n", __FUNCTION__);
    *pos = 0;
    return 0;
}

static ssize_t
dev_write(struct file *filp, const char __user *buf,
        size_t count, loff_t *pos)
{
    printk("%s():\n", __FUNCTION__);
    return count;
}

static int __init init_modules(void)
{
    int ret;

    ret = misc_register(&brook_miscdev);
    if (ret != 0) {
        printk("cannot register miscdev on minor=11 (err=%d)\n",ret);
    }

    return 0;
}

static void __exit exit_modules(void)
{
    misc_deregister(&brook_miscdev);
}

module_init(init_modules);
module_exit(exit_modules);



2009年12月23日 星期三

vbindiff - Visual Binary Diff


VBinDiff是一套視窗化的二進位diff工具,可以將檔案內容以16和ASCII方式顯示(和早期的PCTOOL感覺很像)。


官方網站:
http://www.cjmweb.net/vbindiff/


2009年12月20日 星期日

Linux Kernel(4.2)- seq_file之single page


對於只有一頁的輸出,seq_file中的sart()/next()/stop()就顯得多餘,通常指需要一個show(),所以,seq_file也提供單頁的版本single_open(),以下範例為fs/proc/cmdline.c:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static int cmdline_proc_show(struct seq_file *m, void *v)
{
 seq_printf(m, "%s\n", saved_command_line);
 return 0;
}

static int cmdline_proc_open(struct inode *inode, struct file *file)
{
 return single_open(file, cmdline_proc_show, NULL);
}

static const struct file_operations cmdline_proc_fops = {
 .open  = cmdline_proc_open,
 .read  = seq_read,
 .llseek  = seq_lseek,
 .release = single_release,
};

static int __init proc_cmdline_init(void)
{
 proc_create("cmdline", 0, NULL, &cmdline_proc_fops);
 return 0;
}
module_init(proc_cmdline_init);
和seq_file的不同在於,因為只有一頁所以不需要sart()/next()/stop(),就只剩下show(),再來就是要用single_open()取代seq_open()。而relese也是要使用single_release()取代seq_release()。


2009年12月16日 星期三

Linux Kernel(5)- ioctl


(V)將介紹file operations中的ioctl。ioctl的prototype為:
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
ioctl藉由cmd來判斷後面所接的參數為何,而早期的ioctl號碼並沒有規則,所以很容易重複,後來為了避免重複,採行編碼方式,將cmd拆成幾個部份,包含:
type
  即magic number,可以根據Document/ioctl/ioctl-number.txt挑選一個。
number
  為sequential number或者稱為ordinal number,讓user自行定義,只要自己不重複即可。
direction
  傳輸的方向,不外乎NONOE/READ/WRITE等等。
size
  即參數的size。

因為ioctl藉由cmd來判斷user想要的指令為何,以及後面所帶的參數為何,所以免不了的就會有一個switch/case來判斷,這也算是ioctl的特色吧。
怎麼定義ioctl的command以及如何解譯ioctl的command,我想直接拿ioctl.h來說明。
#define _IOC(dir,type,nr,size) \
         (((dir)  << _IOC_DIRSHIFT) | \
         ((type) << _IOC_TYPESHIFT) | \
         ((nr)   << _IOC_NRSHIFT) | \
         ((size) << _IOC_SIZESHIFT))

/* used to create numbers */
#define _IO(type,nr)            _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)      _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)      _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)     _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOR_BAD(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)            (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)           (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)             (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)           (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
在定義ioctl的command時,我們會根據資料傳輸的方向使用_IO(不需要傳輸資料)/_IOR(讀取)/_IOW(寫入)/_IOWR(讀寫),type是我們挑選的magic number,nr即number,是流水號,size,就是the size of argument,下面是我們的範例brook_ioctl.h:

#ifndef IOC_BROOK_H
#define IOC_BROOK_H

#define BROOK_IOC_MAGIC     'k'
#define BROOK_IOCSETNUM     _IOW(BROOK_IOC_MAGIC,  1, int)
#define BROOK_IOCGETNUM     _IOR(BROOK_IOC_MAGIC,  2, int)
#define BROOK_IOCXNUM       _IOWR(BROOK_IOC_MAGIC, 3, int)
#define BROOK_IOC_MAXNR     3

#endif
這邊定義三個ioctl的command,分別為設定數值(BROOK_IOCSETNUM),取得數值(BROOK_IOCGETNUM)和交換數值(BROOK_IOCXNUM)。

以下是我的module:
#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h> // chrdev
#include <linux/cdev.h> // cdev_add()/cdev_del()
#include <linux/semaphore.h> // up()/down_interruptible()
#include <asm/uaccess.h> // copy_*_user()

#include "ioc_brook.h"

MODULE_LICENSE("GPL");

#define DEV_BUFSIZE         1024


static int dev_major;
static int dev_minor;
struct cdev *dev_cdevp = NULL;

static int 
  dev_open(struct inode*, struct file*);
static int 
  dev_release(struct inode*, struct file*);
static int
  dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long);

static void __exit exit_modules(void);

struct file_operations dev_fops = {
    .owner   = THIS_MODULE,
    .open    = dev_open,
    .release = dev_release,
    .ioctl   = dev_ioctl
};

static int dev_open(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

static int dev_release(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

static int brook_num = 0;
static int 
dev_ioctl(struct inode *inode, struct file *filp,
          unsigned int cmd, unsigned long args)
{
    int tmp, err = 0, ret = 0;

    if (_IOC_TYPE(cmd) != BROOK_IOC_MAGIC)
        return -ENOTTY;
    if (_IOC_NR(cmd) > BROOK_IOC_MAXNR)
        return -ENOTTY;

    if (_IOC_DIR(cmd) & _IOC_READ) {
        err = !access_ok(VERIFY_WRITE, (void __user*)args, _IOC_SIZE(cmd));
    } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
        err = !access_ok(VERIFY_READ, (void __user *)args, _IOC_SIZE(cmd));
    }
    if (err)
        return -EFAULT;

    switch (cmd) {
        case BROOK_IOCSETNUM:
            // don't need call access_ok() again. using __get_user().
            ret = __get_user(brook_num, (int __user *)args); 
            printk("%s(): get val = %d\n", __FUNCTION__, brook_num);
            break;
        case BROOK_IOCGETNUM:
            ret = __put_user(brook_num, (int __user *)args);
            printk("%s(): set val to %d\n", __FUNCTION__, brook_num);
            break;
        case BROOK_IOCXNUM:
            tmp = brook_num;
            ret = __get_user(brook_num, (int __user *)args);
            if (!ret) {
                ret = __put_user(tmp, (int __user *)args);
            }
            printk("%s(): change val from %d to %d\n",
                       __FUNCTION__, tmp, brook_num);
            break;
        default: /* redundant, as cmd was checked against MAXNR */
            return -ENOTTY;
    }
    return 0;
}

static int __init init_modules(void)
{
    dev_t dev;
    int ret;

    ret = alloc_chrdev_region(&dev, 0, 1, "brook");
    if (ret < 0) {
        printk("can't alloc chrdev\n");
        return ret;
    }
    dev_major = MAJOR(dev);
    dev_minor = MINOR(dev);
    printk("register chrdev(%d,%d)\n", dev_major, dev_minor);

    dev_cdevp = kmalloc(sizeof(struct cdev), GFP_KERNEL);
    if (dev_cdevp == NULL) {
        printk("kmalloc failed\n");
        goto failed;
    }
    cdev_init(dev_cdevp, &dev_fops);
    dev_cdevp->owner = THIS_MODULE;
    ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor), 1);
    if (ret < 0) {
        printk("add chr dev failed\n");
        goto failed;
    }

    return 0;

failed:
    if (dev_cdevp) {
        kfree(dev_cdevp);
        dev_cdevp = NULL;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    dev_t dev;

    dev = MKDEV(dev_major, dev_minor);
    if (dev_cdevp) {
        cdev_del(dev_cdevp);
        kfree(dev_cdevp);
    }
    unregister_chrdev_region(dev, 1);
    printk("unregister chrdev\n");
}

module_init(init_modules);
module_exit(exit_modules);

在dev_ioctl()先檢視command的type(magic number)和number(sequential number)是否正確,接著在根據command的read/write特性,使用access_ok()檢驗該位址是否合法,後面就是ioctl慣有的switch/case了,根據不同的case執行不同的command和解釋後面所攜帶的參數。

底下是我的application:
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#include <sys/stat.h>
#include <fcntl.h>

#include "ioc_brook.h"

int main(int argc, char *argv[])
{
    int fd, ret;

    if (argc < 2) {
        printf("Usage: prog \n");
        return -1;
    }

    fd = open(argv[1], O_RDWR);
    if (fd < 0) {
        printf("open %s failed\n", argv[1]);
        return -1;
    }

    ret = 10;
    if (ioctl(fd, BROOK_IOCSETNUM, &ret) < 0) {
        printf("set num failed\n");
        return -1;
    }

    if (ioctl(fd, BROOK_IOCGETNUM, &ret) < 0) {
        printf("get num failed\n");
        return -1;
    }
    printf("get value = %d\n", ret);

    ret = 100;
    if (ioctl(fd, BROOK_IOCXNUM, &ret) < 0) {
        printf("exchange num failed\n");
        return -1;
    }
    printf("get value = %d\n", ret);

    return 0;
}

在app.c中,將開啟上面註冊的device,並且設定數值(BROOK_IOCSETNUM),讀取數值(BROOK_IOCGETNUM),和交換數值(BROOK_IOCXNUM)。



2009年12月15日 星期二

如何利用kvm/qemu練習linux module


"如何利用kvm/qemu練習linux module"將介紹如何編譯一個bzImage在kvm上面執行,我們將掛上一個initramfs當我們的root filesystem,除了沒有實體的裝置以外,其實可以看成一個embedded linux了。我也是利用這種方式撰寫module的文章,因為module一寫不好,kernel就會crash了,透過vm,就不怕kernel crash了。
編譯kernel
brook@ubuntu:~$ mkdir linux
brook@ubuntu:~$ cd linux/
brook@ubuntu:~/linux$ apt-get source linux-image-2.6.31-16-generic
Reading package lists... Done
Building dependency tree       
Reading state information... Done
NOTICE: 'linux' packaging is maintained in the 'Git' version control system at:
http://kernel.ubuntu.com/git-repos/ubuntu/ubuntu-karmic.git
Need to get 81.0MB of source archives.
Get:1 http://tw.archive.ubuntu.com karmic-updates/main linux 2.6.31-16.53 (dsc) [3,781B]
0% [Waiting for headers]    
Fetched 81.0MB in 3min 29s (387kB/s)
gpgv: Signature made Tue 08 Dec 2009 11:50:10 AM CST using DSA key ID 17063E6D
gpgv: Can't check signature: public key not found
dpkg-source: warning: failed to verify signature on ./linux_2.6.31-16.53.dsc
dpkg-source: info: extracting linux in linux-2.6.31
dpkg-source: info: unpacking linux_2.6.31.orig.tar.gz
dpkg-source: info: applying linux_2.6.31-16.53.diff.gz
brook@ubuntu:~/linux$ cd linux-2.6.31
brook@ubuntu:~/linux/linux-2.6.31$ cp /boot/config-2.6.31-16-generic .config
brook@ubuntu:~/linux/linux-2.6.31$ make oldconfig
brook@ubuntu:~/linux/linux-2.6.31$ make menuconfig

接下來要設定我們的initramfs的目錄,先make menuconfig,接著選"General setup" -> "Initramfs source file(s)",接著輸入目錄"/home/brook/linux/rootfs",注意該選項的上一個選項"Initial RAM ilesystem and RAM disk (initramfs/initrd) support"有被enabled。如果要使用的是initrd則"Initramfs source file(s)"就留空白,使用initrd的好處是,initrd有任何改變都不需要重新compile kernel。

rootfs的基本設定
brook@ubuntu:~/linux$ mkdir rootfs
brook@ubuntu:~/linux$ cd rootfs
brook@ubuntu:~/linux/rootfs$ mkdir dev
brook@ubuntu:~/linux/rootfs$ mkdir tmp
brook@ubuntu:~/linux/rootfs$ mkdir bin
brook@ubuntu:~/linux/rootfs$ mkdir sbin
brook@ubuntu:~/linux/rootfs$ mkdir etc
brook@ubuntu:~/linux/rootfs$ mkdir lib
brook@ubuntu:~/linux/rootfs$ mkdir proc
brook@ubuntu:~/linux/rootfs$ mkdir sys
brook@ubuntu:~/linux/rootfs$ mkdir usr
brook@ubuntu:~/linux/rootfs$ apt-get install busybox-static
brook@ubuntu:~/linux/rootfs$ cp /bin/busybox bin
brook@ubuntu:~/linux/rootfs$ cd bin
brook@ubuntu:~/linux/rootfs/bin$ ln -s busybox -s mkdir
brook@ubuntu:~/linux/rootfs/bin$ ln -s busybox -s mknod
brook@ubuntu:~/linux/rootfs/bin$ ln -s busybox -s mount
brook@ubuntu:~/linux/rootfs/bin$ ln -s busybox -s rm
brook@ubuntu:~/linux/rootfs/bin$ ln -s busybox -s sh
brook@ubuntu:~/linux/rootfs/bin$ cd ../sbin
brook@ubuntu:~/linux/rootfs/sbin$ ln -s ../busybox -s init
brook@ubuntu:~/linux/rootfs/sbin$ ln -s ../busybox -s mdev
brook@ubuntu:~/linux/rootfs/$ find . |cpio -H newc -o > ../initrd

如果使用的是initrd則需要用cpio壓縮這個目錄,如果是initramfs就不用最後一個指令了。

接著當然還要寫一下init script,負責開機後的一些基本設定,其內容如下
#!/bin/sh
#Mount things needed by this script
mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
/bin/mount -t proc proc /proc
/bin/mount -t sysfs sysfs /sys

#Create all the symlinks to /bin/busybox
/bin/busybox --install -s

#Create device nodes
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s

exec /sbin/init
最後make bzImage即可。

接著執行kvm/qemu吧
brook@ubuntu:~/linux/linux-2.6.31$ kvm -no-acpi -kernel arch/x86_64/boot/bzImage -net nic,model=pcnet -net tap,ifname=tap0,script=no

如果是initrd則需要多一個參數給kvm/qemu。
brook@ubuntu:~/linux/linux-2.6.31$ kvm -no-acpi -kernel arch/x86_64/boot/bzImage -initrd /home/brook/initrd -net nic,model=pcnet -net tap,ifname=tap0,script=no



2009年12月13日 星期日

Linux Kernel(4.1)- seq_file之範例(fp/proc/devices.c)


(IV .1)是seq_file的實例說明,將Linux中的fp/proc/devices.c拿出來當範例並且予以說明。
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static int devinfo_show(struct seq_file *f, void *v)
{
    int i = *(loff_t *) v;

    if (i < CHRDEV_MAJOR_HASH_SIZE) {
        if (i == 0)
            seq_printf(f, "Character devices:\n");
        chrdev_show(f, i);
    }
#ifdef CONFIG_BLOCK
    else {
        i -= CHRDEV_MAJOR_HASH_SIZE;
        if (i == 0)
            seq_printf(f, "\nBlock devices:\n");
        blkdev_show(f, i);
    }
#endif
    return 0;
}


static void *devinfo_start(struct seq_file *f, loff_t *pos)
{
    if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
        return pos;
    return NULL;
}

static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
{
    (*pos)++;
    if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
        return NULL;
    return pos;
}

static void devinfo_stop(struct seq_file *f, void *v)
{
    /* Nothing to do */
}

static const struct seq_operations devinfo_ops = {
    .start = devinfo_start,
    .next  = devinfo_next,
    .stop  = devinfo_stop,
    .show  = devinfo_show
};

static int devinfo_open(struct inode *inode, struct file *filp)
{
    return seq_open(filp, &devinfo_ops);
}

static const struct file_operations proc_devinfo_operations = {
    .open  = devinfo_open,
    .read  = seq_read,
    .llseek  = seq_lseek,
    .release = seq_release,
};

static int __init proc_devices_init(void)
{
    proc_create("devices", 0, NULL, &proc_devinfo_operations);
    return 0;
}
module_init(proc_devices_init);
首先,這邊只有module_init(),所以只能載入,不能unload。而載入的點就是create一個proc檔 "devices",並且註冊其file operations "proc_devinfo_operations",在"proc_devinfo_operations"可以發現是一個seq_file的架構,所以我們就會想到start()/next()/stop()/show()等function應該負責的功能。
首先會看到start()即"devinfo_start()",可以看出pos代表的是第幾個device,而pos最大為block+char的總和。
"devinfo_next()"應該負責移動pos,所以可以看出只有做了(*pos)++。
由於"devinfo_start()"並沒有和系統要求任何的resource,所以"devinfo_stop()"就不需要有任何cleanup的動作。
而"devinfo_show()"則是各別呼叫"chrdev_show()"和"blkdev_show()"來顯示char device和block device。



2009年12月6日 星期日

telnet之中文(ubuntu)


今天用ubuntu的"Terminal"上ptt,卻出現亂碼,原來是"Character Encoding"預設是"UTF-8",將其改成"BIG5"就OK啦,post文章中文也不成問題了。




2009年12月5日 星期六

Linux Kernel(4)- seq_file


對於如何維護procfs中的position是有些困擾的(超過一頁的顯示時),所以後來有了seq_file,提供了iterator的interface來讀取資料,最大的好處是,position的定義就可以由programmer自行決定,比如position N是讀取第N行之類的。我們將改寫Linux Modules(III)- procfs的範例一來作為我們seq_file的例子。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define MAX_LINE        1000
static uint32_t *lines;

/**
 * seq_start() takes a position as an argument and returns an iterator which
 * will start reading at that position.
 */
static void* seq_start(struct seq_file *s, loff_t *pos)
{
    uint32_t *lines;

    if (*pos >= MAX_LINE) {
        return NULL; // no more data to read
    }

    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        return NULL;
    }

    *lines = *pos + 1;

    return lines;
}

/**
 * move the iterator forward to the next position in the sequence
 */
static void* seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    uint32_t *lines = v;
    *pos = ++(*lines);
    if (*pos >= MAX_LINE) {
        return NULL; // no more data to read
    }
    return lines;
}

/**
 * stop() is called when iteration is complete (clean up)
 */
static void seq_stop(struct seq_file *s, void *v)
{
    kfree(v);
}

/**
 * success return 0, otherwise return error code
 */
static int seq_show(struct seq_file *s, void *v)
{
    seq_printf(s, "Line #%d: This is Brook's demo\n", *((uint32_t*)v));
    return 0;
}

static struct seq_operations seq_ops = {
    .start = seq_start,
    .next  = seq_next,
    .stop  = seq_stop,
    .show  = seq_show
};

static int proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &seq_ops);
}

static struct file_operations proc_ops = {
    .owner   = THIS_MODULE, // system
    .open    = proc_open,
    .read    = seq_read,    // system
    .llseek  = seq_lseek,   // system
    .release = seq_release  // system
};

static int __init init_modules(void)
{
    struct proc_dir_entry *ent;

    ent = create_proc_entry("brook", 0, NULL);
    if (ent) {
        ent->proc_fops = &proc_ops;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

首先,我們要實作start(),其主要功能就是回傳一個指向要讀取位置(position)的iterator。在我們的範例中,由於position是由0開始,而我們打算由line 1開始列印。
void * (*start) (struct seq_file *m, loff_t *pos);

接著我們要實作next(),主要功能為移動iterator到下一個位置去。在我們的範例中的iterator是指第N行,所以一到下一個iteraot就是把行數加一。
void * (*next) (struct seq_file *m, void *v, loff_t *pos);

最後要實作stop(),主要功能為當完成讀取後要執行的function,簡單來說就是cleanup。在我們的範例中,就是釋放start()所要求的resource。
void (*stop) (struct seq_file *m, void *v);

顯示的callback function為show(),要注意的是,在顯示資料的function必須使用seq_printf(),參數和printk()一樣,這樣就完成所以的function的實做了,接下來就是和procfs綁在一起了。
int (*show) (struct seq_file *m, void *v);

首先我們必須宣告struct seq_operations,並且填入我們剛剛實作的seq operations。
static struct seq_operations seq_ops = {
    .start = seq_start,
    .next  = seq_next,
    .stop  = seq_stop,
    .show  = seq_show
};

並且呼叫seq_open()將file和seq_operatio綁在一起。
static int proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &seq_ops);
}

最後在填入file_operation,由於我們使用的是seq_file,所以,read()/llseek()/release()都只要使用seq_file提供的operations即可。
static struct file_operations proc_ops = {
    .owner   = THIS_MODULE, // system
    .open    = proc_open,
    .read    = seq_read,    // system
    .llseek  = seq_lseek,   // system
    .release = seq_release  // system
};



2009年11月26日 星期四

Linux Kernel(3)- procfs


(III)將和大家介紹procfs,雖然後來procfs已經失控,也不再建議大家使用,但是還是有一定的用途,所以,有興趣的人可以一起探討一下。

/proc是一個特殊的檔案系統,當讀取/proc底下的檔案時,其內容由kernel動態產生,很多應用程式也都是存取/proc底下的檔案,我們藉由兩個範例來說明。第一個用於當/proc只提供read-only的時候,第二個用於/proc提供具有write功能的時候。

Read-Only

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>

MODULE_LICENSE("GPL");

#define MAX_LINE        1000

/* read file operations */
static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    uint32_t curline;
    char *p, *np;

    *start = p = np = page;
    curline = *(uint32_t*) data;

    printk("count=%d, off=%ld\n", count, off);
    for (;curline < MAX_LINE && (p - page) < count; curline++) {
        np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
        if ((count - (np - page)) < (np - p)) {
            break;
        }
        p = np;
    }

    if (curline < MAX_LINE) {
        *eof = 1;
    }
    *(uint32_t*)data = curline;
    return (p - page);
}

/* private data */
static uint32_t *lines;

static int __init init_modules(void)
{
    struct proc_dir_entry *ent;

    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        printk("no mem\n");
        return -ENOMEM;
    }
    *lines = 0;

    /* create a procfs entry for read-only */
    ent = create_proc_read_entry ("brook", S_IRUGO, NULL, read_proc, lines);
    if (!ent) {
        printk("create proc failed\n");
        kfree(lines);
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    /* remove procfs entry */
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

在這個例子中我們使用一個globa variable "lines",用於存放上次列印到第幾個數值,MAX_LINE是上限。一開始載入module就先分配記憶體給lines,接著呼叫create_proc_read_entry()建立一個唯讀的procfs檔案。

static inline struct proc_dir_entry *
    create_proc_read_entry(const char *name,
        mode_t mode, struct proc_dir_entry *base,
        read_proc_t *read_proc, void * data);


name是proc底下的檔案名稱。mode是檔案的mode,可以直接用0666的表示法表示,如果是建立目錄則要or S_IFDIR。base如果為null,則會把/proc當根目錄,可以是其他的proc_dir_entry pointer,那麼就會以proc_dir_entry當根目錄開始往下長。read_proc則是當user讀取該檔案時的call-back function。data則用於存放額外資訊的地方,可以先alloc,然後每次執行read時,都可以拿出來使用。

typedef int (read_proc_t)(char *page, char **start, off_t off,
    int count, int *eof, void *data);

page是系統給的buffer,也是我們要寫入的地方,寫到buffer裡面的資料,會被當成proc的內容。start如果只要一次就可以read完,可以忽略start,否則只要將page設定給start就可以了。count則是user-space想要讀取的資料量。offset指示目前檔案位置。eof指示是否已經讀到結尾。data用於存放額外的資訊。


Writable

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");

#define MAX_LINE        1000

/* write file operations */
static int write_proc(struct file *file, const char __user *buf,
                       unsigned long count, void *data)
{
    char num[10], *p;
    int nr, len;

    /* no data be written */
    if (!count) {
        printk("count is 0\n");
        return 0;
    }

    /* Input size is too large to write our buffer(num) */
    if (count > (sizeof(num) - 1)) {
        printk("input is too large\n");
        return -EINVAL;
    }

    if (copy_from_user(num, buf, count)) {
        printk("copy from user failed\n");
        return -EFAULT;
    }

    /* atoi() */
    p = num;
    len = count;
    nr = 0;
    do {
        unsigned int c = *p - '0';
        if (*p == '\n') {
            break;
        }
        if (c > 9) {
            printk("%c is not digital\n", *p);
            return -EINVAL;
        }
        nr = nr * 10 + c;
        p++;
    } while (--len);
    *(uint32_t*) data = nr;

    return count;
}

static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    uint32_t curline;
    char *p, *np;

    *start = p = np = page;
    curline = *(uint32_t*) data;

    printk("count=%d, off=%ld\n", count, off);
    for (;curline < MAX_LINE && (p - page) < count; curline++) {
        np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
        if ((count - (np - page)) < (np - p)) {
            break;
        }
        p = np;
    }

    if (curline < MAX_LINE) {
        *eof = 1;
    }
    *(uint32_t*)data = curline;
    return (p - page);
}

static uint32_t *lines;

static int __init init_modules(void)
{

    struct proc_dir_entry *ent;
    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        printk("no mem\n");
        return -ENOMEM;
    }
    *lines = 0;

    ent = create_proc_entry("brook", S_IFREG | S_IRWXU, NULL);
    if (!ent) {
        printk("create proc failed\n");
        kfree(lines);
    } else {
        ent->write_proc = write_proc;
        ent->read_proc = read_proc;
        ent->data = lines;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

第二個範例則提供了write的功能,所以不能直接使用create_proc_read_entry()建立procfs檔案,而是要使用create_proc_entry()建立procfs檔案,並且設定read/write file operations,以及"data"。該範例中的write是設定line。

typedef int (write_proc_t)(struct file *file, const char __user *buffer,
    unsigned long count, void *data);




2009年11月25日 星期三

Linux Kernel(2)- register char device


II我們將介紹如何向Linux註冊char device,並且read/write該device。
#include <linux/init.h>
#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h> // chrdev
#include <linux/cdev.h> // cdev_add()/cdev_del()
#include <asm/uaccess.h> // copy_*_user()

MODULE_LICENSE("GPL");

#define DEV_BUFSIZE 1024

int dev_major;
int dev_minor;
struct cdev *dev_cdevp = NULL;

int dev_open(struct inode*, struct file*);
int dev_release(struct inode*, struct file*);
ssize_t dev_read(struct file*, char __user*, size_t, loff_t*);
ssize_t dev_write(struct file*, const char __user *, size_t, loff_t*);
static void __exit exit_modules(void);

struct file_operations dev_fops = {
    .owner = THIS_MODULE,
    .open = dev_open,
    .release = dev_release,
    .read = dev_read,
    .write = dev_write,
};

int dev_open(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

int dev_release(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

ssize_t 
dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    char data[] = "brook";
    ssize_t ret = 0;

    printk("%s():\n", __FUNCTION__);

    if (*f_pos >= sizeof(data)) {
        goto out;
    }

    if (count > sizeof(data)) {
        count = sizeof(data);
    }

    if (copy_to_user(buf, data, count) < 0) {
        ret = -EFAULT;
        goto out;
    }
    *f_pos += count;
    ret = count;

out:
    return ret;
}

ssize_t 
dev_write(struct file *filp, const char __user *buf, size_t count,
          loff_t *f_pos)
{
    char *data;
    ssize_t ret = 0;

    printk("%s():\n", __FUNCTION__);

    data = kzalloc(sizeof(char) * DEV_BUFSIZE, GFP_KERNEL);
    if (!data) {
        return -ENOMEM;
    }

    if (count > DEV_BUFSIZE) {
        count = DEV_BUFSIZE;
    }

    if (copy_from_user(data, buf, count) < 0) {
        ret = -EFAULT;
        goto out;
    }
    printk("%s(): %s\n", __FUNCTION__, data);
    *f_pos += count;
    ret = count;
out:
    kfree(data);
    return ret;
}

static int __init init_modules(void)
{
    dev_t dev;
    int ret;

    ret = alloc_chrdev_region(&dev, 0, 1, "brook");
    if (ret) {
        printk("can't alloc chrdev\n");
        return ret;
    }

    dev_major = MAJOR(dev);
    dev_minor = MINOR(dev);
    printk("register chrdev(%d,%d)\n", dev_major, dev_minor);

    dev_cdevp = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    if (dev_cdevp == NULL) {
        printk("kzalloc failed\n");
        goto failed;
    }
    cdev_init(dev_cdevp, &dev_fops);
    dev_cdevp->owner = THIS_MODULE;
    ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor), 1);
    if (ret < 0) {
        printk("add chr dev failed\n");
        goto failed;
    }

    return 0;

failed:
    if (dev_cdevp) {
        kfree(dev_cdevp);
        dev_cdevp = NULL;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    dev_t dev;

    dev = MKDEV(dev_major, dev_minor);
    if (dev_cdevp) {
        cdev_del(dev_cdevp);
        kfree(dev_cdevp);
    }
    unregister_chrdev_region(dev, 1);
    printk("unregister chrdev\n");
}

module_init(init_modules);
module_exit(exit_modules);

當module被load進來時,init_modules()會被執行,而init_modules()做了幾件事情,首先向Linux要求分配char device number "alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)"。dev是分配到的dev_t(output parameter),baseminor是起始的minor number,當baseminor=0會由系統分配,否則會試圖尋找可以符合baseminor的minor number,count是minor的數量,name是註冊的名稱。
接著設定struct cdev "cdev_init()",並且註冊到系統"cdev_add()",在cdev_init()的同時也設定了該device的file operations。

struct file_operations就是device的file operations,簡單的說,UNIX所有的東西都可以視為檔案,當然包含device,既然是檔案,就會有open、close/release、read和write等等操作,而這些操作都會觸發對應的function來反應,這就是file operations的功能了。

我們的read會回應"brook",而write則是藉由printk()印出來,open和close/release都單純的回應成功而已。

而exit_modules()當然就是負責歸還當初和OS要的資源了"kfree()",當然包含之前註冊的char device number,現在也要unregister "unregister_chrdev_region()"。




U-Boot Standalone Applications


U-Boot支援"standlone" applications,可以動態的load並且執行。這些application可以存取U-Boot的I/O,memort和interrupt services。

在U-Boot目錄裡面有example,裡面就有"standlone" application的範例,只要確定U-Boot/Makefile的SUBDIRS有將example包含進去即可。接著我們就用loads指令將hello_world.srec載到U-Boot上,並且執行。注意:要將modem設為ASCII。

這樣可以快速的在U-Boot上發展一些應用程式。



2009年11月23日 星期一

configure bridge


最近在玩kvm/qemu,因為要設定自己的網路,所以就順便寫一下紀錄。
網路設定在Linux上都是使用/etc/network/interfaces,這邊大概沒有什麼網路設定不能設的了。 我的設定是要把TUN/TAP(tap0)加入bridge(br0)中,並且設定br0的IP,如此簡單而已,我的/etc/network/interfaces設定如下:
auto br0
iface br0 inet static
auto br0
iface br0 inet static
    pre-up brctl addbr br0
    pre-up tunctl -b -u brook -t tap0
    pre-up brctl addif br0 tap0
    pre-up ifconfig tap0 up
    post-dwon brctl delif br0 tap0
    post-down tunctl -d tap0
    post-down brctl delbr br0
    address 192.168.12.1
    netmask 255.255.255.0
    bridge_port qtap0
    bridge_fd 9
    bridge_hello 2
    bridge_maxage 12
    bridge_stp off
iface br0 inet static是說br0是static IP。
pre-up 是說在up該interface之前,先執行
post-down 是說在down該interface之後,執行
address / netmask 是設定IP資訊。
bridge_xxx 是設定bridge參數。

這樣每次開機後,就會產生tap0並且把他加入br0,以及設定好br0。



2009年11月22日 星期日

好態度能改變一切


每天都可以看到一個公益廣告,"這題你不是練好幾遍 笨得喔",換個方式說"你不笨 是這題得練好幾遍喔",其實很多人需要鼓勵,讓他能在鼓勵中成長茁壯,真得是時代在變,回想我小時候,父母用的都是打罵教育,我們也沒因此倒地不起,而且常常被教育是要越挫越勇,就像七龍珠裡面的孫悟空。
不過幾天前,某人告訴我,打罵也是教育,鼓勵也是教育,何不讓小孩在快樂的環境中長大,其實我也認同,讓小孩有正面的態度,其實並不需要打罵。


Linux Kernel(1)- Linux Module簡介


Linux module練習手札I紀錄如何撰寫一個簡單的module,並且編輯它,以及load和unload一個module。

write a module

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");

static int __init init_modules(void)
{
    printk("hello world\n");
    return 0;
}

static void __exit exit_modules(void)
{
    printk("goodbye\n");
}

module_init(init_modules);
module_exit(exit_modules);
<linux/init.h>和#include <linux/module.h>是Linux 任何的module都會用到的header file,init.h主要定義module的init和cleanup,如module_init()和module_exit()。而module.h定義了module所需要的資料結構與macro。
對於__init的解釋在init.h有非常好的解釋:
The kernel can take this as hint that the function is used only during the initialization phase and free up used memory resources after.
簡單的說就是這個function在初始化後(執行完)就被free了。

而__exit的解釋是:
__exit is used to declare a function which is only required on exit: the function will be dropped if this file is not compiled as a module.

module_init()的解釋是:
The module_init() macro defines which function is to be called at module insertion time (if the file is compiled as a module), or at boot time: if the file is not compiled as a module the module_init() macro becomes equivalent to __initcall(), which through linker magic ensures that the function is called on boot.
主要是用來設定當insert該module後,應該要被執行的進入點(enrty point)。

module_exit()的解釋是:
This macro defines the function to be called at module removal time (or never, in the case of the file compiled into the kernel). It will only be called if the module usage count has reached zero. This function can also sleep, but cannot fail: everything must be cleaned up by the time it returns.
Note that this macro is optional: if it is not present, your module will not be removable (except for 'rmmod -f').

簡言之,就是當user執行rmmod時,會被執行到的function。沒有module_exit(),module就不能被rmmod。


write a Makefile to manage the module

mname := brook_modules
$(mname)-objs := main.o
obj-m := $(mname).o

KERNELDIR := /lib/modules/`uname -r`/build

all:
        $(MAKE) -C $(KERNELDIR) M=`pwd` modules

clean:
        $(MAKE) -C $(KERNELDIR) M=`pwd` clean
$(mname)-objs是告訴make這個module有哪些object files。
obj-m是告訴make這個module的name是什麼。
KERNELDIR是告訴make這個module的kernel所在的位置。
後面就接兩個target(all/clean),用於處理產生和清除module用。


load/unload a module




2009年11月20日 星期五

衝吧ubuntu


由於現在的CPU都有動態調整CPU頻率的能力,所以,一般都會在比較低的頻率上執行,在ubuntu 9.10上,預設是ondemand,所以平常都是以最低的頻率執行,有需要的時候才會逐漸調高頻率執行,有哪些參數可以下?請參考:
brook@ubuntu:~$ more /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors 
conservative ondemand userspace powersave performance 

您可以在您的/etc/init.d底下發現一個名為ondemand的檔案,將內文中的"ondemand"改為您想要執行的governor即可,我是效能愛好者,所以,我當然是"performance"嚕。如:
#! /bin/sh
### BEGIN INIT INFO
# Provides:          ondemand
# Required-Start:    $remote_fs $all
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:
# Short-Description: Set the CPU Frequency Scaling governor to "ondemand"
### END INIT INFO


PATH=/sbin:/usr/sbin:/bin:/usr/bin

. /lib/init/vars.sh
. /lib/lsb/init-functions

case "$1" in
    start)
        start-stop-daemon --start --background --exec /etc/init.d/ondemand -- background
        ;;
    background)
        sleep 60 # probably enough time for desktop login

        for CPUFREQ in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
        do
                [ -f $CPUFREQ ] || continue
                echo -n performance > $CPUFREQ
        done
        ;;
    restart|reload|force-reload)
        echo "Error: argument '$1' not supported" >&2
        exit 3
        ;;
    stop)
        ;;
    *)
        echo "Usage: $0 start|stop" >&2
        exit 3
        ;;
esac



2009年11月14日 星期六

coverity初體驗


最近在玩coverity,安裝設定上看看手冊大家應該都不成問題,不過在設定green hill可能會遇到小小的問題,就是coverity用__ghs,而一般的code則使用__ghs__,所以要小改一下。
再來就是幫忙review code的看法,以及新手常犯的錯誤,拿出來一起討論一下吧:

Return Address Of Local Variable

由於local variable的address存在stack之中,當程式結束後stack就會被回收,於是存取這塊記憶體時,就會變成invalid access。
錯誤的Example
#define SIZE    10
char* test()
{
    char s[SIZE];
    strcpy(s, "hello");
    return s;
}
幸運的是,GCC通常會出現"warning: function returns address of local variable"提醒Programmer。
基本上,可以使用malloc或者由外面傳進來:
Example
char* test()
{
    char *s;
    s = (char *) malloc(sizeof(char) * SIZE);
    strcpy(s, "hello");
    return s;
}
或者
char* test(char *s, int len)
{
    strncpy(s, "hello", len);
    return s;
}
個人比較偏愛由外面傳進來,再由外面的函數負責free resource。



真的理解C嗎?


昨天忽然搜尋到真的理解 C 語言的 types 嗎?後,自己在拜讀一下C99的standard,才發現自己真的對C感到很陌生,忽然對文中的"20年的工作經驗不過是一年的經驗,重複了20年"感觸頗深。
就以自己的工作經驗而言,常常發現許多工作好幾年的人與其年資完全不符,似乎是進來的沒多,而抱怨著,寫code不會賺錢,先不論寫code會不會賺錢,其實,自己的年資已經和累積的經驗已經有差距了,如何讓工作不再變成只是重複性的工作,我想這是任何一個programmer應該思考的議題。




vmware的timekeeping問題


您是不是常常被vmware抱怨CPU速度和偵測到的速度不符合,這是由於x86的power-saving讓CPU降頻執行所導致,解決的方法就是告知vmware,CPU的正確頻率是多少,找到設定檔config.ini,然後設定CPU頻率:
  • Windows 2000 or XP - %AllUsersProfile%\Application Data\VMware\<VMware-Product>\config.ini
  • Windows Vista or Windows 7 - C:\ProgramData\VMware\VMware\<VMware-Product>\config.ini

不存在的話就自行建立新檔,並且將以下字串貼入其中:
host.cpukHz = "2000000"
      host.noTSC = "TRUE"
      ptsc.noTSC = "TRUE"

"2000000"是我的電腦速度2G,請依照您的頻率調整。 參考資料:
http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1227



2009年11月12日 星期四

atftp on ubuntu


在ubuntu上安裝atftp當然就是一個指令搞定,不過有些東西要小改一下。
apt-get install atftpd

首先,不要直接修改/etc/init.d/atftpd了,改/etc/default/atftpd即可。
USE_INETD=false
OPTIONS="--daemon --tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758 --mcast-addr 239.239.239.0-255 --mcast-ttl 1 --maxthread 100 --verbose=5 /var/lib/tftpboot"
沒有加--daemon會出現以下錯誤。
如果出現
atftpd: can't bind port :69/udp

就把/etc/inetd.conf的tftp註解掉。



2009年11月9日 星期一

廢了三天的wireless


三天前,我在我的CQ45 101TX上裝ubuntu 9.10 (karmic),wireless都有訊號但卻是都無法正常work,最後還是換了一張USB的wireless,立刻正常work,看來broadcom的driver似乎有些問題,真是廢了三天。



ubuntu上好用的MSN軟體 -- emesene


因為好用,所以值得推薦,emesene使用上給我的感覺和MSN真的差異不大,有機會試試看吧。




2009年11月5日 星期四

Connectting Windows Desktop on Linux


在ubuntu上有個rdesktop的套件,可以直接連上windows的遠端桌面 --- rdesktop,安裝後執行rdesktop ip即可連上windows的遠端桌面。
sudo apt-get install rdesktop
rdesktop 172.23.19.243

2009年11月3日 星期二

C macro的誤用


macro主要是拿來取代,比如#define MAX_NUM 10,當用到MAX_NUM時,就會被轉成10,我們常用拿macro來define一些函數,比如:
#include <stdio.h>
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))

int main(int argc, char *argv[])
{
    int x=10, y=11, big;
    big = MAX(++x, ++y);
    printf("the bigger + 1 = %d\n", big);

    return 0;
}

可是這裡就會發生問題了,當你使用CPP你可以看到,X全部被++x取代,而Y全部被++y取代,所以執行完的結果顯然就會錯誤了。
int main(int argc, char *argv[])
{
    int x=10, y=11, big;
    big = ((++x) > (++y) ? (++x) : (++y));
    printf("the bigger + 1 = %d\n", big);

    return 0;
}
結果是13而不是12。

這邊應該改寫為
#include <stdio.h>
#define MAX(x, y) \
({ typeof(x) _x = (x); \
    typeof(y) _y = (y); \
 _x > _y ? _x : _y; })

int main(int argc, char *argv[])
{
    int x=10, y=11, big;
    big = MAX(++x, ++y);
    printf("the bigger + 1 = %d\n", big);

    return 0;
}
因為你無法預期使用者會輸入何種表示法,為了避免side effect,還是在內部宣告一個變數儲存使用者的輸入吧。
至於typeof可以參考一下GCC的說明http://gcc.gnu.org/onlinedocs/gcc/Typeof.html。



2009年10月29日 星期四

gparted


這是一套類似partition magic的軟體,可以幫您重新reisze您的partition,而且不會遺失資料,由於是圖形化操作,我就不囉嗦了。










module-init-tools


module-init-tools是我拿ubuntu的package名稱來當標題,這邊是要來介紹tools for managing Linux kernel modules管理Linux kernel module的工具。
這裡面主要的工具有
/bin/lsmod
顯示目前kernel的module之狀態
/sbin/insmod
insert module到kernel中
/sbin/rmmod
將module從kernel中移除
/sbin/depmod
用以產生modules.dep和map檔
/sbin/modprobe
新增/刪除module,藉由modules.dep連帶將相關的module做新增/刪除
/sbin/modinfo
顯示module的相關資訊



2009年10月28日 星期三

Universally Unique Identifier, UUID


UUID是一組16進制組成的128bit的ID,如果每秒產生1兆個UUID,要花100億年才會將所有UUID用完。所以,重複的機率非常低。UUID 的目的,是讓分散式系統中的所有元素,都能有唯一的辨識資訊,而不需要透過中央控制端來做辨識資訊的指定。UUID被廣泛的使用,而這邊要介紹在linux的hard disk上。
由於可能因為hard disk的順序變了,原本設定的/dev/sda1開機,現在變成了/dev/sdb1,需要更改boot的menu,如果我們對每個partition都給一個UUID,那麼即便hard disk順序變了,UUID不變的情況下,boot的menu就不需要做任何改變了。在fstab也是相同的道理。
可以利用tune2fs或blkid取得UUID。

2009年10月21日 星期三

ubuntu 9.04(jaunty)升級至9.10(karmic)


今天心血來潮,索性把ubuntu 9.04升級到9.10,沒考慮太多,所以不怕危險的話,就彷下面步驟升級吧。
  1. 修改/etc/apt/sources.list,把裏面的jaunty改成karmic
  2. apt-get update
  3. apt-get dist-upgrade

重開機後就完成啦。

cachefilesd


由debian的說明,來看
FSCache is a generic caching manager in the Linux kernel which can be used by network and other filesystems to cache data locally. 
FSCache是linux kernel中一個通用的cache管理者,用於管理網路和其他檔案系統的cache。 

CacheFiles is an FSCache backend that's meant to use as a cache a directory on an already mounted filesystem of a local type (such as Ext3). This package installs the userspace support required by the cachefiles backend. 
在ubuntu中即便安裝了cachefilesd,nfs還是無法使用cache機制,所以要重先編譯一下kernel module。將CONFIG_NFS_FSCACHE=y才能讓nfs支援cachefilesd。
而cache所在的partition必須是ext3或ext4並支援user_xattr屬性:
 tune2fs -o user_xattr /dev/sdaX
 sudo mount /var/cache/fscache

接著將修改/etc/cachefilesd.conf(或者使用預設值)。
當要mount nfs時,必須-o fsc告訴kernel這個nfs要使用cache機制。
 sudo mount -o fsc 10.0.100.11:/opt /opt

心得: 由於常用的是compile程式,大量的小檔讀寫,使用cache並沒有帶來好處。



2009年10月19日 星期一

quota無法正常在ubuntu 9.04上的ext4運作


這是ubuntu 9.04上package的bug,下載9.10的套件裝就好啦,here
brook@ubuntu1:~$ wget http://kr.archive.ubuntu.com/ubuntu/pool/main/q/quota/quota_3.17-3ubuntu1_i386.deb
--2009-10-19 16:09:14--  http://kr.archive.ubuntu.com/ubuntu/pool/main/q/quota/quota_3.17-3ubuntu1_i386.deb
Resolving kr.archive.ubuntu.com... 143.248.234.110
Connecting to kr.archive.ubuntu.com|143.248.234.110|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 473516 (462K) [application/x-debian-package]
Saving to: `quota_3.17-3ubuntu1_i386.deb'

100%[======================================>] 473,516      490K/s   in 0.9s

2009-10-19 16:09:17 (490 KB/s) - `quota_3.17-3ubuntu1_i386.deb' saved [473516/473516]

brook@ubuntu1:~$ sudo dpkg --purge quota
(Reading database ... 112680 files and directories currently installed.)
Removing quota ...
 * Turning off quotas...                                                 [ OK ]
 * Stopping quota service rpc.rquotad                                    [ OK ]
Purging configuration files for quota ...
Processing triggers for man-db ...
brook@ubuntu1:~$ sudo apt-get autoremove
Reading package lists... Done
Building dependency tree
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
brook@ubuntu1:~$ sudo dpkg -i quota_3.17-3ubuntu1_i386.deb
Selecting previously deselected package quota.
(Reading database ... 112624 files and directories currently installed.)
Unpacking quota (from quota_3.17-3ubuntu1_i386.deb) ...
Setting up quota (3.17-3ubuntu1) ...
 * Checking quotas...
                                                                         [ OK ]
 * Turning on quotas...                                                  [ OK ]
 * Starting quota service rpc.rquotad                                    [ OK ]

Processing triggers for man-db ...
brook@ubuntu1:~$ sudo mount -o remount /home
[sudo] password for brook:

2009年10月18日 星期日

32位元的compiler在32bit與64bit ubuntu下效能比較


繼之前的效能比較後(64bit與32bit應用程式在64位元ubuntu下之效能比較),我們面對比較實際的問題,因為開發用的compiler為32-bit之montavista,那麼到底是32位元的ubuntu效能好還是64位元的ubuntu好,所以我們決定直接將專案分別在32bit和64bit的ubuntu下compiler 10次,最後,發現在32bit底下有較好的效能。

圖為montavista 32bit compiler分別在32bit與64bit ubuntu下,compile專案所需的時間,以及所設定的make之job數比較圖,很明顯的,當job數越多,效能越好,而且32bit效能一直好過64bit。
JOBS 64-bit(sec) 32-bit(sec)
default(4) 479.567 451.847
6 426.922 400.545
8 400.724 370.174
10 393.842 363.798
12 391.594 361.957

這邊的結論不是想說32bit或是64bit的ubuntu哪個好,而是得到在我們的編譯環境下,32bit的ubuntu是比較適合我們的

2009年10月17日 星期六

64bit與32bit應用程式在64位元ubuntu下之效能比較


手邊最近剛好進來了一台i7搭配6G的PC,雖然裝了ubuntu 64bit-server的OS,但是我們的應用程式很多都還是32位元的,而且很好奇到底是64bit的application快,還是32bit的application快,於是我分別將unixbench編譯成32-bit和64-bit進行測試。
結果發現,即便是在64bit的OS下執行程式,32bit的效能不見得比64bit來的差,不是單純的將OS和硬體升級成64位元,電腦就會變快了,應用程式有沒有對64做最佳化也是非常重要的關鍵。

在讀寫方面,64位元小勝32位元。圖表為讀寫10sec所讀寫到的資料量,file system為ext3。

在一些演算法的運算下,各有小勝的項目。

在process的處理上,32bit勝出。


至於system call和pipe則64bit勝出。


檔案複製上,32bit勝出。(這點感覺還挺那悶的)

數值運算上,64bit小勝。

所有數據資料
  64-bit 32-bit  
Dhrystone 2 without register variables 22989948.3 15872890.4 lps
Dhrystone 2 using register variables 23031345.5 15525587.1 lps
Arithmetic Test (type = register) 3795084.9 3800669.2 lps
Arithmetic Test (type = short) 3792994 3475120.4 lps
Arithmetic Test (type = int) 3798887.6 3795243.4 lps
Arithmetic Test (type = long) 1361641.8 3797989.6 lps
Arithmetic Test (type = float) 2999814.7 1755172.6 lps
Arithmetic Test (type = double) 1908910.6 1755790.4 lps
System Call Overhead Test 5310434.8 4194268.7 lps
Pipe Throughput Test 2713396.7 2364773.8 lps
Pipe-based Context Switching Test N/A 421790.3  
Process Creation Test 13766.1 14274.1 lps
Execl Throughput Test 4028.2 4128.9 lps
File Read  (10 seconds) 8619286 7545589 KBps
File Write (10 seconds) 1519095 1453324 KBps
File Copy  (10 seconds) 79768 91090 KBps
File Write (30 seconds) 1515752 N/A KBps
File Copy  (30 seconds) 89420 N/A KBps
C Compiler Test 1826.8 1863 lpm
Shell scripts (1 concurrent) 8495.3 15928.3 lpm
Shell scripts (2 concurrent) 6293.3 12343.3 lpm
Shell scripts (4 concurrent) 4276.7 7584.6 lpm
Shell scripts (8 concurrent) 2818 5387 lpm
Dc: sqrt(2) to 99 decimal places 593737.4 625083.3 lpm
Recursion Test--Tower of Hanoi 244750.3 168275.4 lps
Arithmetic Test (type = double)         751 690.8  
Dhrystone 2 without register variables  1027.9 709.7  
Execl Throughput Test                   244.1 250.2  
File Copy  (30 seconds) 499.6 N/A  
Pipe-based Context Switching Test       0 319.9  
Shell scripts (8 concurrent)            704.5 1346.8  
下載unixbench:
http://www.tux.org/pub/tux/benchmarks/System/unixbench

2009年10月12日 星期一

KVM


KVM是Linux底下的Kernel-based Virtual Machine,而在用KVM前,要先知道CPU本身是否支援硬體VM,否則不能用KVM:
Intel CPU:
grep vmx /proc/cpuinfo
AMD CPU:
grep svm /proc/cpuinfo
如果沒有就不支援kvm啦,不過在執行的時候會被轉成qemu執行,並且出現以下錯誤:
open /dev/kvm: No such file or directory
Could not initialize KVM, will disable KVM support


接著載入kvm.ko,並且根據CPU是Intel還是AMD分別載入kvm-intel.ko或kvm-amd.ko。
相關的套件可以使用以下指令一次安裝:
sudo apt-get install kvm libvirt-bin ubuntu-vm-builder bridge-utils

接著使用kvm-img建立disk,或者直接使用硬碟的partition(/dev/sdaX)。
完整的指令如下:
/usr/bin/kvm -M pc -m 2000 -smp 8 -localtime -boot c -hda /var/lib/libvirt/images/linux.img -hdb /dev/sda2

接著就開始安裝您的guest OS嚕。


2009年10月8日 星期四

AJAX - Asynchronous JavaScript And XML


AJAX算是一個不算是一個新的技術,利用非同步的方式,提供互動式的網頁,AJAX面對最大的問題就是各個Browser之間對Java Script的標準與設計不一,常常寫好的能在FireFox上執行,在IE卻不能執行,所以在設計AJAX的時候最好考慮一下相容性的問題。
簡單的來說,AJAX提供XMLHttpRequest送出request給Web Server,接著等待回應,並處理回應的資料。

XMLHttpRequest

由於Browser之間對Java Script的標準與設計不一,所以下面的寫法可以用於大多數的形況。
// Provide the XMLHttpRequest class for IE 5.x-6.x: // Other browsers (including IE 7.x-8.x) ignore this // when XMLHttpRequest is predefined if (typeof(XMLHttpRequest) == "undefined") { XMLHttpRequest = function() { try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch(e) {} try { return new ActiveXObject("Msxml2.XMLHTTP.4.0"); } catch(e) {} try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch(e) {} try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) {} try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {} throw new Error("This browser does not support XMLHttpRequest."); }; } xmlHttpRequest = new XMLHttpRequest(); 有了XMLHttpRequest之後,我們就可以送出request並且處理資料

xmlHttpRequest.onreadystatechange = function () { //處理來自Server的回應 if(xmlHttpRequest.readyState==4) { alert(xmlHttpRequest.responseText); // Server回應的內容 // 如果回傳的是XML資料可以使用responseXML } } xmlHttpRequest.open("GET","my.php", true); // 設定送出的method(GET/POST), 以及對象(URL) // 基於安全考量,你不能呼叫不同網域的網頁。如果網域不同時會出現「權限不足,拒絕存取」的錯誤。 // 第三個參數設定此request是否不同步進行,如果設定為TRUE,則即使伺服器尚未回傳資料,也會繼續執行其餘的程式。 xmlhttp.send(null); // 送出request
StateDescription
0The request is not initialized
1The request has been set up
2The request has been sent
3The request is in process
4The request is complete



2009年10月6日 星期二

vmware server on Linux(ubuntu 9.04)


VMware Server現在也正式成為VMware的一個免費產品。所以記得前往官方網站下載註冊碼,VMware Server提供Linux和Windows兩個平台,下載後解壓縮,執行sudo ./vmware-install.pl,基本上一直按enter就會用default值安裝。 常遇到的問題就是出現找不到linux的header file(因為要編譯module): What is the location of the directory of C header files that match your running kernel? 解決方法就是安裝heade file嚕sudo apt-get install linux-headers-`uname -r`。移除時,執行sudo vmware-uninstall即可。 vmware server 2.0是以web方式呈現,https://server:8333,然後輸入之前設定時的admin account即可進入修改(必須是存在系統的帳號)。

Virtual Box on Linux(ubuntu 9.04)


ubuntu 9.04的apt預設會以virtualbox-ose取代virtualbox-3.0,所以必須修改一下apt。 1. 編輯 /etc/apt/sources.list 加上下列內容。 deb http://download.virtualbox.org/virtualbox/debian jaunty non-free 2. 加入 Sun public key for apt-secure。 wget -q http://download.virtualbox.org/virtualbox/debian/sun_vbox.asc -O- | sudo apt-key add - 3. 更新 Source List。 sudo apt-get update 4. 安裝 VirtualBox 3.0。 sudo apt-get install virtualbox-3.0 5. 執行VirtualBox。

terms

host OS:實際在機器上執行的OS。 guest OS:在Virtual Machine上執行的OS。 Guest Additions:通常VM會提供一些套件,安裝在guest OS上,增加guest OS的效能與提供額外的功能。

2009年9月30日 星期三

linux - 監控溫度(EX58-UD3R)


規格:
主機板: EX58-UD3R
CPU : i7 920

安裝:
brook@ubuntu:~$ sudo apt-install lm-sensors
brook@ubuntu:~$ sudo modprobe coretemp
brook@ubuntu:~$ sensors

錯誤排除:
brook@ubuntu:~$ sensors No sensors found! Make sure you loaded all the kernel drivers you need. Try sensors-detect to find out which these are. brook@ubuntu:~$ sudo modprobe coretemp 或者 brook@ubuntu:~$ sudo sensors-detect # sensors-detect revision 5249 (2008-05-11 22:56:25 +0200) This program will help you determine which kernel modules you need to load to use lm_sensors most effectively. It is generally safe and recommended to accept the default answers to all questions, unless you know what you're doing. We can start with probing for (PCI) I2C or SMBus adapters. ......
結果:

2009年9月29日 星期二

lvm設定步驟


1. 設定partition為LVM 2. 建立VG 3. 建立LV 4. formation LV




安裝已存在的LVM



2009年9月27日 星期日

linux - hlist


一樣在include/linux/list.h中,開宗明義的說Mostly useful for hash tables where the two pointer list head is too wastful,點出hlist為啥要有hlist_head和hlist_node了。
/* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; #define HLIST_HEAD_INIT { .first = NULL } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) static inline void INIT_HLIST_NODE(struct hlist_node *h) { h->next = NULL; h->pprev = NULL; } HLIST_HEAD_INIT()和HLIST_HEAD()用於未定義的變數,INIT_HLIST_HEAD()用於以定義的變數。

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } /* next must be != NULL */ static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next) { next->next = n->next; n->next = next; next->pprev = &n->next; if(next->next) next->next->pprev = &next->next; } hlist_add_head()就很是很簡單的將n加到h的第一個node上。

hlist_add_after()是將next加到n的後面去,這裡新加入的node是next

hlist_add_before()是將n加到next的前面去,這裡新加入的node則是n


static inline void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } __hlist_del()就像__list_del()一樣,把要del的node的前後串起來。



熱門文章