2009年12月27日 星期日

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。


熱門文章