Linux环境下用户空间与内核空间数据的交换方式

在linux环境开发过程中,经常会需要在用户空间和内核空间之间进行数据交换。
介绍了 Linux 系统下用户空间与内核空间数据交换的几种方式
 
第一节:使用procfs实现内核交互简明教程(1)
 
第二节:使用procfs实现内核交互简明教程(2)
 
第三节:基于register_sysctl_table实现内核数据交互(Sysctl方式)
 
第四节:通过bootloader向内核传输启动参数
 
第五节:Linux的kobject机制
 
第六节:利用内核模块添加系统调用

 
第七节:使用ioctl向linux内核传递参数的方法实例
 
第一:procfs信息读取实现案例

procfs是比较老的一种用户态与内核态的数据交换方式,内核的很多数据都是通过这种方式出口给用户的,内核的很多参数也是通过这种方式来让用户方便设置的。

  1. struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
  2. struct proc_dir_entry *parent)

该函数用于创建一个正常的proc条目,参数name给出要建立的proc条目的名称,参数mode给出了建立的该proc条目的访问权限,参数parent指定建立的proc条目所在的目录。如果要在/proc下建立proc条目,parent应当为NULL。

  1. /*
  2. * Remove a /proc entry and free it if it's not currently in use.
  3. */
  4. void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

