当前位置:Linux教程 - Linux - Linux内核模块编程(二)

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 来自:奥索网