目录
注:
内核版本:3.18.20
一. kset简介
1.1 定义
kset是具有相同类型的kobject的集合,在sysfs中体现成一个目录,在内核中用kset数据结构表示,定义为:
/*1. kset定义*/
/**
* struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
*
* A kset defines a group of kobjects. They can be individually
* different "types" but overall these kobjects all want to be grouped
* together and operated on in the same manner. ksets are used to
* define the attribute callbacks and other common events that happen to
* a kobject.
*
* @list: the list of all kobjects for this kset
* @list_lock: a lock for iterating over the kobjects
* @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
* @uevent_ops: the set of uevent operations for this kset. These are
* called whenever a kobject has something happen to it so that the kset
* can add new environment variables, or filter out the uevents if so
* desired.
*/
struct kset {
struct list_head list; /*kobject容器,即连接该kset中所有kobject的链表头*/
spinlock_t list_lock; /*链表自旋锁*/
struct kobject kobj; /*内嵌的kobject,即kset继承与kobject*/
const struct kset_uevent_ops *uevent_ops; /*处理热插拔事件的操作集合*/
};
/*2.uevent操作集合:消息回调函数:*/
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char *(* const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
/*注:
问题一:这三个函数什么时候调用?
答:当该kset所管理的kobject和kset状态发生变化时(如被加入,移动),这三个函数将被调用。
(例:kobject_uevent调用)
问题二:这三个函数的功能是什么?
答:filter:决定是否将事件传递到用户空间。如果 filter返回 0,将不传递事件。(例: uevent_filter)
name:用于将字符串传递给用户空间的热插拔处理程序。
uevent:将用户空间需要的参数添加到环境变量中。(例:dev_uevent)
*/
/*3.内核对象的动作类型*/
/*
* The actions here must match the index to the string array
* in lib/kobject_uevent.c
*
* Do not add new actions here without checking with the driver-core
* maintainers. Action strings are not meant to express subsystem
* or device specific properties. In most cases you want to send a
* kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
* specific variables added to the event environment.
*/
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
1.2 热插拔事件
在Linux系统中,当系统配置发生变化时,如:添加kset到系统;移动kobject, 一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中相应的处理程序(如udev,mdev)被调用, 这些处理程序会通过加载驱动程序, 创建设备节点等来响应热插拔事件。
1.2.1 消息通知的两种方式
- udev --netlink (udev 通常用于pc机的消息通知)
- mdev --call usermodehelper (mdev 通常用于嵌入式设备的消息通知)
1.2.2 操作接口
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
二. kobject与kset的关系
2.1 继承关系
- 结构体看:kset包含有一个kobject成员对象。
- 接口函数看:初始化,注册,注销,都会先处理kobject相关工作。
- kset继承了kobject的name、kref、sd等。
2.2 层级关系
- kset可以作为kobject的父目录。
- kset作为具有共同特征的kobject的容器。
2.3 具体继承关系的体现:
static inline struct kset *to_kset(struct kobject *kobj)
{
return kobj ? container_of(kobj, struct kset, kobj) : NULL;
}
三. 内核接口
/*1. 初始化kset*/
extern void kset_init(struct kset *kset);
/*2. 向内核注册一个kset*/
extern int __must_check kset_register(struct kset *kset);
/*3. 注销一个kset*/
extern void kset_unregister(struct kset *kset);
/*4. 创建并添加一个kset*/
extern struct kset * __must_check kset_create_and_add(const char *name,
const struct kset_uevent_ops *u,
struct kobject *parent_kobj);
/*5. 根据kset中的kobject结构获取kset结构地址*/
static inline struct kset *to_kset(struct kobject *kobj)
/*6. kset引用计数加一*/
static inline struct kset *kset_get(struct kset *k)
/*7. kset引用计数减一*/
static inline void kset_put(struct kset *k)
/*8. 查找kset容器中所有的kobject*/
extern struct kobject *kset_find_obj(struct kset *, const char *);
四. kset实例
4.1 Makefile
ifneq ($(KERNELRELEASE),)
obj-m := kset_test.o
else
PWD := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers
endif
4.2 实例1
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/kobject.h>
/*from class subsystem*/
static struct kset *devices_set = NULL;
static struct kobject *kobj_dev = NULL;
static void kobj_dev_release(struct kobject *kobj)
{
printk("kobject: '%s' (%p): %s\n", kobject_name(kobj), kobj, __func__);
kfree(kobj);
}
static struct kobj_type devices_ktype = {
.release = kobj_dev_release,
};
static int devices_uevent_filter(struct kset *kset, struct kobject *kobj)
{
printk("UEVENT: filter kobj %s. \n", kobj->name);
return 1;
}
static const char *devices_uevent_name(struct kset *kset, struct kobject *kobj)
{
static char buf[20];
printk("UEVENT: name kobj %s. \n", kobj->name);
sprintf(buf, "%s", "kset_test");
return buf;
}
static int devices_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
int i = 0;
printk("UEVENT: uevent kobj %s. \n", kobj->name);
while (i < env->envp_idx) {
printk("%s. \n", env->envp[i]);
i++;
}
return 0;
}
static const struct kset_uevent_ops devices_uevent_ops = {
.filter = devices_uevent_filter,
.name = devices_uevent_name,
.uevent = devices_uevent,
};
static int kset_test_init(void)
{
int retval = 0;
devices_set = kset_create_and_add("devices_set", &devices_uevent_ops, NULL);
if (!devices_set)
return -ENOMEM;
kobj_dev = kzalloc(sizeof(struct kobject), GFP_KERNEL);
if (!kobj_dev) {
retval = -ENOMEM;
goto alloc_fail;
}
/*初始化kobject相关属性*/
kobj_dev->kset = devices_set;
kobject_init(kobj_dev, &devices_ktype);
retval = kobject_add(kobj_dev, NULL, "%s_dev", "1");
if (retval)
goto out;
/*这里会调用kset的 filter、name、uevent回调函数*/
kobject_uevent(kobj_dev, KOBJ_ADD);
printk("kset_test init. \n");
return 0;
out:
kobject_put(kobj_dev);
alloc_fail:
kset_unregister(devices_set);
return retval;
}
static void kset_test_exit(void)
{
kobject_del(kobj_dev);
kobject_put(kobj_dev);
kset_unregister(devices_set);
printk("kset test exit. \n");
}
module_init(kset_test_init);
module_exit(kset_test_exit);
MODULE_AUTHOR("vector");
MODULE_LICENSE("Dual BSD/GPL");
4.3 实例2
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/kobject.h>
/*from platform subsys, bus type
ql-ol-kernel\drivers\base*/
static struct kset *bus_set = NULL;
static struct kset *subsys = NULL;
static struct kset *devices = NULL;
static struct kset *drivers = NULL;
static void kset_release(struct kobject *kobj)
{
printk("subsys kobj: '%s' (%p): %s\n", kobject_name(kobj), kobj, __func__);
kfree(kobj);
}
/*用于分发到各个具体的attr属性文件中的show,store函数中*/
static ssize_t kset_ops_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct kobj_attribute *my_attr;
ssize_t ret = -EIO;
my_attr = container_of(attr, struct kobj_attribute, attr);
if (my_attr->show)
ret = my_attr->show(kobj, my_attr, buf);
return ret;
}
static ssize_t kset_ops_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
struct kobj_attribute *my_attr;
ssize_t ret = -EIO;
my_attr = container_of(attr, struct kobj_attribute, attr);
if (my_attr->store)
ret = my_attr->store(kobj, my_attr, buf, count);
return ret;
}
static const struct sysfs_ops kset_sysfs_ops = {
.show = kset_ops_show,
.store = kset_ops_store,
};
static struct kobj_type kset_ktype = {
.sysfs_ops = &kset_sysfs_ops,
.release = kset_release,
};
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
printk("UEVENT: filter kobj %s. \n", kobj->name);
return 1;
}
static const char *bus_uevent_name(struct kset *kset, struct kobject *kobj)
{
static char buf[20];
printk("UEVENT: name kobj %s. \n", kobj->name);
sprintf(buf, "%s", "kset_test");
return buf;
}
static int bus_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
int i = 0;
printk("UEVENT: uevent kobj %s. \n", kobj->name);
while (i < env->envp_idx) {
printk("%s. \n", env->envp[i]);
i++;
}
return 0;
}
static const struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
.name = bus_uevent_name,
.uevent = bus_uevent,
};
static int kset_create_file(struct kset *kset, struct kobj_attribute *kobj_attr)
{
int error;
error = sysfs_create_file(&kset->kobj, &kobj_attr->attr);
printk("uevent file: %d", error);
return 0;
}
static int kset_remove_file(struct kset *kset, struct kobj_attribute *kobj_attr)
{
sysfs_remove_file(&kset->kobj, &kobj_attr->attr);
return 0;
}
static ssize_t uevent_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
printk("%s :in\n", __FUNCTION__);
strcpy(buf, "uevent show");
return strlen("uevent show");
}
static ssize_t uevent_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
printk("%s :in, %s!\n", __FUNCTION__, buf);
return count;
}
static struct kobj_attribute kset_attr_uevent =
__ATTR(uevent, 0644, uevent_show, uevent_store);
static int kset_test_init(void)
{
int retval = 0;
bus_set = kset_create_and_add("bus_set", &bus_uevent_ops, NULL);
if (!bus_set)
return -ENOMEM;
/*该kset没有直接向用户发送uevent事件的能力,但是可发给它所属的kset,
然后由上级kset发event给用户。*/
subsys = kzalloc(sizeof(struct kset), GFP_KERNEL);
if (!subsys) {
retval = -ENOMEM;
goto alloc_fail;
}
retval = kobject_set_name(&subsys->kobj, "%s", "platform");
if (retval)
goto out;
subsys->kobj.ktype = &kset_ktype;
subsys->kobj.kset = bus_set;
/*uevent--1*/
retval = kset_register(subsys);
if (retval)
goto out;
retval = kset_create_file(subsys, &kset_attr_uevent);
if (retval)
goto files_fail;
/*uevent--2*/
devices = kset_create_and_add("devices", NULL, &subsys->kobj);
if (!devices) {
retval = -ENOMEM;
goto devices_fail;
}
/*uevent--3*/
drivers = kset_create_and_add("drivers", NULL, &subsys->kobj);
if (!drivers) {
retval = -ENOMEM;
goto drivers_fail;
}
/*uevent--4*/
/*这里会调用kset的 filter、name、uevent回调函数*/
kobject_uevent(&drivers->kobj, KOBJ_ADD);
printk("kset_test init. \n");
return 0;
drivers_fail:
kset_unregister(devices);
devices_fail:
kset_remove_file(subsys, &kset_attr_uevent);
files_fail:
kset_unregister(subsys);
out:
kfree(subsys);
alloc_fail:
kset_unregister(bus_set);
return retval;
}
static void kset_test_exit(void)
{
kset_unregister(drivers);
kset_unregister(devices);
kset_remove_file(subsys, &kset_attr_uevent);
kset_unregister(subsys);
kset_unregister(bus_set);
printk("kset test exit. \n");
}
module_init(kset_test_init);
module_exit(kset_test_exit);
MODULE_AUTHOR("vector");
MODULE_LICENSE("Dual BSD/GPL");
4.4 udev--netlink
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
struct sockaddr_nl {
unsigned short nl_family; /*AF_NETLINK*/
unsigned short nl_pad; /*zero*/
unsigned int nl_pid; /*port ID*/
unsigned int nl_groups; /*multicast groups mask*/
};
static int init_hotplug_sock(void)
{
struct sockaddr_nl snl;
const int buffer_size = 16 * 1024 *1024;
int retval;
memset(&snl, 0x00, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = 1;
int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, 15);
if (-1 == hotplug_sock) {
printf("error getting socket: %s\n", strerror(errno));
return -1;
}
/*set receive buffer size*/
setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffer_size, sizeof(buffer_size));
retval = bind(hotplug_sock, (struct sockaddr_nl *)&snl, sizeof(struct sockaddr_nl));
if (retval < 0) {
printf("bind failed: %s\n", strerror(errno));
close(hotplug_sock);
hotplug_sock = -1;
return -1;
}
return hotplug_sock;
}
#define UEVENT_BUFFER_SIZE 2048
int main(int argc, char *argv[])
{
int hotplug_sock = init_hotplug_sock();
while (1) {
char buf[UEVENT_BUFFER_SIZE * 2] = {0};
recv(hotplug_sock, &buf, sizeof(buf), 0);
printf("vec:%s\n", buf);
}
return 0;
}