该函数用于删除上面函数创建的proc条目,参数name给出要删除的proc条目的名称,参数parent指定建立的proc条目所在的目录。

  1. struct proc_dir_entry {
  2. ……
  1. read_proc_t *read_proc; /* 读接口 */
  2. write_proc_t *write_proc; /* 写接口 */
  3. ……

procfs读写信息实例:

  1. /**********************************************
  2. * Author: lewiyon@hotmail.com
  3. * File name: proc_sample.c
  4. * Description: create a file "proc_example" in the /proc,
  5. * which allows both input and output.
  6. * Date: 2011-12-12
  7. *********************************************/
  8. #include <linux/kernel.h> /* We're doing kernel work */
  9. #include <linux/module.h> /* Specifically, a module */
  10. #include <linux/proc_fs.h> /* Necessary because we use proc fs */
  11. #include <asm/uaccess.h> /* for get_user and put_user */
  12. #define MSG_LEN 10
  13. #define PROC_NAME "sample"
  14. char chflag[MSG_LEN] = "sample";
  15. static struct proc_dir_entry *sample;
  16. /**
  17. * proc_read_data()
  18. * @page - buffer to write the data in
  19. * @start - where the actual data has been written in page
  20. * @offset - same meaning as the read system call
  21. * @count - same meaning as the read system call
  22. * @eof - set if no more data needs to be returned
  23. * @data - pointer to our soft state
  24. */
  25. static int proc_read_rwdata(char *page, char **stat, off_t off,
  26. int count, int *eof, void *data)
  27. {
  28. int len;
  29. len = sprintf(page,"%s\n", chflag);
  30. return len;
  31. }
  32. static int proc_write_rwdata(struct file *file, const char __user *buf,
  33. unsigned long count, void *data)
  34. {
  35. if (count > MSG_LEN)
  36. count = MSG_LEN;
  37. if (copy_from_user(&chflag, buf, count))
  38. return -EFAULT;
  39. return count;
  40. }
  41. /*
  42. * 模块初始化
  43. */
  44. static int __init sample_init(void)
  45. {
  46. int ret = 0;
  47. /*
  48. * create_proc_entry(name, mode,parent)
  49. * 在parent对应的目录下创建name文件
  50. * 返回目录对应的proc_dir_dentry
  51. */
  52. sample = create_proc_entry(PROC_NAME, 0644, NULL);
  53. if (NULL == sample) {
  54. ret = -ENOMEM;
  55. goto sample_err;
  56. }
  57. sample->read_proc = &proc_read_rwdata,
  58. sample->write_proc = &proc_write_rwdata,
  59. printk(KERN_INFO "Create sample\n");
  60. return ret;
  61. sample_err:
  62. return ret;
  63. }
  64. /*
  65. * 模块清理
  66. */
  67. static void __exit sample_exit(void)
  68. {
  69. remove_proc_entry(PROC_NAME, NULL);
  70. printk(KERN_INFO "Remove /proc/proc_sample\n");
  71. }
  72. module_init(sample_init);
  73. module_exit(sample_exit);
  74. MODULE_LICENSE("GPL");
  75. MODULE_AUTHOR("lewiyon <lewiyon@hotmail.com>");

  1. /**********************************************
  2. * Author: lewiyon@hotmail.com
  3. * File name: proc_sample.c
  4. * Description: create a file "proc_example" in the /proc,
  5. * which allows read.
  6. * Date: 2011-12-11
  7. * Version: V1.0
  8. *********************************************/
  9. #include <linux/kernel.h> /* We're doing kernel work */
  10. #include <linux/module.h> /* Specifically, a module */
  11. #include <linux/proc_fs.h> /* Necessary because we use proc fs */
  12. #include <asm/uaccess.h> /* for get_user and put_user */
  13. #define MESSAGE_LENGTH 80
  14. #define PROC_NAME "proc_sample"
  15. unsigned int flag = 100;
  16. static struct proc_dir_entry *proc_sample;
  17. static struct proc_dir_entry *sample, *sample_r;
  18. /**
  19. * proc_read_data()
  20. * @page - buffer to write the data in
  21. * @start - where the actual data has been written in page
  22. * @offset - same meaning as the read system call
  23. * @count - same meaning as the read system call
  24. * @eof - set if no more data needs to be returned
  25. * @data - pointer to our soft state
  26. */
  27. static int proc_read_data(char *page, char **stat, off_t off,
  28. int count, int *eof, void *data)
  29. {
  30. int len;
  31. len = sprintf(page, "jiffies = %ld\n", jiffies);
  32. return len;
  33. }
  34. /*
  35. * 模块初始化
  36. */
  37. static int __init sample_init(void)
  38. {
  39. int ret = 0;
  40. /*
  41. * proc_mkdir(name, parent)
  42. * 在parent对应的目录下创建name目录
  43. * 返回目录对应的proc_dir_dentry
  44. */
  45. proc_sample = proc_mkdir(PROC_NAME, NULL);
  46. if (NULL == proc_sample) {
  47. ret = -ENOMEM;
  48. goto proc_sample_err;
  49. }
  50. /*
  51. * create_proc_entry(name, mode,parent)
  52. * 在parent对应的目录下创建name文件
  53. * 返回目录对应的proc_dir_dentry
  54. */
  55. sample = create_proc_entry("sample", 0644, proc_sample);
  56. if (NULL == sample) {
  57. ret = -ENOMEM;
  58. goto sample_err;
  59. }
  60. sample_r = create_proc_read_entry("sample_r", 0444,
  61. proc_sample, proc_read_data, NULL);
  62. if (NULL == sample_r) {
  63. ret = -ENOMEM;
  64. goto sample_r_err;
  65. }
  66. printk(KERN_INFO "Create sample\n");
  67. return ret;
  68. sample_r_err:
  69. remove_proc_entry("sample", proc_sample);
  70. sample_err:
  71. remove_proc_entry(PROC_NAME, NULL);
  72. proc_sample_err:
  73. return ret;
  74. }
  75. /*
  76. * 模块清理
  77. */
  78. static void __exit sample_exit(void)
  79. {
  80. remove_proc_entry("sample", proc_sample);
  81. remove_proc_entry("sample_r", proc_sample);
  82. remove_proc_entry(PROC_NAME, NULL);
  83. printk(KERN_INFO "Remove /proc/proc_sample\n");
  84. }
  85. module_init(sample_init);
  86. module_exit(sample_exit);
  87. MODULE_LICENSE("GPL");
  88. MODULE_AUTHOR("lewiyon <lewiyon@hotmail.com>");
在/proc/创建文件目录proc_sampe,然后在其下创建了两个文件。其中sample_r可读取数据


第二: register_sysctl_table实现内核数据交互

Sysctl是一种用户应用来设置和获得运行时内核的配置参数的一种有效方式,通过这种方式,用户应用可以在内核运行的任何时刻来改变内核的配置参数,也可以在任何时候获得内核的配置参数。

通常,内核的这些配置参数也出现在proc文件系统的/proc/sys
目录下,用户应用可以直接通过这个目录下的文件来实现内核配置的读写操作。
使用register_sysctl_table方式实现内核数据交互,就不得不用提到struct ctl_table
。下面来介绍一下这个结构体。
1 结构体ctl_table
每一个sysctl条目对应一个 struct ctl_table 结构,在该结构体定义在文件./include/
linux/sysctl.h中,定义及解释如下:
/* A sysctl table is an array of struct ctl_table: */
  1. struct ctl_table
  2. {
  3. const char *procname; /* Text ID for /proc/sys, or zero */
  4. void *data;
  5. int maxlen;
  6. mode_t mode;
  7. struct ctl_table *child;
  8. struct ctl_table *parent; /* Automatically set */
  9. proc_handler *proc_handler; /* Callback for text formatting */
  10. void *extra1;
  11. void *extra2;
  12. };
成员变量解释:
const char *procname; /* 表示在proc/sys/下显示的文件名称 */
void *data; /* 表示对应于内核中的变量名称 */
int maxlen; /* 表示条目允许的最大长度 */
mode_t mode; /* 条目在proc文件系统下的访问权限 */
struct ctl_table *child;
struct ctl_table *parent; /* Automatically set */
proc_handler *proc_handler; /*回调函数&proc_dointvec/&proc_dostring */
void *extra1;
void *extra2;

字段maxlen,它主要用于字符串内核变量,以便在对该条目设置时,对超过该最大长度的字符串截掉后面超长的部分.

字段proc_handler,表示回调函数,对于整型内核变量,应当设置为&proc_dointvec,而对于字符串内核变量,则设置为 &proc_dostring。

Sysctl 条目也可以是目录,此时 mode 字段应当设置为 0555,否则通过 sysctl 系统调用将无法访问它下面的 sysctl 条目,child 则指向该目录条目下面的所有条目,对于在同一目录下的多个条目,不必一一注册,用户可以把它们组织成一个 struct ctl_table 类型的数组,然后一次注册就可以。

2 注册register_sysctl_table
注册sysctl条目使用函数register_sysctl_table,函数原型如下:
struct ctl_table_header *register_sysctl_table(struct ctl_table *table)
第一个参数为定义的struct ctl_table结构的sysctl条目或条目数组指针;
3 卸载unregister_sysctl_table
当模块卸载时,需要使用函数unregister_sysctl_table,其原型:
void unregister_sysctl_table(struct ctl_table_header * header)
其中struct ctl_table_header是通过函数register_sysctl_table
注册时返回的结构体指针。
4 实例
  1. /**********************************************
  2. * Author: lewiyon@hotmail.com
  3. * File name: sysctl_example.c
  4. * Description: sysctl example
  5. * Date: 2013-04-24
  6. *********************************************/
  7. #include <linux/module.h>
  8. #include <linux/init.h>
  9. #include <linux/kernel.h>
  10. #include <linux/sysctl.h>
  11. static int sysctl_kernusr_data = 1024;
  12. static int kernusr_callback(ctl_table *table, int write,
  13. void __user *buffer, size_t *lenp, loff_t *ppos)
  14. {
  15. int rc;
  16. int *data = table->data;
  17. printk(KERN_INFO "original value = %d\n", *data);
  18. rc = proc_dointvec(table, write, buffer, lenp, ppos);
  19. if (write)
  20. printk(KERN_INFO "this is write operation, current value = %d\n", *
  21. data);
  22. return rc;
  23. }
  24. static struct ctl_table kernusr_ctl_table[] = {
  25. {
  26. .procname = "kernusr",
  27. .data = &sysctl_kernusr_data,
  28. .maxlen = sizeof(int),
  29. .mode = 0644,
  30. .proc_handler = kernusr_callback,
  31. },
  32. {
  33. /* sentinel */
  34. },
  35. };
  36. static struct ctl_table_header *sysctl_header;
  37. static int __init sysctl_example_init(void)
  38. {
  39. sysctl_header = register_sysctl_table(kernusr_ctl_table);
  40. if (sysctl_header == NULL) {
  41. printk(KERN_INFO "ERR: register_sysctl_table!");
  42. return -1;
  43. }
  44. printk(KERN_INFO "sysctl register success.\n");
  45. return 0;
  46. }
  47. static void __exit sysctl_example_exit(void)
  48. {
  49. unregister_sysctl_table(sysctl_header);
  50. printk(KERN_INFO "sysctl unregister success.\n");
  51. }
  52. module_init(sysctl_example_init);
  53. module_exit(sysctl_example_exit);
  54. MODULE_LICENSE("GPL");
5 参考文献
http://www.ibm.com/developerworks/cn/linux/l-kerns-usrs/
http://blog.chinaunix.net/uid-7210505-id-146429.html

Linux提供了一种通过bootloader向其传输启动参数的功能,内核开发者可以通过这种方式来向内核传输数据,从而控制内核启动行为。

通常的使用方式是,定义一个分析参数的函数,而后使用内核提供的宏 __setup把它注册到内核中,该宏定义在 linux/init.h 中,因此要使用它必须包含该头文件:

__setup("para_name=", parse_func)

其中:@para_name为参数名;@parse_func为分析参数值的函数,它负责把该参数的值转换成相应的内核变量的值并设置那个内核变量。

内核为整数参数值的分析提供了函数 get_option get_options,前者用于分析参数值为一个整数的情况,而后者用于分析参数值为逗号分割的一系列整数的情况,对于参数值为字符串的情况,需要开发者自定义相应的分析函数。

1 setup使用案例

在文件./mm/slub.c

  1. static int __init setup_slub_min_order(char *str)
  2. {
  3. get_option(&str, &slub_min_order);
  4. return 1;
  5. }
  6. __setup("slub_min_order=", setup_slub_min_order);
  7. static int __init setup_slub_max_order(char *str)
  8. {
  9. get_option(&str, &slub_max_order);
  10. slub_max_order = min(slub_max_order, MAX_ORDER - 1);
  11. return 1;
  12. }
  13. __setup("slub_max_order=", setup_slub_max_order);

2 内核程序kern-boot-params测试

在源代码包中的内核程序kern-boot-params.c说明了三种情况的使用。该程序列举了参数为一个整数、逗号分割的整数串以及字符串三种情况,读者要想测试该程序,需要把该程序拷贝到要使用的内核的源码目录树的一个目录下,为了避免与内核其他部分混淆,作者建议在内核源码树的根目录下创建一个新目录,如examples,然后把该程序拷贝到 examples 目录下并重新命名为 setup_example.c,并且为该目录创建一个 Makefile文件:

obj-y = setup_example.o

Makefile 仅需这一行就足够了,然后需要修改源码树的根目录下的 Makefile文件的一行,把下面行

core-y := usr/

修改为

core-y := usr/ examples/

注意:如果读者创建的新目录和重新命名的文件名与上面不同,需要修改上面所说 Makefile文件相应的位置。做完以上工作就可以按照内核构建步骤去构建新的内核。

  1. //filename: kern-boot-params.c
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>
  4. #include <linux/string.h>
  5. #define MAX_SIZE 5
  6. static int setup_example_int;
  7. static int setup_example_int_array[MAX_SIZE];
  8. static char setup_example_string[16];
  9. static int __init parse_int(char * s)
  10. {
  11. int ret;
  12. ret = get_option(&s, &setup_example_int);
  13. if (ret == 1) {
  14. printk("setup_example_int=%d\n", setup_example_int);
  15. }
  16. return 1;
  17. }
  18. static int __init parse_int_string(char *s)
  19. {
  20. char * ret_str;
  21. int i;
  22. ret_str = get_options(s, MAX_SIZE, setup_example_int_array);
  23. if (*ret_str != '\0') {
  24. printk("incorrect setup_example_int_array paramters: %s\n", ret_str);
  25. }
  26. else {
  27. printk("setup_example_int_array=");
  28. for (i=1; i<MAX_SIZE; i++) {
  29. printk("%d", setup_example_int_array[i]);
  30. if (i < (MAX_SIZE -1)) {
  31. printk(",");
  32. }
  33. }
  34. printk("\n");
  35. printk("setup_example_int_array includes %d intergers\n", setup_example_int_array[0]);
  36. }
  37. return 1;
  38. }
  39. static int __init parse_string(char *s)
  40. {
  41. if (strlen(s) > 15) {
  42. printk("Too long setup_example_string parameter, \n");
  43. printk("maximum length is less than or equal to 15\n");
  44. }
  45. else {
  46. memcpy(setup_example_string, s, strlen(s) + 1);
  47. printk("setup_example_string=%s\n", setup_example_string);
  48. }
  49. return 1;
  50. }
  51. __setup("setup_example_int=", parse_int);
  52. __setup("setup_example_int_array=", parse_int_string);
  53. __setup("setup_example_string=", parse_string);

3 变量配置方法

在构建好内核并设置好lilogrub为该内核的启动条目后,就可以启动该内核,然后使用lilogrub的编辑功能为该内核的启动参数行增加如下参数串:

setup_example_int=1234 setup_example_int_array=100,200,300,400 setup_example_string=Thisisatest

当然,该参数串也可以直接写入到lilo或grub的配置文件中对应于该新内核的内核命令行参数串中。读者可以使用其它参数值来测试该功能.

  1. [root@RedHat ~]# cat /boot/grub/menu.lst
  2. # grub.conf generated by anaconda
  3. #
  4. # Note that you do not have to rerun grub after making changes to this file
  5. # NOTICE: You have a /boot partition. This means that
  6. # all kernel and initrd paths are relative to /boot/, eg.
  7. # root (hd0,0)
  8. # kernel /vmlinuz-version ro root=/dev/mapper/VolGroup-lv_root
  9. # initrd /initrd-[generic-]version.img
  10. #boot=/dev/sda
  11. default=0
  12. timeout=3
  13. splashimage=(hd0,0)/grub/splash.xpm.gz
  14. hiddenmenu
  15. title Red Hat Enterprise Linux Server (3.2.0trace)
  16. root (hd0,0)
  17. kernel /vmlinuz-3.2.0trace ro root=/dev/mapper/VolGroup-lv_root rd_LVM_LV=VolGroup/lv_root rd_LVM_LV=VolGroup/lv_swap rd_NO_LUKS rd_NO_MD rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto rhgb quiet setup_example_int=1234 setup_example_int_array=10,20,30,40 setup_example_string=lewiyon
  18. initrd /initramfs-3.2.0trace.img
  19. title Red Hat Enterprise Linux (2.6.32-71.el6.i686)
  20. root (hd0,0)
  21. kernel /vmlinuz-2.6.32-71.el6.i686 ro root=/dev/mapper/VolGroup-lv_root rd_LVM_LV=VolGroup/lv_root rd_LVM_LV=VolGroup/lv_swap rd_NO_LUKS rd_NO_MD rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto rhgb quiet
  22. initrd /initramfs-2.6.32-71.el6.i686.img
  23. [root@RedHat ~]#

使用dmesg | grep setup来查看该程序的输出。
  
  
  1. [root@RedHat ~]# dmesg | grep setup
  2. setup_percpu: NR_CPUS:32 nr_cpumask_bits:32 nr_cpu_ids:1 nr_node_ids:1
  3. Kernel command line: ro root=/dev/mapper/VolGroup-lv_root rd_LVM_LV=VolGroup/lv_root rd_LVM_LV=VolGroup/lv_swap rd_NO_LUKS rd_NO_MD rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us crashkernel=auto rhgb quiet setup_example_int=1234 setup_example_int_array=10,20,30,40 setup_example_string=lewiyon
  4. setup_example_int=1234
  5. setup_example_int_array=10,20,30,40,
  6. setup_example_int_array includes 4 intergers
  7. setup_example_string=lewiyon
  8. [root@RedHat ~]#
 

4 参考文献

http://www.ibm.com/developerworks/cn/linux/l-kerns-usrs/

第四: Linux的kobject机制

sysfs文件系统下的每个目录对应于一个kobj,kset是kobj的封装,内嵌了一个kobj,其代表kset自身,ktype代表属性操作集,但由于通用性,因此把ktype单独剥离出来,kobj,kset,ktype成为了各个驱动模型最底层的关联元素,并由此形成了sys下的各种拓扑结构。

1 kobject,kset,子系统层次结构

内核通常用kobject结构将各个对象连接起来组成一个分层的结构体系。

一个 kset是嵌入到相同类型结构的 kobject的集合。

struct kobj_type 关注的是对象的类型,而struct kset关心的是对象的集合,可认为kset是kobjects的顶层容器类。每个 kset 在内部包含自己的 kobject,并可以用多种处理kobject的方法处理kset。 kset总是在 sysfs中出现;一旦设置了 kset并把它添加到系统中,将在 sysfs 中创建一个目录;kobjects不必在 sysfs中表示, 但kset中的每一个 kobject成员都在sysfs中得到表述。

2 如何实现sysfs接口

kobject 是在 sysfs虚拟文件系统后的机制。对每个在 sysfs中的目录,在内核中都会有一个 kobject与之对应。每个 kobject都输出一个或多个属性,它在 kobject 的 sysfs目录中以文件的形式出现,其中的内容由内核产生。

当创建kobject 时,每个 kobject都被给定一系列默认属性。这些属性保存在 kobj_type结构中:

  1. struct kobj_type {
  2. void (*release)(struct kobject *);
  3. struct sysfs_ops *sysfs_ops;/*提供实现以下属性的方法*/
  4. struct attribute **default_attrs; /*用于保存类型属性列表(指针的指针) */
  5. };
  6. struct attribute {
  7. char *name;/*属性的名字( 在 kobject 的 sysfs 目录中显示)*/
  8. struct module *owner;/*指向模块的指针(如果有), 此模块负责实现这个属性*/
  9. mode_t mode; /*属性的保护位,modes 的宏定义在 <linux/stat.h>:例如S_IRUGO 为只读属性等等*/
  10. }; /*default_attrs 列表中的最后一个元素必须用 0 填充*/

sysfs 读写这些属性是由 kobj_type->sysfs_ops成员中的函数完成的:

  1. struct sysfs_ops {
  2. ssize_t (*show)(struct kobject *, struct attribute *,char *);
  3. ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
  4. const void *(*namespace)(struct kobject *, const struct attribute *);
  5. };

当用户空间读取一个属性时,内核会使用指向 kobject的指针(kobj)和正确的属性结构(*attr)来调用show方法,该方法将给定属性值编码进缓冲(buffer)(注意不要越界( PAGE_SIZE字节)),并返回实际数据长度。sysfs的约定要求每个属性应当包含一个可读值;若返回大量信息,需将它分为多个属性.

也可对所有 kobject关联的属性使用同一个 show方法,用传递到函数的 attr指针来判断所请求的属性。有的 show方法包含对属性名字的检查。有的show方法会将属性结构嵌入另一个结构,这个结构包含需要返回属性值的信息,这时可用container_of获得上层结构的指针以返回属性值的信息。

store 方法将存在缓冲(buffer)的数据( size为数据的长度,不能超过 PAGE_SIZE )解码并保存新值到属性(*attr),返回实际解码的字节数。store方法只在拥有属性的写权限时才能被调用。此时注意:接收来自用户空间的数据一定要验证其合法性。如果到数据不匹配,返回一个负的错误值。

3 案例

  1. /**********************************************
  2. * Author: lewiyon@hotmail.com
  3. * File name: kset_sample.c
  4. * Description: kset example
  5. * Date: 2011-12-10
  6. *********************************************/
  7. #include <linux/kobject.h>
  8. #include <linux/string.h>
  9. #include <linux/sysfs.h>
  10. #include <linux/slab.h>
  11. #include <linux/module.h>
  12. #include <linux/init.h>
  13. /*
  14. * 将要创建的文件foo对应的kobj.
  15. */
  16. struct foo_obj {
  17. struct kobject kobj;
  18. int foo;
  19. int baz;
  20. int bar;
  21. };
  22. /* 通过域成员返回结构体的指针 */
  23. #define to_foo_obj(x) container_of(x, struct foo_obj, kobj)
  24. /* 属性 */
  25. struct foo_attribute {
  26. struct attribute attr;
  27. ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
  28. ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
  29. };
  30. #define to_foo_attr(x) container_of(x, struct foo_attribute, attr)
  31. /*
  32. * 属性foo信息显示函数 (涉及文件目录foo/)
  33. */
  34. static ssize_t foo_attr_show(struct kobject *kobj,
  35. struct attribute *attr,
  36. char *buf)
  37. {
  38. struct foo_attribute *attribute;
  39. struct foo_obj *foo;
  40. attribute = to_foo_attr(attr);
  41. foo = to_foo_obj(kobj);
  42. if (!attribute->show)
  43. return -EIO;
  44. return attribute->show(foo, attribute, buf);
  45. }
  46. /*
  47. * 属性foo存储函数(涉及文件目录foo/) (when a value is written to a file.)
  48. */
  49. static ssize_t foo_attr_store(struct kobject *kobj,
  50. struct attribute *attr,
  51. const char *buf, size_t len)
  52. {
  53. struct foo_attribute *attribute;
  54. struct foo_obj *foo;
  55. attribute = to_foo_attr(attr);
  56. foo = to_foo_obj(kobj);
  57. if (!attribute->store)
  58. return -EIO;
  59. return attribute->store(foo, attribute, buf, len);
  60. }
  61. /*
  62. * foo的show/store列表
  63. */
  64. static const struct sysfs_ops foo_sysfs_ops = {
  65. .show = foo_attr_show,
  66. .store = foo_attr_store,
  67. };
  68. /*
  69. * The release function for our object. This is REQUIRED by the kernel to
  70. * have. We free the memory held in our object here.
  71. */
  72. static void foo_release(struct kobject *kobj)
  73. {
  74. struct foo_obj *foo;
  75. foo = to_foo_obj(kobj);
  76. kfree(foo);
  77. }
  78. /*
  79. * 当需要从foo文件中读取信息时,调用此函数
  80. */
  81. static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
  82. char *buf)
  83. {
  84. return sprintf(buf, "%d\n", foo_obj->foo);
  85. }
  86. /*
  87. * 当往foo文件写入信息时,调用此函数
  88. */
  89. static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
  90. const char *buf, size_t count)
  91. {
  92. sscanf(buf, "%du", &foo_obj->foo);
  93. return count;
  94. }
  95. static struct foo_attribute foo_attribute =
  96. __ATTR(foo, 0666, foo_show, foo_store);
  97. /*
  98. * foo_ktype的属性列表
  99. */
  100. static struct attribute *foo_default_attrs[] = {
  101. &foo_attribute.attr,
  102. NULL, /* need to NULL terminate the list of attributes */
  103. };
  104. /*
  105. * 定义kobj_type结构体
  106. * 指定sysfs_ops,release函数, 属性列表foo_default_attrs
  107. */
  108. static struct kobj_type foo_ktype = {
  109. .sysfs_ops = &foo_sysfs_ops,
  110. .release = foo_release,
  111. .default_attrs = foo_default_attrs,
  112. };
  113. static struct kset *example_kset;
  114. static struct foo_obj *foo_obj;
  115. static struct foo_obj *create_foo_obj(const char *name)
  116. {
  117. struct foo_obj *foo;
  118. int retval;
  119. /* allocate the memory for the whole object */
  120. foo = kzalloc(sizeof(*foo), GFP_KERNEL);
  121. if (!foo)
  122. return NULL;
  123. foo->kobj.kset = example_kset;
  124. /*
  125. * 初始化kobject数据结结构foo->lobj,
  126. * 即 在foo->kobj的层次组织kset中创建个名为name的文件foo/foo
  127. * 成功返回0
  128. */
  129. retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
  130. if (retval) {
  131. /* 减小kobj的引用计数 */
  132. kobject_put(&foo->kobj);
  133. return NULL;
  134. }
  135. /*
  136. * 发送 KOBJ_ADD / KOBJ_REMOVE 等事件
  137. * We are always responsible for sending the uevent that the kobject
  138. * was added to the system.
  139. */
  140. kobject_uevent(&foo->kobj, KOBJ_ADD);
  141. return foo;
  142. }
  143. static void destroy_foo_obj(struct foo_obj *foo)
  144. {
  145. /* 减小kobj的引用计数 */
  146. kobject_put(&foo->kobj);
  147. }
  148. static int __init example_init(void)
  149. {
  150. /*
  151. * 动态地在kernel_kobj所对应的目录/sys/kernel/下创建一个目录kset_example
  152. * 并返回kset_example对应的kset
  153. */
  154. example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
  155. if (!example_kset)
  156. return -ENOMEM;
  157. foo_obj = create_foo_obj("foo");
  158. if (!foo_obj)
  159. goto foo_error;
  160. return 0;
  161. foo_error:
  162. return -EINVAL;
  163. }
  164. static void __exit example_exit(void)
  165. {
  166. destroy_foo_obj(foo_obj);
  167. kset_unregister(example_kset);
  168. }
  169. module_init(example_init);
  170. module_exit(example_exit);
  171. MODULE_LICENSE("GPL");
  172. MODULE_AUTHOR("Younger Liu <younger.liucn@gmail.com>");
