在linux环境开发过程中,经常会需要在用户空间和内核空间之间进行数据交换。
介绍了 Linux 系统下用户空间与内核空间数据交换的几种方式
第一节:使用procfs实现内核交互简明教程(1)
第二节:使用procfs实现内核交互简明教程(2)
第三节:基于register_sysctl_table实现内核数据交互(Sysctl方式)
第四节:通过bootloader向内核传输启动参数
第五节:Linux的kobject机制
第六节:利用内核模块添加系统调用
第七节:使用ioctl向linux内核传递参数的方法实例
第一:procfs信息读取实现案例
procfs是比较老的一种用户态与内核态的数据交换方式,内核的很多数据都是通过这种方式出口给用户的,内核的很多参数也是通过这种方式来让用户方便设置的。
- struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
- struct proc_dir_entry *parent)
该函数用于创建一个正常的proc条目,参数name给出要建立的proc条目的名称,参数mode给出了建立的该proc条目的访问权限,参数parent指定建立的proc条目所在的目录。如果要在/proc下建立proc条目,parent应当为NULL。
-
-
-
- void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
该函数用于删除上面函数创建的proc条目,参数name给出要删除的proc条目的名称,参数parent指定建立的proc条目所在的目录。
- struct proc_dir_entry {
- ……
- read_proc_t *read_proc;
- write_proc_t *write_proc;
- ……
procfs读写信息实例:
-
-
-
-
-
-
-
-
- #include <linux/kernel.h> /* We're doing kernel work */
- #include <linux/module.h> /* Specifically, a module */
- #include <linux/proc_fs.h> /* Necessary because we use proc fs */
- #include <asm/uaccess.h> /* for get_user and put_user */
-
- #define MSG_LEN 10
- #define PROC_NAME "sample"
-
- char chflag[MSG_LEN] = "sample";
-
- static struct proc_dir_entry *sample;
-
-
-
-
-
-
-
-
-
-
- static int proc_read_rwdata(char *page, char **stat, off_t off,
- int count, int *eof, void *data)
- {
- int len;
- len = sprintf(page,"%s\n", chflag);
- return len;
- }
-
- static int proc_write_rwdata(struct file *file, const char __user *buf,
- unsigned long count, void *data)
- {
- if (count > MSG_LEN)
- count = MSG_LEN;
-
- if (copy_from_user(&chflag, buf, count))
- return -EFAULT;
-
- return count;
- }
-
-
-
- static int __init sample_init(void)
- {
- int ret = 0;
-
-
-
-
-
-
- sample = create_proc_entry(PROC_NAME, 0644, NULL);
- if (NULL == sample) {
- ret = -ENOMEM;
- goto sample_err;
- }
- sample->read_proc = &proc_read_rwdata,
- sample->write_proc = &proc_write_rwdata,
-
- printk(KERN_INFO "Create sample\n");
- return ret;
-
- sample_err:
- return ret;
- }
-
-
-
-
- static void __exit sample_exit(void)
- {
- remove_proc_entry(PROC_NAME, NULL);
- printk(KERN_INFO "Remove /proc/proc_sample\n");
- }
-
- module_init(sample_init);
- module_exit(sample_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("lewiyon <[email protected]>");
-
-
-
-
-
-
-
-
-
- #include <linux/kernel.h> /* We're doing kernel work */
- #include <linux/module.h> /* Specifically, a module */
- #include <linux/proc_fs.h> /* Necessary because we use proc fs */
- #include <asm/uaccess.h> /* for get_user and put_user */
-
- #define MESSAGE_LENGTH 80
- #define PROC_NAME "proc_sample"
-
- unsigned int flag = 100;
-
- static struct proc_dir_entry *proc_sample;
- static struct proc_dir_entry *sample, *sample_r;
-
-
-
-
-
-
-
-
-
-
- static int proc_read_data(char *page, char **stat, off_t off,
- int count, int *eof, void *data)
- {
- int len;
- len = sprintf(page, "jiffies = %ld\n", jiffies);
- return len;
- }
-
-
-
-
- static int __init sample_init(void)
- {
- int ret = 0;
-
-
-
-
-
-
- proc_sample = proc_mkdir(PROC_NAME, NULL);
- if (NULL == proc_sample) {
- ret = -ENOMEM;
- goto proc_sample_err;
- }
-
-
-
-
-
- sample = create_proc_entry("sample", 0644, proc_sample);
- if (NULL == sample) {
- ret = -ENOMEM;
- goto sample_err;
- }
-
- sample_r = create_proc_read_entry("sample_r", 0444,
- proc_sample, proc_read_data, NULL);
- if (NULL == sample_r) {
- ret = -ENOMEM;
- goto sample_r_err;
- }
-
- printk(KERN_INFO "Create sample\n");
- return ret;
-
- sample_r_err:
- remove_proc_entry("sample", proc_sample);
- sample_err:
- remove_proc_entry(PROC_NAME, NULL);
- proc_sample_err:
- return ret;
- }
-
-
-
-
- static void __exit sample_exit(void)
- {
- remove_proc_entry("sample", proc_sample);
- remove_proc_entry("sample_r", proc_sample);
- remove_proc_entry(PROC_NAME, NULL);
- printk(KERN_INFO "Remove /proc/proc_sample\n");
- }
-
- module_init(sample_init);
- module_exit(sample_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("lewiyon <[email protected]>");
在/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: */
- struct ctl_table
- {
- const char *procname;
- void *data;
- int maxlen;
- mode_t mode;
- struct ctl_table *child;
- struct ctl_table *parent;
- proc_handler *proc_handler;
- void *extra1;
- void *extra2;
- };
成员变量解释:
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 实例
-
-
-
-
-
-
-
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/sysctl.h>
-
- static int sysctl_kernusr_data = 1024;
-
- static int kernusr_callback(ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
- {
- int rc;
- int *data = table->data;
-
- printk(KERN_INFO "original value = %d\n", *data);
-
- rc = proc_dointvec(table, write, buffer, lenp, ppos);
- if (write)
- printk(KERN_INFO "this is write operation, current value = %d\n", *
- data);
-
- return rc;
- }
-
- static struct ctl_table kernusr_ctl_table[] = {
- {
- .procname = "kernusr",
- .data = &sysctl_kernusr_data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = kernusr_callback,
- },
- {
-
- },
- };
-
- static struct ctl_table_header *sysctl_header;
-
- static int __init sysctl_example_init(void)
- {
- sysctl_header = register_sysctl_table(kernusr_ctl_table);
- if (sysctl_header == NULL) {
- printk(KERN_INFO "ERR: register_sysctl_table!");
- return -1;
- }
-
- printk(KERN_INFO "sysctl register success.\n");
- return 0;
-
- }
-
- static void __exit sysctl_example_exit(void)
- {
- unregister_sysctl_table(sysctl_header);
- printk(KERN_INFO "sysctl unregister success.\n");
- }
-
- module_init(sysctl_example_init);
- module_exit(sysctl_example_exit);
- MODULE_LICENSE("GPL");
5 参考文献
http://www.ibm.com/developerworks/cn/linux/l-kerns-usrs/
http://blog.chinaunix.net/uid-7210505-id-146429.html
第三:
通过bootloader向内核传输启动参数
Linux提供了一种通过bootloader向其传输启动参数的功能,内核开发者可以通过这种方式来向内核传输数据,从而控制内核启动行为。
通常的使用方式是,定义一个分析参数的函数,而后使用内核提供的宏 __setup把它注册到内核中,该宏定义在 linux/init.h 中,因此要使用它必须包含该头文件:
__setup("para_name=", parse_func)
其中:@para_name为参数名;@parse_func为分析参数值的函数,它负责把该参数的值转换成相应的内核变量的值并设置那个内核变量。
内核为整数参数值的分析提供了函数 get_option和 get_options,前者用于分析参数值为一个整数的情况,而后者用于分析参数值为逗号分割的一系列整数的情况,对于参数值为字符串的情况,需要开发者自定义相应的分析函数。
1 setup使用案例
在文件./mm/slub.c
- static int __init setup_slub_min_order(char *str)
- {
- get_option(&str, &slub_min_order);
- return 1;
- }
- __setup("slub_min_order=", setup_slub_min_order);
-
- static int __init setup_slub_max_order(char *str)
- {
- get_option(&str, &slub_max_order);
- slub_max_order = min(slub_max_order, MAX_ORDER - 1);
- return 1;
- }
- __setup("slub_max_order=", setup_slub_max_order);
2 内核程序kern-boot-params测试
在源代码包中的内核程序kern-boot-params.c说明了三种情况的使用。该程序列举了参数为一个整数、逗号分割的整数串以及字符串三种情况,读者要想测试该程序,需要把该程序拷贝到要使用的内核的源码目录树的一个目录下,为了避免与内核其他部分混淆,作者建议在内核源码树的根目录下创建一个新目录,如examples,然后把该程序拷贝到 examples 目录下并重新命名为 setup_example.c,并且为该目录创建一个 Makefile文件:
Makefile 仅需这一行就足够了,然后需要修改源码树的根目录下的 Makefile文件的一行,把下面行
修改为
注意:如果读者创建的新目录和重新命名的文件名与上面不同,需要修改上面所说 Makefile文件相应的位置。做完以上工作就可以按照内核构建步骤去构建新的内核。
-
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/string.h>
-
- #define MAX_SIZE 5
-
- static int setup_example_int;
- static int setup_example_int_array[MAX_SIZE];
- static char setup_example_string[16];
-
- static int __init parse_int(char * s)
- {
- int ret;
-
- ret = get_option(&s, &setup_example_int);
- if (ret == 1) {
- printk("setup_example_int=%d\n", setup_example_int);
- }
-
- return 1;
- }
-
- static int __init parse_int_string(char *s)
- {
- char * ret_str;
- int i;
-
- ret_str = get_options(s, MAX_SIZE, setup_example_int_array);
- if (*ret_str != '\0') {
- printk("incorrect setup_example_int_array paramters: %s\n", ret_str);
- }
- else {
- printk("setup_example_int_array=");
- for (i=1; i<MAX_SIZE; i++) {
- printk("%d", setup_example_int_array[i]);
- if (i < (MAX_SIZE -1)) {
- printk(",");
- }
- }
- printk("\n");
- printk("setup_example_int_array includes %d intergers\n", setup_example_int_array[0]);
- }
-
- return 1;
- }
-
- static int __init parse_string(char *s)
- {
- if (strlen(s) > 15) {
- printk("Too long setup_example_string parameter, \n");
- printk("maximum length is less than or equal to 15\n");
- }
- else {
- memcpy(setup_example_string, s, strlen(s) + 1);
- printk("setup_example_string=%s\n", setup_example_string);
- }
- return 1;
- }
-
- __setup("setup_example_int=", parse_int);
- __setup("setup_example_int_array=", parse_int_string);
- __setup("setup_example_string=", parse_string);
3 变量配置方法
在构建好内核并设置好lilo或grub为该内核的启动条目后,就可以启动该内核,然后使用lilo或grub的编辑功能为该内核的启动参数行增加如下参数串:
setup_example_int=1234 setup_example_int_array=100,200,300,400 setup_example_string=Thisisatest
当然,该参数串也可以直接写入到lilo或grub的配置文件中对应于该新内核的内核命令行参数串中。读者可以使用其它参数值来测试该功能.
- [root@RedHat ~]# cat /boot/grub/menu.lst
- # grub.conf generated by anaconda
- #
- # Note that you do not have to rerun grub after making changes to this file
- # NOTICE: You have a /boot partition. This means that
- # all kernel and initrd paths are relative to /boot/, eg.
- # root (hd0,0)
- # kernel /vmlinuz-version ro root=/dev/mapper/VolGroup-lv_root
- # initrd /initrd-[generic-]version.img
- #boot=/dev/sda
- default=0
- timeout=3
- splashimage=(hd0,0)/grub/splash.xpm.gz
- hiddenmenu
- title Red Hat Enterprise Linux Server (3.2.0trace)
- root (hd0,0)
- 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
- initrd /initramfs-3.2.0trace.img
-
- title Red Hat Enterprise Linux (2.6.32-71.el6.i686)
- root (hd0,0)
- 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
- initrd /initramfs-2.6.32-71.el6.i686.img
- [root@RedHat ~]#
使用dmesg | grep setup来查看该程序的输出。
- [root@RedHat ~]# dmesg | grep setup
- setup_percpu: NR_CPUS:32 nr_cpumask_bits:32 nr_cpu_ids:1 nr_node_ids:1
-
- 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
- setup_example_int=1234
- setup_example_int_array=10,20,30,40,
- setup_example_int_array includes 4 intergers
- setup_example_string=lewiyon
- [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结构中:
- struct kobj_type {
- void (*release)(struct kobject *);
- struct sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
- };
- struct attribute {
- char *name;
- struct module *owner;
- mode_t mode;
- };
sysfs 读写这些属性是由 kobj_type->sysfs_ops成员中的函数完成的:
- struct sysfs_ops {
- ssize_t (*show)(struct kobject *, struct attribute *,char *);
- ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
- const void *(*namespace)(struct kobject *, const struct attribute *);
- };
当用户空间读取一个属性时,内核会使用指向 kobject的指针(kobj)和正确的属性结构(*attr)来调用show方法,该方法将给定属性值编码进缓冲(buffer)(注意不要越界( PAGE_SIZE字节)),并返回实际数据长度。sysfs的约定要求每个属性应当包含一个可读值;若返回大量信息,需将它分为多个属性.
也可对所有 kobject关联的属性使用同一个 show方法,用传递到函数的 attr指针来判断所请求的属性。有的 show方法包含对属性名字的检查。有的show方法会将属性结构嵌入另一个结构,这个结构包含需要返回属性值的信息,这时可用container_of获得上层结构的指针以返回属性值的信息。
store 方法将存在缓冲(buffer)的数据( size为数据的长度,不能超过 PAGE_SIZE )解码并保存新值到属性(*attr),返回实际解码的字节数。store方法只在拥有属性的写权限时才能被调用。此时注意:接收来自用户空间的数据一定要验证其合法性。如果到数据不匹配,返回一个负的错误值。
3 案例
-
-
-
-
-
-
-
- #include <linux/kobject.h>
- #include <linux/string.h>
- #include <linux/sysfs.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/init.h>
-
-
-
-
- struct foo_obj {
- struct kobject kobj;
- int foo;
- int baz;
- int bar;
- };
-
-
- #define to_foo_obj(x) container_of(x, struct foo_obj, kobj)
-
-
- struct foo_attribute {
- struct attribute attr;
- ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
- ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
- };
-
- #define to_foo_attr(x) container_of(x, struct foo_attribute, attr)
-
-
-
-
- static ssize_t foo_attr_show(struct kobject *kobj,
- struct attribute *attr,
- char *buf)
- {
- struct foo_attribute *attribute;
- struct foo_obj *foo;
-
- attribute = to_foo_attr(attr);
- foo = to_foo_obj(kobj);
-
- if (!attribute->show)
- return -EIO;
-
- return attribute->show(foo, attribute, buf);
- }
-
-
-
-
- static ssize_t foo_attr_store(struct kobject *kobj,
- struct attribute *attr,
- const char *buf, size_t len)
- {
- struct foo_attribute *attribute;
- struct foo_obj *foo;
-
- attribute = to_foo_attr(attr);
- foo = to_foo_obj(kobj);
-
- if (!attribute->store)
- return -EIO;
-
- return attribute->store(foo, attribute, buf, len);
- }
-
-
-
-
- static const struct sysfs_ops foo_sysfs_ops = {
- .show = foo_attr_show,
- .store = foo_attr_store,
- };
-
-
-
-
-
- static void foo_release(struct kobject *kobj)
- {
- struct foo_obj *foo;
-
- foo = to_foo_obj(kobj);
- kfree(foo);
- }
-
-
-
-
- static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
- char *buf)
- {
- return sprintf(buf, "%d\n", foo_obj->foo);
- }
-
-
-
-
- static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
- const char *buf, size_t count)
- {
- sscanf(buf, "%du", &foo_obj->foo);
- return count;
- }
-
- static struct foo_attribute foo_attribute =
- __ATTR(foo, 0666, foo_show, foo_store);
-
-
-
-
- static struct attribute *foo_default_attrs[] = {
- &foo_attribute.attr,
- NULL,
- };
-
-
-
-
-
- static struct kobj_type foo_ktype = {
- .sysfs_ops = &foo_sysfs_ops,
- .release = foo_release,
- .default_attrs = foo_default_attrs,
- };
-
- static struct kset *example_kset;
- static struct foo_obj *foo_obj;
-
- static struct foo_obj *create_foo_obj(const char *name)
- {
- struct foo_obj *foo;
- int retval;
-
-
- foo = kzalloc(sizeof(*foo), GFP_KERNEL);
- if (!foo)
- return NULL;
-
- foo->kobj.kset = example_kset;
-
-
-
-
-
-
- retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
- if (retval) {
-
- kobject_put(&foo->kobj);
- return NULL;
- }
-
-
-
-
-
-
- kobject_uevent(&foo->kobj, KOBJ_ADD);
-
- return foo;
- }
-
- static void destroy_foo_obj(struct foo_obj *foo)
- {
-
- kobject_put(&foo->kobj);
- }
-
- static int __init example_init(void)
- {
-
-
-
-
- example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
- if (!example_kset)
- return -ENOMEM;
-
- foo_obj = create_foo_obj("foo");
- if (!foo_obj)
- goto foo_error;
-
- return 0;
-
- foo_error:
- return -EINVAL;
- }
-
- static void __exit example_exit(void)
- {
- destroy_foo_obj(foo_obj);
- kset_unregister(example_kset);
- }
-
- module_init(example_init);
- module_exit(example_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Younger Liu <[email protected]>");
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