给出一个完整的源码来展示如何创建、初始化并向系统中添加一个对象,以及如何通过sysfs文件系统接口在用户空间和内核空间进行沟通,另一个有趣的事情是它通过/sbin/hotplug机制来通知用户空间某一个kobject状态的变化。在这个例子中,我们将用自己编译的一个应用程序取代系统的/sbin/hotplug,该应用程序会打出一些环境变量,记在/var/log/messages文件中。
kobject.c
kobject.c中中实现sysfs文件接口下文件的创建,及其建立变量与文件之间的联系
#include <linux/module.h> #include <linux/init.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/slab.h> static struct kobject *parent; static struct kobject *child; static struct kset *c_kset; static unsigned long flag; ssize_t att_show(struct kobject *kobj, struct attribute *attr, char *buf){ size_t count = 0; count += sprintf(&buf[count],"%lu\n",flag); return count; } ssize_t att_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count){ flag = buf[0] - '0'; switch(flag){ case 0: kobject_uevent(kobj,KOBJ_ADD); break; case 1: kobject_uevent(kobj,KOBJ_REMOVE); break; case 2: kobject_uevent(kobj,KOBJ_CHANGE); break; case 3: kobject_uevent(kobj,KOBJ_MOVE); break; case 4: kobject_uevent(kobj,KOBJ_ONLINE); break; case 5: kobject_uevent(kobj,KOBJ_OFFLINE); break; } return count; } static struct attribute cld_att = { .name = "cldatt", .mode = S_IRUGO | S_IWUSR, }; static const struct sysfs_ops att_ops = { .show = att_show, .store = att_store, }; static struct kobj_type cld_ktype = { .sysfs_ops = &att_ops, }; static int kobj_demo_init(void){ int err; parent = kobject_create_and_add("pa_obj", NULL); child = kzalloc(sizeof(* child),GFP_KERNEL); if(!child){ return PTR_ERR(child); } //一个能够通知用户空间状态变化的kobject必須隶屬于某一个kset,也就是所谓的 //subsystem,所以此处给内核对象chlld创建一个kset对象c_kset c_kset = kset_create_and_add("c_kset", NULL, parent); if(!c_kset){ return -1; } child-> kset = c_kset; err = kobject_init_and_add(child,&cld_ktype,parent,"cld_obj"); if(err) return err; err = sysfs_create_file(child,&cld_att); return err; } static void kobj_demo_exit(void) { sysfs_remove_file(child,&cld_att); kset_unregister(c_kset); kobject_del(child); kobject_del(parent); } module_init(kobj_demo_init); module_exit(kobj_demo_exit); MODULE_LICENSE("GPL");
app文件原本用于测试取代系统的/sbin/hotplug,该应用程序会打出一些环境变量,记在/var/log/messages文件中。但只在使用时,这个功能并没有实现。
app
#include <stdio.h> #include <syslog.h> extern char **environ; int main(int argc,char *argv[]){ char **var; syslog(LOG_INFO | LOG_LOCAL0,"---------------------------\n"); syslog(LOG_INFO | LOG_LOCAL0,"argv[1]=%s\n",argv[1]); for(var = environ; *var != NULL;++var){ syslog(LOG_INFO | LOG_LOCAL0,"argv[1]=%s\n",argv[1]); } syslog(LOG_INFO | LOG_LOCAL0,"---------------------------\n"); return 0; }
Makefile
- make 编译项目
- make file 在存放.ko文件目录中创建对应项目的目录
- make install 将*.ko及其应用测试文件移动到根文件中
所有路径需要按照自己的路劲来修改
#make 编译项目 #make file 在存放.ko文件目录中创建对应项目的目录 #make install 将*.ko及其应用测试文件移动到根文件中 # 1. 使用不同的开发板内核时, 一定要修改KERN_DIR # 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: # 2.1 ARCH, 比如: export ARCH=arm64 # 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- # 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin # 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, # 请参考各开发板的高级用户使用手册 # ROOTFS_DIR 根文件系统中存放 *.ko文件所在目录 # PROJECT_NAME 在存放.ko文件目录中创建对应项目的目录 # DRIVER_NAME 项目中需要编译出.ko来的驱动 # APP_NAME 项目中的应用测试文件 #make 编译项目 #make file 在存放.ko文件目录中创建对应项目的目录 #make install 将*.ko及其应用测试文件移动到根文件中 KERN_DIR = /home/alientek/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek ROOTFS_DIR = /home/alientek/linux/nfs/rootfs/experiment #项目名字 PROJECT_NAME = kobject #各驱动名字,ko DRIVER_NAME1 = kobject DRIVER_NAME2 = #app名字 APP_NAME = app all: make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)arm-linux-gnueabihf-gcc -o $(APP_NAME) $(APP_NAME).c clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order rm -f $(APP_NAME) file: mkdir $(ROOTFS_DIR)/$(PROJECT_NAME) install: cp *.ko $(ROOTFS_DIR)/$(PROJECT_NAME) cp $(APP_NAME) $(ROOTFS_DIR)/$(PROJECT_NAME) # 参考内核源码drivers/char/ipmi/Makefile # 要想把a.c, b.c编译成ab.ko, 可以这样指定: # ab-y := a.o b.o # obj-m += ab.o obj-m += $(DRIVER_NAME1).o
结果
将编译好的内核模块kodemo.ko通过insmod加入系统后,除了在/sys目录下生成parent与child内核对象所对应的入口点pa_obj
还会在/sys/pa_obj/cld_obj目录下生成child内核对象的一个属性文件cldatt:
通过sysfs文件接口来改变内核模块中的变量flag
通过fasync接口也可以实现文件接口来修改内核模块中的变量,详细见之前的文章。