4          参考资料:
sysfs文件系统详解:http://blog.chinaunix.net/u1/55599/showart_1089096.html
sysfs.h源码详析:http://blog.chinaunix.net/u1/55599/showart_1091002.html
http://www.diybl.com/course/6_system/linux/Linuxjs/2008727/134055.html
第五: 利用内核模块添加系统调用

操作系统的主要功能是为应用程序的运行创建良好的环境,为了达到这个目的,内核提供一系列具备预定功能的多内核函数,通过一组称为系统调用(system call)的接口呈现给用户。系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序,如果没有系统调用和内核函数,用户将不能编写大型应用程序。
Linux系统调用,包含了大部分常用系统调用和由系统调用派生出的的函数。

其步骤如下,

第一:增加系统调用号

在文件unistd_32.h或unistd_64.h(取决去您的机器是32位还是64位的)下,添加系统调用号,

比如:(最后一个系统调用号为348)

#define __NR_mysyscall 349

如果存在系统调用总数目宏,那么也需要修改系统调用号的数目(原来为349)

#define NR_syscalls 350

第二:实现int sys_yoursyscall()系统调用

新增一个系统调用就意味着要给内核增加1个函数,将新函数放入文件kernel/sys.c中(当然你也可以新增一个文件,那么就必须将新增文件添加到Makefile文件中使得随着内核一起编译)。新函数代码如下:

