Linux内核模块编程(二)
变量/函数的导出(export)
刚才讲的都是使用别人的 function 上遇到的名字 encode 问题。但是,如果我们自己的 module 想要 export 一些东西让别的 module 使用呢。很简单。在 default 上,在你的 module 里所有的 global variable 和 function 都会被认定为你要 export 出去的。所以,如果你的 module 里有 10 个 global variable,经由 ksyms,你可以发现这十个 variable 都会被 export 出去。这当然是个很方便的事啦,但是,你知道,有时候我们根本不想把所有的 variable 都 export 出去,万一有个 module 没事乱改我们的 variable 怎幺办呢 ? 所以,在很多时候,我们都只会限定几个必要的东西 export 出去。在 2.2.1 之前的 kernel (不是很确定) 可以利用 register_symtab 来帮我们。但是,现在更新的版本早就出来了。所以,在此,我会介绍 kernel 2.2.1 里所提供的。kernel 2.2.1 里提供了一个 macro,叫做 EXPORT_SYMBOL,这是用来帮我们选择要 export 的 variable 或 function。比方说,我要 export 一个叫 full 的 variable,那我只要在 module 里写:
EXPORT_SYMBOL(full);
就会自动将 full export 出去,你马上就可以从 ksyms 里发现有 full 这个变量被 export 出去。在使用 EXPORT_SYMBOL 之前,要小心一件事,就是必须在 gcc 里定义 EXPORT_SYMTAB 这个 constant,否则在 compile 时会发生 parser error。所以,要使用 EXPORT_SYMBOL 的话,那 gcc 应该要下:
gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS -DEXPORT_SYMTAB
main.c -include /usr/src/linux/include/linux/modversions.h
如果我们不想 export 任何的东西,那我们只要在 module 里下
EXPORT_NO_SYMBOLS;
就可以了。使用 EXPORT_NO_SYMBOLS 用不着定义任何的 constant。其实,如果各位使用过旧版的 register_symbol 的话,一定会觉得新版的方式比较好用。至少我是这样觉得啦。因为使用 register_symbol 还要先定义出自己的 symbol_table,感觉有点麻烦。
当我们使用 EXPORT_SYMBOL 把一些 function 或 variable export 出来之后,我们使用 ksyma -a 去看一些结果。我们发现 EXPORT_SYMBOL(full) 的确是把 full export出来了 :
c8822200 full [my_module]
c01b8e08 pci_find_slot_R454463b5
. . .
但是,结果怎幺跟我们想象中的不太一样,照理说,应该是 full_Rxxxxxx 之类的东西才对啊,怎幺才出现 full 而已呢 ? 奇怪,问题在那里呢 ?
其实,问题就在于我们没有对本身的 module 所 export 出来的 function 或 variable 的名字做 encode。想想,如果在 module 的开头。我们加入一行
#define full full_Rxxxxxx
之后,我们再重新 compile module 一次,载到 kernel 之后,就可以发现 ksyms -a 显示的是
c8822200 full_Rxxxxxx [my_module]
c01b8e08 pci_find_slot_R454463b5
. . . . .
了。那是不是说,我们要去对每一个 export 出来的 variable 和 function 做 define 的动作呢 ? 当然不是啰。记得吗,前头我们讲去使用 kernel export 的 function 时,由于 include 了一些 .ver 的档案,以致于我们不用再做 define 的动作。现在,我们也要利用 .ver 的档案来帮我们,使我们 module export 出来的 function 也可以自动加入 kernel version 的 information。也就是变成 full_Rxxxxxx 之类的东西。
Linux 里提供了一个 command,叫 genksyms,就是用来帮我们产生这种 .ver 的档案的。它会从 stdin 里读取 source code,然后检查 source code 里是否有 export 的 variable 或 function。如果有,它就会自动为每个 export 出来的东西产生一些 define。这些 define 就是我们之前说的。等我们有了这些 define 之后,只要在我们的 module 里加入这些 define,那 export 出来的 function 或 variable 就会变成上面那个样子。
假设我们的程序都放在一个叫 main.c 的档案里,我们可以使用下列的方式产生这些 define。
gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver
gcc 的 -E 参数是指将 preprocessing 的结果 show 出来。也就是说将它 include 的档案,一些 define 的结果都展开。-D__GENKSYMS__ 是一定要的。如果没有定义这个 constant,你将不会看到任何的结果。用一个管线是因为 genksyms 是从 stdin 读资料的,所以,经由管线将 gcc 的结果传给 genksyms。-k 2.2.1 是指目前使用的 kernel 版本是 2.2.1,如果你的 kernel 版本不一样,必须指定你的 kernel 的版本。产生的 define 将会被放到 main.ver 里。产生完 main.ver 档之后,在 main.c 里将它 include 进来,那一切就 OK 了。有件事要告诉各位的是,使用这个方式产生的 module,其 export 出来的东西会经由 main.ver 的 define 改头换面。所以如果你要让别人使用,那你必须将 main.ver 公开,不然,别人就没办法使用你 export 出来的东西了。
内核模块和系统的交互
/proc和设备驱动程序是内核和系统进行交互的两种方式。
/proc
/proc的使用:
defined in /kerneltree/include/linux/proc_fs.h
2.4.0:
struct proc_dir_entry {
unsigned short low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
unsigned long size;
struct inode_operations * proc_iops;
struct file_operations * proc_fops;
get_info_t *get_info;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int deleted; /* delete flag */
kdev_t rdev;
};
extern struct proc_dir_entry *proc_sys_root;
#ifdef CONFIG_SYSCTL
EXPORT_SYMBOL(proc_sys_root);
#endif
EXPORT_SYMBOL(proc_symlink);
EXPORT_SYMBOL(proc_mknod);
EXPORT_SYMBOL(proc_mkdir);
EXPORT_SYMBOL(create_proc_entry);
EXPORT_SYMBOL(remove_proc_entry);
EXPORT_SYMBOL(proc_root);
EXPORT_SYMBOL(proc_root_fs);
EXPORT_SYMBOL(proc_net);
EXPORT_SYMBOL(proc_bus);
EXPORT_SYMBOL(proc_root_driver);
2.2.18
struct proc_dir_entry {
unsigned short low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
unsigned long size;
struct inode_operations * ops;
int (*get_info)(char *, char **, off_t, int, int);
void (*fill_inode)(struct inode *, int);
struct proc_dir_entry *next, *parent, *subdir;
void *data;
int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data);
int (*readlink_proc)(struct proc_dir_entry *de, char *page);
unsigned int count; /* use count */
int deleted; /* delete flag */
};
#ifdef CONFIG_SYSCTL
EXPORT_SYMBOL(proc_sys_root);
#endif
EXPORT_SYMBOL(proc_register);
EXPORT_SYMBOL(proc_unregister);
EXPORT_SYMBOL(create_proc_entry);
EXPORT_SYMBOL(remove_proc_entry);
EXPORT_SYMBOL(proc_root);
EXPORT_SYMBOL(proc_root_fs);
EXPORT_SYMBOL(proc_get_inode);
EXPORT_SYMBOL(proc_dir_inode_operations);
EXPORT_SYMBOL(proc_net_inode_operations);
EXPORT_SYMBOL(proc_net);
EXPORT_SYMBOL(proc_bus);
example:
struct proc_dir_entry Our_Proc_File =
{
0, /* Inode number - ignore, it will be filled by
* proc_register[_dynamic] */
4, /* Length of the file name */
"test", /* The file name */
S_IFREG | S_IRUGO, /* File mode - this is a regular
* file which can be read by its
* owner, its group, and everybody
* else */
1, /* Number of links (directories where the
* file is referenced) */
0, 0, /* The uid and gid for the file - we give it
* to root */
80, /* The size of the file reported by ls. */
NULL, /* functions which can be done on the inode
* (linking, removing, etc.) - we don
* support any. */
procfile_read, /* The read function for this file,
* the function called when somebody
* tries to read something from it. */
NULL /* We could have here a function to fill the
* files inode, to enable us to play with
* permissions, ownership, etc. */
};
使用:
2.2.* & 2.0.*
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
/* In version 2.2, proc_register assign a dynamic
* inode number automatically if it is zero in the
* structure , so theres no more need for
* proc_register_dynamic
*/
return proc_register(&proc_root, &Our_Proc_File);
#else
return proc_register_dynamic(&proc_root, &Our_Proc_File);
#endif
proc_unregister(&proc_root, Our_Proc_File.low_ino);
2.4.*
#ifndef _LINUX_SYSCTL_H
#define _LINUX_SYSCTL_H
#include
#include
#include
/* A sysctl table is an array of struct ctl_table: */
struct ctl_table
{
int ctl_name; /* Binary ID */
const char *procname; /* Text ID for /proc/sys, or zero */
void *data;
int maxlen;
mode_t mode;
ctl_table *child;
proc_handler *proc_handler; /* Callback for text formatting */
ctl_handler *strategy; /* Callback function for all r/w */
struct proc_dir_entry *de; /* /proc control block */
void *extra1;
void *extra2;
};
/* struct ctl_table_header is used to maintain dynamic lists of
ctl_table trees. */
struct ctl_table_header
{
ctl_table *ctl_table;
struct list_head ctl_entry;
};
struct ctl_table_header * register_sysctl_table(ctl_table * table,
int insert_at_head);
void unregister_sysctl_table(struct ctl_table_header * table);
例:
/kerneltree/net/khttpd/sysctl.c
1.#inlclude
2.对/kerneltree/inlcude/linux/sysctl.h进行修改
设备驱动程序
设备文件的使用:
/usr/include/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 (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
int (*lock) (struct file *, int, struct file_lock *);
};
重新定义相应的函数。
设备和系统的交互:
#include /* The character device
* definitions are here */
#include /* A wrapper which does
* next to nothing at
* at present, but may
* help for compatibility
* with future versions
* of Linux */
主码/从码
printk("Device: %d.%dn", inode->i_rdev >> 8, inode->i_rdev & 0xFF);
primary key secondery key
1. 字符设备
Major = module_register_chrdev(0,
DEVICE_NAME,
&Fops);
ret = module_unregister_chrdev(Major, DEVICE_NAME);
2. 块设备
Major = module_register_blkdev(0,
DEVICE_NAME,
&Fops);
ret = module_unregister_blkdev(Major, DEVICE_NAME);
//defined in &
系统调用
和模块有关的系统调用
sys_create_module()
sys_init_module()
sys_delete_module()
sys_get_kernel_syms()
get_module_list()
get_ksyms_list()
sys_query_module()
int request_module (const char *name);
int release_module (const char* name, int waitflag);
int delayed_release_module (const char *name);
int cancel_release_module (const char *name);
系统调用原型:
syscalln(rettype, name, type1, parm1,……)
#include
系统调用的使用:
1. 系统调用表
#include
asmlinkage int (*original_call)(const char *, int, int);
asmlinkage int our_sys_open(const char *filename,
int flags,
int mode)
original_call = sys_call_table[__NR_open];
sys_call_table[__NR_open] = our_sys_open;
if (sys_call_table[__NR_open] != our_sys_open) {
printk("Somebody else also played with the ");
printk("open system calln");
printk("The system may be left in ");
printk("an unstable state.n");
}
sys_call_table[__NR_open] = original_call;
2. syscalln
static inline _syscall1(int, brk, void *, end_data_segment);
...
int ret, tmp;
char *truc = OLDEXEC;
char *nouveau = NEWEXEC;
unsigned long mmm;
mmm = current->mm->brk; /*定位当前进程数据段大小*/
ret = brk((void ) (mmm + 256)); /*利用系统调用brk为当前进程增加内存256个字节*/
if (ret < 0)
return ret; /*分配不成功*/
memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
3. 扩展
#define _syscall1(type,name,type1,arg1)
type name(type1 arg1)
{
long __res;
__asm__ volatile ("int $0x80"
: "=a" (__res)
: "0" (__NR_##name),"b" ((long)(arg1)));
if (__res >= 0)
return (type) __res;
errno = -__res;
return -1;
}
.................
end发布人:netbull 来自:奥索网