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毫秒