asmlingkage sys_testsyscall()
{
    ……
    return 0;
}

第三:内核编译及重启

(2.6.32之后标准linux内核版本编译)

make clean;

make –j (-j表示并行编译,速度较快,但也会使得您的机器无法做其他工作)

make install

reboot

第四:测试新系统调用


第六:使用ioctl向linux内核传递参数的方法实例

一、应用层

uint16 data16;

if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

{

printf("socket failed\n\r");

}

if(ioctl(fd, SIOCSIFVLAN_PVID_PRI, &data16) < 0)

{

printf("ioctl pvid failed\n\r");

}

 

二、linux内核

1 sockios.h中定义

#define SIOCSIFVLAN_PVID_PRI 0x8985 /* Set 802.1Q VLAN pvid */

2、在af_inet.c

添加

extern int VLAN1QEN(unsigned int ,void *arg);

inet_ioctl()函数中添加

case SIOCSIFVLAN_PVID_PRI:

return VLAN1QEN(cmd, arg);

3、另外定义:

static unsigned int VLAN_PVID_PRI = 0;

int VLAN1QEN(unsigned int cmd,void *arg)

{

unsigned int data;

if (copy_from_user(&data, arg, sizeof(int)))

return -EFAULT;

switch (cmd) {

case SIOCSIFVLAN_PVID_PRI:

VLAN_PVID_PRI = data;

break;

default:

return -EINVAL;

}

}



本文转自:http://blog.csdn.net/younger_china/article/details/11181081


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值