Embedded 版 (精华区)

发信人: Zinux (Linux技工), 信区: Embedded_system
标  题: 从内核空间进行系统调用(ksystime.c注解)(转载)
发信站: 哈工大紫丁香 (2001年10月26日18:29:29 星期五), 站内信件


这段时间在完成一项驱动程序的工作,有在内核模块中进行系统调用的需
要,直接调用read()肯定是不行的,没有想明白。后来看了"Making Sys-
tem Calls From Kernel Space",有些明白了。原文精华区里面有,不过
是E文,又比较长,没有耐心看的朋友可以看看我对示例源码的注解, 应
该能够较快地了解其方法。

Unix的内核空间和用户空间之间有明确的界限。系统调用是用户空间程序
得以获得内核服务的手段。然而我们也可以从内核空间进行系统调用,从
而使传统上在用户空间完成的任务在内核中完成,比如一个高性能的 Web
服务器。

/* ksystime.c
 * 作者: Alessandro Rubini
 * 译注: dot@BYHH
 * 演示了内核空间系统调用的几种途径,
 * 并且分别进行了计时
 * 并且分别进行了计时
 */
#define __KERNEL__
#define MODULE
#define __KERNEL_SYSCALLS__     /** 必须定义这个宏 */
#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/fs.h>

#include <linux/types.h>
#include <linux/unistd.h>       /** 定义了__NR_read等, 系统调用产生
                                    软中断(0x80)的参数 */

#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm/msr.h>            /** rdtsl(), 读Pentium时间戳计数器 */



#include "params.h"

int errno;

void ksys_print(char *name, unsigned long value)
{
    printk("ksystime: %3li -- %s\n", value, name);
}

int init_module(void)
{
    mm_segment_t fs;
    unsigned long ini, end, now, best, tsc;
    int i;
    char buffer[4];
    struct file *file;
    ssize_t (*readptr)(struct file *, char *, size_t, loff_t *);
    loff_t *posptr;

/** 下面定义的宏用来对代码code进行计时, 结果是CPU的tick数,
  * 很精确啊, 根据主频可以换算成时间.
  * NTRIALS是自己定义的一个数, 目的是测试很多次, 取一个最
  * NTRIALS是自己定义的一个数, 目的是测试很多次, 取一个最
  * 小值, 从而去掉CPU执行code期间被打断所造成的误差 */
#define measure_time(code) \
    for (i = 0; i < NTRIALS; i++) { \
    rdtscl(ini); \
        code; \
    rdtscl(end); \
    now = end - ini; \
    if (now < best) best = now; \
    }

/** 先测试了什么都不执行的时间, 其实是rdtsc()的时间 */
    /* time rdtsc (i.e. no code) */
    best = ~0;
    measure_time( 0 );
    tsc = best;
    ksys_print("tsc", tsc);

/** 下面改变了task_struct结构中addr_limit的值, 这是
  * 内核系统调用的关键! 原理嘛, 跟linux系统内存管理
  * 有关, 具体我也说不清楚, 呵呵. */
    /* prepare to invoke a system call */
    fs = get_fs();
    fs = get_fs();
    set_fs (get_ds());

/** 下面分别演示了内核系统调用的3种方法:
  * (1) 直接调用read() */
    /* time an empty read() */
    best = ~0;
    measure_time( read(0 /* stdin */, buffer, 0) );
    ksys_print("read()", best - tsc);

/** (2) 直接调用sys_read(), 因为用户空间调用了read()之后
  * 系统往下还是调用了sys_read(). 但是你需要知道sys_read
  * 在内核空间的确切地址, 所以还是别考虑这种方法了吧. */
#if 0
    /* call sys_read directly. This is disabled because
       you need to know the exact address of sys_read in kernel
       space. The value shown is the one I found in the system
       map of my current kernel. Please be careful */
    {
    ssize_t (*sys_read_ptr)(unsigned int , char *, size_t);
    sys_read_ptr = 0xc012a9e0; /* THIS MUST BE CORRECTED */

    best = ~0;
    best = ~0;
    measure_time( (*sys_read_ptr)(0 /* stdin */, buffer, 0) );
    ksys_print("sys_read()", best - tsc);
    }
#endif

/** (3) 调用文件操作的方法(这是面向对象概念的"方法"),
  * 这是最直接的方法.
  * 下面是从<linux/fs.h>中拷贝/粘贴的,
struct file_operations {
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, 
unsigned
long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *);
    int (*fasync) (int, struct file *, int);
    int (*fasync) (int, struct file *, int);
    int (*check_media_change) (kdev_t dev);
    int (*revalidate) (kdev_t dev);
    int (*lock) (struct file *, int, struct file_lock *);
};
  * 实现一个驱动程序很大部分工作是在实现这些与设备相关的方法. */
    /* use the file operation directly */
    file = fget(0 /* fd */);
    if (file && file->f_op && file->f_op->read) {
    best = ~0;
    measure_time(
        file->f_op->read(file, buffer, 0, &file->f_pos)
    );
    ksys_print("f_op->read()", best - tsc);
    }

/** 下面进一步把file->f_op->read方法指针cache起来, 加快了
  * 调用的速度而已. */
    /* cache the read ptr */
    if (file && file->f_op && file->f_op->read) {
    readptr = file->f_op->read;
    posptr = &file->f_pos;
    best = ~0;
    best = ~0;
    measure_time(
        readptr(file, buffer, 0, posptr)
    );
    ksys_print("cached_f_op_read()", best - tsc);
    }

    if (file) fput(file);

/** 千万别忘了恢复fs, 否则会崩溃吧, 我没有试过. */
    /* restore fs and make insmod fail */
    set_fs (fs);
/** insmod不成功, 就用不着rmmod, 反正我们也不是真的要
  * 加载这个模块. 嘿嘿, 我怎么没有想到, 真聪明. */
    return -EINVAL;
}

/** 没有加载, 也就不用cleanup了. */
void cleanup_module(void) {} /* never used */

好了, 不知道写明白了没有, 不行还是看原文吧.
下面是作者的500MHz机子上的运行结果:
kernel: ksystime:  11 -- tsc
下面是作者的500MHz机子上的运行结果:
kernel: ksystime: 424 -- read()
kernel: ksystime: 216 -- sys_read()
kernel: ksystime: 175 -- f_op->read()
kernel: ksystime: 173 -- cached_f_op_read()

用户空间调用一个空的read()要花 474个ticks.
我觉得还是第三种方法比较好, 又快又干净.
不对的地方各位大虾要多指正啊, 我就要靠这个
写程序了.


--

  puke! 
  技工而已

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.239.152]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.570毫秒