原文地址:http://blog.chinaunix.net/uid-20786208-id-4158792.html
对于热插拔,当然网上有很多资料,包括刚开始我也简单的讲过usb 热插拔. 当时说道2.6以后的内核都用uevent机制来代替老的机制.(具体从那个版本改变的可以看linux官网开发记录)
由于在看《深入理解linux网络内幕》中网络设备初始化一节中又提到了,而看《深入linux设备驱动程序内核机制》中详细讲解.当然这里我也自己分析了一把^^,我们就接着usb热插拔事件继续说事: 当有usb插板事件的时候,会触发到drivers/usb/core/hub.c 中
/* Handle physical or logical connection change events.
* This routine is called when:
* a port connection-change occurs;
* a port enable-change occurs (often caused by EMI);
* usb_reset_and_verify_device() encounters changed descriptors (as from
* a firmware download)
* caller already locked the hub
*/
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
...
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev);
if (status) {
spin_lock_irq(&device_state_lock);
hdev->children[port1-1] = NULL;
spin_unlock_irq(&device_state_lock);
}
}
...
}
这里我们只是做一个简单回忆 ,重点是 usb_new_device函数:
点击(此处)折叠或打开
-
/**
-
* usb_new_device - perform initial device setup (usbcore-internal)
-
* @udev: newly addressed device (in ADDRESS
state)
-
*
-
* This is called with devices which have been detected but not fully
-
* enumerated. The device descriptor is available, but not descriptors
-
* for any device configuration. The caller must have locked either
-
* the parent hub (if udev is a
normal device) or else the
-
* usb_bus_list_lock (if udev is a
root hub). The parent's pointer to
-
* udev has already been installed, but udev is not yet
visible through
-
* sysfs or other filesystem code.
-
*
-
* It will return if the device is configured properly or not. Zero if
-
* the interface was registered with the driver core; else a negative
-
* errno value.
-
*
-
* This call is synchronous, and may not be
used in an interrupt context.
-
*
-
* Only the hub driver or root-hub registrar should ever call this.
-
*/
-
int usb_new_device(struct usb_device *udev)
-
{
-
int err;
-
-
if (udev->parent) {
-
/* Increment the parent's count of unsuspended children */
-
usb_autoresume_device(udev->parent);
-
-
/* Initialize non-root-hub
device wakeup to disabled;
-
* device (un)configuration controls wakeup capable
-
* sysfs power/wakeup controls wakeup enabled/disabled
-
*/
-
device_init_wakeup(&udev->dev, 0);
-
}
-
-
err = usb_enumerate_device(udev); /* Read
descriptors */
-
if (err < 0)
-
goto fail;
-
dev_dbg(&udev->dev, "udev
%d, busnum %d, minor = %d\n",
-
udev->devnum, udev->bus->busnum,
-
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
-
/* export the usbdev device-node for libusb */
-
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
-
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
-
-
/* Tell the */
-
announce_device(udev);
-
-
if (udev->serial)
-
add_device_randomness(udev->serial, strlen(udev->serial));
-
if (udev->product)
-
add_device_randomness(udev->product, strlen(udev->product));
-
if (udev->manufacturer)
-
add_device_randomness(udev->manufacturer,
-
strlen(udev->manufacturer));
-
-
/* Register the device. The device driver is responsible
-
* for configuring the device and invoking the add-device
-
* notifier chain (used by usbfs and possibly others).
-
*/
-
err = device_add(&udev->dev);
-
if (err) {
-
dev_err(&udev->dev, "can't
device_add, error %d\n", err);
-
goto fail;
-
}
-
-
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
-
return err;
-
-
fail:
-
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-
usb_stop_pm(udev);
-
return err;
- }
里面的具体的一些初始化这里不在一一说明,我们只看device_add函数:
点击(此处)折叠或打开
-
/**
-
* device_add - add device to device hierarchy.
-
* @dev: device.
-
*
-
* This is part 2 of device_register(), though
may be called
-
* separately _iff_ device_initialize() has been called separately.
-
*
-
* This adds @dev to the kobject hierarchy via kobject_add(), adds
it
-
* to the global and sibling lists for the
device, then
-
* adds it to the other relevant subsystems of the driver model.
-
*
-
* NOTE: _Never_ directly free @dev after calling this function, even
-
* if it returned an Always use put_device() to give
up your
-
* reference instead.
-
*/
-
int device_add(struct device *dev)
-
{
-
struct device *parent = NULL;
-
struct class_interface *class_intf;
-
int error = -EINVAL;
-
-
dev = get_device(dev);
-
if (!dev)
-
goto done;
-
-
if (!dev->p) {
-
error = device_private_init(dev);
-
if (error)
-
goto done;
-
}
-
-
/*
-
* for statically allocated devices, which should all be converted
-
* some day, we need to initialize
the name. We prevent reading back
-
* the name, and force the use of dev_name()
-
*/
-
if (dev->init_name) {
-
dev_set_name(dev, "%s", dev->init_name);
-
dev->init_name = NULL;
-
}
-
-
if (!dev_name(dev))
-
goto name_error;
-
-
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
-
-
parent = get_device(dev->parent);
-
setup_parent(dev, parent);
-
-
/* use parent numa_node */
-
if (parent)
-
set_dev_node(dev, dev_to_node(parent));
-
-
/* first, register with generic layer. */
-
/* we require the name to be set before, and pass NULL */
-
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
-
if (error)
-
goto Error;
-
-
/* notify platform of device entry */
-
if (platform_notify)
-
platform_notify(dev);
-
-
error = device_create_file(dev, &uevent_attr);
-
if (error)
-
goto attrError;
-
-
if (MAJOR(dev->devt)) {
-
error = device_create_file(dev, &devt_attr);
-
if (error)
-
goto ueventattrError;
-
-
error = device_create_sys_dev_entry(dev);
-
if (error)
-
goto devtattrError;
-
-
devtmpfs_create_node(dev);
-
}
-
-
error = device_add_class_symlinks(dev);
-
if (error)
-
goto SymlinkError;
-
error = device_add_attrs(dev);
-
if (error)
-
goto AttrsError;
-
error = bus_add_device(dev);
-
if (error)
-
goto BusError;
-
error = dpm_sysfs_add(dev);
-
if (error)
-
goto DPMError;
-
device_pm_add(dev);
-
-
/* Notify clients of device addition. This call must
come
-
* after dpm_sysf_add() and before
kobject_uevent().
-
*/
-
if (dev->bus)
-
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
-
BUS_NOTIFY_ADD_DEVICE, dev);
-
-
kobject_uevent(&dev->kobj, KOBJ_ADD);
-
bus_probe_device(dev);
-
if (parent)
-
klist_add_tail(&dev->p->knode_parent,
-
&parent->p->klist_children);
-
-
if (dev->class) {
-
mutex_lock(&dev->class->p->class_mutex);
-
/* tie the class to the
device */
-
klist_add_tail(&dev->knode_class,
-
&dev->class->p->class_devices);
-
-
/* notify any interfaces that the device is here */
-
list_for_each_entry(class_intf,
-
&dev->class->p->class_interfaces, node)
-
if (class_intf->add_dev)
-
class_intf->add_dev(dev, class_intf);
-
mutex_unlock(&dev->class->p->class_mutex);
-
}
-
done:
-
put_device(dev);
-
return error;
-
DPMError:
-
bus_remove_device(dev);
-
BusError:
-
device_remove_attrs(dev);
-
AttrsError:
-
device_remove_class_symlinks(dev);
-
SymlinkError:
-
if (MAJOR(dev->devt))
-
device_remove_sys_dev_entry(dev);
-
devtattrError:
-
if (MAJOR(dev->devt))
-
device_remove_file(dev, &devt_attr);
-
ueventattrError:
-
device_remove_file(dev, &uevent_attr);
-
attrError:
-
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
-
kobject_del(&dev->kobj);
-
Error:
-
cleanup_device_parent(dev);
-
if (parent)
-
put_device(parent);
-
name_error:
-
kfree(dev->p);
-
dev->p = NULL;
-
goto done;
- }
这个函数除了对设备模型做了些工作,还通知了一些事件. kobject_uevent(&dev->kobj, KOBJ_ADD);忘了说上边的代码已经到了drivers/base/core.c中. 我们看到KOBJ_ADD操作。而具体支持的操作看头文件include/linux/kobject.h
/*
* 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
};
那么kobject_uevent做了什么工作呢,我们知道,新内核机制已经变成uevent模式.
lib/kobject_uevent.c
点击(此处)折叠或打开
-
/**
-
* kobject_uevent - notify userspace by ending an uevent
-
*
-
* @action: action that is happening
-
* @kobj: struct kobject that the action is happening to
-
*
-
* Returns 0 if kobject_uevent() is completed
with success or the
-
* corresponding error when it fails.
-
*/
-
int kobject_uevent(struct kobject *kobj, enum
kobject_action action)
-
{
-
return kobject_uevent_env(kobj, action, NULL);
-
}
- EXPORT_SYMBOL_GPL(kobject_uevent);
好吧,调用了 kobject_uevent_env函数.
点击(此处)折叠或打开
-
/**
-
* kobject_uevent_env - send an uevent with environmental data
-
*
-
* @action: action that is happening
-
* @kobj: struct kobject that the action is happening to
-
* @envp_ext: pointer to environmental data
-
*
-
* Returns 0 if kobject_uevent() is completed
with success or the
-
* corresponding error when it fails.
-
*/
-
int kobject_uevent_env(struct kobject *kobj, enum
kobject_action action,
-
char *envp_ext[])
-
{
-
struct kobj_uevent_env *env;
-
const char *action_string = kobject_actions[action];
-
const char *devpath = NULL;
-
const char *subsystem;
-
struct kobject *top_kobj;
-
struct kset *kset;
-
struct kset_uevent_ops *uevent_ops;
-
u64 seq;
-
int i = 0;
-
int retval = 0;
-
-
pr_debug("kobject: '%s' (%p): %s\n",
-
kobject_name(kobj), kobj, __func__);
-
-
/* search the kset we belong to */
-
top_kobj = kobj;
-
while (!top_kobj->kset && top_kobj->parent)
-
top_kobj = top_kobj->parent;
-
-
if (!top_kobj->kset) {
-
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
-
"without kset!\n", kobject_name(kobj), kobj,
-
__func__);
-
return -EINVAL;
-
}
-
-
kset = top_kobj->kset;
-
uevent_ops = kset->uevent_ops;
-
-
/* skip the event, if uevent_suppress is set*/
-
if (kobj->uevent_suppress) {
-
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
-
"caused the event to drop!\n",
-
kobject_name(kobj), kobj, __func__);
-
return 0;
-
}
-
/* skip the event, if the filter returns
zero. */
-
if (uevent_ops && uevent_ops->filter)
-
if (!uevent_ops->filter(kset, kobj)) {
-
pr_debug("kobject: '%s' (%p): %s: filter function "
-
"caused the event to drop!\n",
-
kobject_name(kobj), kobj, __func__);
-
return 0;
-
}
-
-
/* originating subsystem */
-
if (uevent_ops && uevent_ops->name)
-
subsystem = uevent_ops->name(kset, kobj);
-
else
-
subsystem = kobject_name(&kset->kobj);
-
if (!subsystem) {
-
pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
-
"event to drop!\n", kobject_name(kobj), kobj,
-
__func__);
-
return 0;
-
}
-
-
/* environment buffer */
-
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
-
if (!env)
-
return -ENOMEM;
-
-
/* complete object path */
-
devpath = kobject_get_path(kobj, GFP_KERNEL);
-
if (!devpath) {
-
retval = -ENOENT;
-
goto exit;
-
}
-
-
/* default keys */
-
retval = add_uevent_var(env, "ACTION=%s", action_string);
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
-
if (retval)
-
goto exit;
-
-
/* keys passed in from the caller */
-
if (envp_ext) {
-
for (i = 0; envp_ext[i]; i++) {
-
retval = add_uevent_var(env, "%s", envp_ext[i]);
-
if (retval)
-
goto exit;
-
}
-
}
-
-
/* let the kset specific function add
its stuff */
-
if (uevent_ops && uevent_ops->uevent) {
-
retval = uevent_ops->uevent(kset, kobj, env);
-
if (retval) {
-
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
-
"%d\n", kobject_name(kobj), kobj,
-
__func__, retval);
-
goto exit;
-
}
-
}
-
-
/*
-
* Mark "add" and "remove" events in the
object to ensure proper
-
* events to userspace during automatic cleanup. If the
object did
-
* send an "add" event, "remove" will
automatically generated by
-
* the core, if not already
done by the caller.
-
*/
-
if (action == KOBJ_ADD)
-
kobj->state_add_uevent_sent = 1;
-
else if (action == KOBJ_REMOVE)
-
kobj->state_remove_uevent_sent = 1;
-
-
/* we will send an event, so request a new sequence number */
-
spin_lock(&sequence_lock);
-
seq = ++uevent_seqnum;
-
spin_unlock(&sequence_lock);
-
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned
long long)seq);
-
if (retval)
-
goto exit;
-
-
#if defined(CONFIG_NET)
-
/* send netlink message */
-
if (uevent_sock) {
-
struct sk_buff *skb;
-
size_t len;
-
-
/* allocate message with the maximum possible size */
-
len = strlen(action_string) + strlen(devpath) + 2;
-
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
-
if (skb) {
-
char *scratch;
-
-
/* add header */
-
scratch = skb_put(skb, len);
-
sprintf(scratch, "%s@%s", action_string, devpath);
-
-
/* copy keys to our continuous event payload buffer */
-
for (i = 0; i < env->envp_idx; i++) {
-
len = strlen(env->envp[i]) + 1;
-
scratch = skb_put(skb, len);
-
strcpy(scratch, env->envp[i]);
-
}
-
-
NETLINK_CB(skb).dst_group = 1;
-
retval = netlink_broadcast(uevent_sock, skb, 0, 1,
-
GFP_KERNEL);
-
/* ENOBUFS should be handled in userspace */
-
if (retval == -ENOBUFS || retval == -ESRCH)
-
retval = 0;
-
} else
-
retval = -ENOMEM;
-
}
-
#endif
-
-
/* call uevent_helper, usually
only enabled during early boot */
-
if (uevent_helper[0]) {
-
char *argv [3];
-
-
argv [0] = uevent_helper;
-
argv [1] = (char *)subsystem;
-
argv [2] = NULL;
-
retval = add_uevent_var(env, "HOME=/");
-
if (retval)
-
goto exit;
-
retval = add_uevent_var(env,
-
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
-
if (retval)
-
goto exit;
-
-
retval = call_usermodehelper(argv[0], argv,
-
env->envp, UMH_WAIT_EXEC);
-
}
-
-
exit:
-
kfree(devpath);
-
kfree(env);
-
return retval;
- }
这个函数里做了最重要的工作. 第一发送一个netlink组播;第二调用了call_usermodehelper执行用户空间的应用程序.
这里面不得不说uevent_helper变量.由它指定具体调用哪个应用程序.add_uevent_var只是传递一些变量.
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; //系统默认的值
而CONFIG_UEVENT_HELPER_PATH,可以查看arch/XXX/configs/ 里。一般是/sbin/XXX 。
当然从用户空间也可以修改这个值:
#cat /proc/sys/kernel/hotplug
or
#cat /sys/kernel/uevent_helper
可以查看当前系统设定的值,由于我做的是嵌入式系统,默认是/sbin/mdev (这里说明一下mdev只是udev缩减版,具体资料可以查一查就明白,它会检测设备模型下面一些变量值的变化并作出相应动作)
而对于为什么这两个变量的值是一样的,我做了简单分析:
第一个是procfs,我们看kernel/sysctl.c :
kernel_table :
点击(此处)折叠或打开
-
static struct ctl_table kern_table[] = {
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_child_runs_first",
-
.data = &sysctl_sched_child_runs_first,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#ifdef CONFIG_SCHED_DEBUG
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_min_granularity_ns",
-
.data = &sysctl_sched_min_granularity,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &sched_nr_latency_handler,
-
.strategy = &sysctl_intvec,
-
.extra1 = &min_sched_granularity_ns,
-
.extra2 = &max_sched_granularity_ns,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_latency_ns",
-
.data = &sysctl_sched_latency,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &sched_nr_latency_handler,
-
.strategy = &sysctl_intvec,
-
.extra1 = &min_sched_granularity_ns,
-
.extra2 = &max_sched_granularity_ns,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_wakeup_granularity_ns",
-
.data = &sysctl_sched_wakeup_granularity,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &min_wakeup_granularity_ns,
-
.extra2 = &max_wakeup_granularity_ns,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_shares_ratelimit",
-
.data = &sysctl_sched_shares_ratelimit,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_shares_thresh",
-
.data = &sysctl_sched_shares_thresh,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &zero,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_features",
-
.data = &sysctl_sched_features,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_migration_cost",
-
.data = &sysctl_sched_migration_cost,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_nr_migrate",
-
.data = &sysctl_sched_nr_migrate,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_time_avg",
-
.data = &sysctl_sched_time_avg,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "timer_migration",
-
.data = &sysctl_timer_migration,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &zero,
-
.extra2 = &one,
-
},
-
#endif
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_rt_period_us",
-
.data = &sysctl_sched_rt_period,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &sched_rt_handler,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_rt_runtime_us",
-
.data = &sysctl_sched_rt_runtime,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &sched_rt_handler,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "sched_compat_yield",
-
.data = &sysctl_sched_compat_yield,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#ifdef CONFIG_PROVE_LOCKING
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "prove_locking",
-
.data = &prove_locking,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_LOCK_STAT
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "lock_stat",
-
.data = &lock_stat,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
{
-
.ctl_name = KERN_PANIC,
-
.procname = "panic",
-
.data = &panic_timeout,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_CORE_USES_PID,
-
.procname = "core_uses_pid",
-
.data = &core_uses_pid,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_CORE_PATTERN,
-
.procname = "core_pattern",
-
.data = core_pattern,
-
.maxlen = CORENAME_MAX_SIZE,
-
.mode = 0644,
-
.proc_handler = &proc_dostring,
-
.strategy = &sysctl_string,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "core_pipe_limit",
-
.data = &core_pipe_limit,
-
.maxlen = sizeof(unsigned int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#ifdef CONFIG_PROC_SYSCTL
-
{
-
.procname = "tainted",
-
.maxlen = sizeof(long),
-
.mode = 0644,
-
.proc_handler = &proc_taint,
-
},
-
#endif
-
#ifdef CONFIG_LATENCYTOP
-
{
-
.procname = "latencytop",
-
.data = &latencytop_enabled,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_BLK_DEV_INITRD
-
{
-
.ctl_name = KERN_REALROOTDEV,
-
.procname = "real-root-dev",
-
.data = &real_root_dev,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "print-fatal-signals",
-
.data = &print_fatal_signals,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#ifdef CONFIG_SPARC
-
{
-
.ctl_name = KERN_SPARC_REBOOT,
-
.procname = "reboot-cmd",
-
.data = reboot_command,
-
.maxlen = 256,
-
.mode = 0644,
-
.proc_handler = &proc_dostring,
-
.strategy = &sysctl_string,
-
},
-
{
-
.ctl_name = KERN_SPARC_STOP_A,
-
.procname = "stop-a",
-
.data = &stop_a_enabled,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_SPARC_SCONS_PWROFF,
-
.procname = "scons-poweroff",
-
.data = &scons_pwroff,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_SPARC64
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "tsb-ratio",
-
.data = &sysctl_tsb_ratio,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef __hppa__
-
{
-
.ctl_name = KERN_HPPA_PWRSW,
-
.procname = "soft-power",
-
.data = &pwrsw_enabled,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_HPPA_UNALIGNED,
-
.procname = "unaligned-trap",
-
.data = &unaligned_enabled,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
{
-
.ctl_name = KERN_CTLALTDEL,
-
.procname = "ctrl-alt-del",
-
.data = &C_A_D,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#ifdef CONFIG_FUNCTION_TRACER
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "ftrace_enabled",
-
.data = &ftrace_enabled,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &ftrace_enable_sysctl,
-
},
-
#endif
-
#ifdef CONFIG_STACK_TRACER
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "stack_tracer_enabled",
-
.data = &stack_tracer_enabled,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &stack_trace_sysctl,
-
},
-
#endif
-
#ifdef CONFIG_TRACING
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "ftrace_dump_on_oops",
-
.data = &ftrace_dump_on_oops,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_MODULES
-
{
-
.ctl_name = KERN_MODPROBE,
-
.procname = "modprobe",
-
.data = &modprobe_path,
-
.maxlen = KMOD_PATH_LEN,
-
.mode = 0644,
-
.proc_handler = &proc_dostring,
-
.strategy = &sysctl_string,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "modules_disabled",
-
.data = &modules_disabled,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
/* only handle a transition from default "0" to "1" */
-
.proc_handler = &proc_dointvec_minmax,
-
.extra1 = &one,
-
.extra2 = &one,
-
},
-
#endif
-
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
-
{
-
.ctl_name = KERN_HOTPLUG,
-
.procname = "hotplug",
-
.data = &uevent_helper,
-
.maxlen = UEVENT_HELPER_PATH_LEN,
-
.mode = 0644,
-
.proc_handler = &proc_dostring,
-
.strategy = &sysctl_string,
-
},
-
#endif
-
#ifdef CONFIG_CHR_DEV_SG
-
{
-
.ctl_name = KERN_SG_BIG_BUFF,
-
.procname = "sg-big-buff",
-
.data = &sg_big_buff,
-
.maxlen = sizeof (int),
-
.mode = 0444,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_BSD_PROCESS_ACCT
-
{
-
.ctl_name = KERN_ACCT,
-
.procname = "acct",
-
.data = &acct_parm,
-
.maxlen = 3*sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_MAGIC_SYSRQ
-
{
-
.ctl_name = KERN_SYSRQ,
-
.procname = "sysrq",
-
.data = &__sysrq_enabled,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_PROC_SYSCTL
-
{
-
.procname = "cad_pid",
-
.data = NULL,
-
.maxlen = sizeof (int),
-
.mode = 0600,
-
.proc_handler = &proc_do_cad_pid,
-
},
-
#endif
-
{
-
.ctl_name = KERN_MAX_THREADS,
-
.procname = "threads-max",
-
.data = &max_threads,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_RANDOM,
-
.procname = "random",
-
.mode = 0555,
-
.child = random_table,
-
},
-
{
-
.ctl_name = KERN_OVERFLOWUID,
-
.procname = "overflowuid",
-
.data = &overflowuid,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &minolduid,
-
.extra2 = &maxolduid,
-
},
-
{
-
.ctl_name = KERN_OVERFLOWGID,
-
.procname = "overflowgid",
-
.data = &overflowgid,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &minolduid,
-
.extra2 = &maxolduid,
-
},
-
#ifdef CONFIG_S390
-
#ifdef CONFIG_MATHEMU
-
{
-
.ctl_name = KERN_IEEE_EMULATION_WARNINGS,
-
.procname = "ieee_emulation_warnings",
-
.data = &sysctl_ieee_emulation_warnings,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
{
-
.ctl_name = KERN_S390_USER_DEBUG_LOGGING,
-
.procname = "userprocess_debug",
-
.data = &sysctl_userprocess_debug,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
{
-
.ctl_name = KERN_PIDMAX,
-
.procname = "pid_max",
-
.data = &pid_max,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = sysctl_intvec,
-
.extra1 = &pid_max_min,
-
.extra2 = &pid_max_max,
-
},
-
{
-
.ctl_name = KERN_PANIC_ON_OOPS,
-
.procname = "panic_on_oops",
-
.data = &panic_on_oops,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#if defined CONFIG_PRINTK
-
{
-
.ctl_name = KERN_PRINTK,
-
.procname = "printk",
-
.data = &console_loglevel,
-
.maxlen = 4*sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_PRINTK_RATELIMIT,
-
.procname = "printk_ratelimit",
-
.data = &printk_ratelimit_state.interval,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_jiffies,
-
.strategy = &sysctl_jiffies,
-
},
-
{
-
.ctl_name = KERN_PRINTK_RATELIMIT_BURST,
-
.procname = "printk_ratelimit_burst",
-
.data = &printk_ratelimit_state.burst,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "printk_delay",
-
.data = &printk_delay_msec,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &zero,
-
.extra2 = &ten_thousand,
-
},
-
#endif
-
{
-
.ctl_name = KERN_NGROUPS_MAX,
-
.procname = "ngroups_max",
-
.data = &ngroups_max,
-
.maxlen = sizeof (int),
-
.mode = 0444,
-
.proc_handler = &proc_dointvec,
-
},
-
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
-
{
-
.ctl_name = KERN_UNKNOWN_NMI_PANIC,
-
.procname = "unknown_nmi_panic",
-
.data = &unknown_nmi_panic,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.procname = "nmi_watchdog",
-
.data = &nmi_watchdog_enabled,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_nmi_enabled,
-
},
-
#endif
-
#if defined(CONFIG_X86)
-
{
-
.ctl_name = KERN_PANIC_ON_NMI,
-
.procname = "panic_on_unrecovered_nmi",
-
.data = &panic_on_unrecovered_nmi,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "panic_on_io_nmi",
-
.data = &panic_on_io_nmi,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = KERN_BOOTLOADER_TYPE,
-
.procname = "bootloader_type",
-
.data = &bootloader_type,
-
.maxlen = sizeof (int),
-
.mode = 0444,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "bootloader_version",
-
.data = &bootloader_version,
-
.maxlen = sizeof (int),
-
.mode = 0444,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "kstack_depth_to_print",
-
.data = &kstack_depth_to_print,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "io_delay_type",
-
.data = &io_delay_type,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#if defined(CONFIG_MMU)
-
{
-
.ctl_name = KERN_RANDOMIZE,
-
.procname = "randomize_va_space",
-
.data = &randomize_va_space,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#if defined(CONFIG_S390) && defined(CONFIG_SMP)
-
{
-
.ctl_name = KERN_SPIN_RETRY,
-
.procname = "spin_retry",
-
.data = &spin_retry,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#if defined(CONFIG_ACPI_SLEEP) && defined(CONFIG_X86)
-
{
-
.procname = "acpi_video_flags",
-
.data = &acpi_realmode_flags,
-
.maxlen = sizeof (unsigned long),
-
.mode = 0644,
-
.proc_handler = &proc_doulongvec_minmax,
-
},
-
#endif
-
#ifdef CONFIG_IA64
-
{
-
.ctl_name = KERN_IA64_UNALIGNED,
-
.procname = "ignore-unaligned-usertrap",
-
.data = &no_unaligned_warning,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "unaligned-dump-stack",
-
.data = &unaligned_dump_stack,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_DETECT_SOFTLOCKUP
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "softlockup_panic",
-
.data = &softlockup_panic,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &zero,
-
.extra2 = &one,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "softlockup_thresh",
-
.data = &softlockup_thresh,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dosoftlockup_thresh,
-
.strategy = &sysctl_intvec,
-
.extra1 = &neg_one,
-
.extra2 = &sixty,
-
},
-
#endif
-
#ifdef CONFIG_DETECT_HUNG_TASK
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "hung_task_panic",
-
.data = &sysctl_hung_task_panic,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec_minmax,
-
.strategy = &sysctl_intvec,
-
.extra1 = &zero,
-
.extra2 = &one,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "hung_task_check_count",
-
.data = &sysctl_hung_task_check_count,
-
.maxlen = sizeof(unsigned long),
-
.mode = 0644,
-
.proc_handler = &proc_doulongvec_minmax,
-
.strategy = &sysctl_intvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "hung_task_timeout_secs",
-
.data = &sysctl_hung_task_timeout_secs,
-
.maxlen = sizeof(unsigned long),
-
.mode = 0644,
-
.proc_handler = &proc_dohung_task_timeout_secs,
-
.strategy = &sysctl_intvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "hung_task_warnings",
-
.data = &sysctl_hung_task_warnings,
-
.maxlen = sizeof(unsigned long),
-
.mode = 0644,
-
.proc_handler = &proc_doulongvec_minmax,
-
.strategy = &sysctl_intvec,
-
},
-
#endif
-
#ifdef CONFIG_COMPAT
-
{
-
.ctl_name = KERN_COMPAT_LOG,
-
.procname = "compat-log",
-
.data = &compat_log,
-
.maxlen = sizeof (int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_RT_MUTEXES
-
{
-
.ctl_name = KERN_MAX_LOCK_DEPTH,
-
.procname = "max_lock_depth",
-
.data = &max_lock_depth,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "poweroff_cmd",
-
.data = &poweroff_cmd,
-
.maxlen = POWEROFF_CMD_PATH_LEN,
-
.mode = 0644,
-
.proc_handler = &proc_dostring,
-
.strategy = &sysctl_string,
-
},
-
#ifdef CONFIG_KEYS
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "keys",
-
.mode = 0555,
-
.child = key_sysctls,
-
},
-
#endif
-
#ifdef CONFIG_RCU_TORTURE_TEST
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "rcutorture_runnable",
-
.data = &rcutorture_runnable,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_SLOW_WORK
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "slow-work",
-
.mode = 0555,
-
.child = slow_work_sysctls,
-
},
-
#endif
-
#ifdef CONFIG_PERF_EVENTS
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "perf_event_paranoid",
-
.data = &sysctl_perf_event_paranoid,
-
.maxlen = sizeof(sysctl_perf_event_paranoid),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "perf_event_mlock_kb",
-
.data = &sysctl_perf_event_mlock,
-
.maxlen = sizeof(sysctl_perf_event_mlock),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "perf_event_max_sample_rate",
-
.data = &sysctl_perf_event_sample_rate,
-
.maxlen = sizeof(sysctl_perf_event_sample_rate),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_KMEMCHECK
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "kmemcheck",
-
.data = &kmemcheck_enabled,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
#ifdef CONFIG_BLOCK
-
{
-
.ctl_name = CTL_UNNUMBERED,
-
.procname = "blk_iopoll",
-
.data = &blk_iopoll_enabled,
-
.maxlen = sizeof(int),
-
.mode = 0644,
-
.proc_handler = &proc_dointvec,
-
},
-
#endif
-
/*
-
* NOTE: do not add
new entries to this table unless you have read
-
* Documentation/sysctl/ctl_unnumbered.txt
-
*/
-
{ .ctl_name = 0 }
- }
-
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
-
{
-
.ctl_name = KERN_HOTPLUG,
-
.procname = "hotplug",
-
.data = &uevent_helper,
-
.maxlen = UEVENT_HELPER_PATH_LEN,
-
.mode = 0644,
-
.proc_handler = &proc_dostring,
-
.strategy = &sysctl_string,
-
},
- #endif
我们看到hotplug的参数是uevent_helper
那么/sys/XXX 下面的是在哪里初始化的呢?
当然它涉及sysfs . 我们看文件kernel/ksysfs.c
点击(此处)折叠或打开
-
static int __init ksysfs_init(void)
-
{
-
int error;
-
-
kernel_kobj = kobject_create_and_add("kernel", NULL);
-
if (!kernel_kobj) {
-
error = -ENOMEM;
-
goto exit;
-
}
-
error = sysfs_create_group(kernel_kobj, &kernel_attr_group);
-
if (error)
-
goto kset_exit;
-
-
if (notes_size > 0) {
-
notes_attr.size = notes_size;
-
error = sysfs_create_bin_file(kernel_kobj, ¬es_attr);
-
if (error)
-
goto group_exit;
-
}
-
-
return 0;
-
-
group_exit:
-
sysfs_remove_group(kernel_kobj, &kernel_attr_group);
-
kset_exit:
-
kobject_put(kernel_kobj);
-
exit:
-
return error;
- }
它首先在/sys/目录下添加了kernel目录对象. 我们是否还记得uevent_helper变量?
我们看这段代码,似乎就会明白了
点击(此处)折叠或打开
-
#define KERNEL_ATTR_RO(_name) \
-
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
-
-
#define KERNEL_ATTR_RW(_name) \
-
static struct kobj_attribute _name##_attr = \
-
__ATTR(_name, 0644, _name##_show, _name##_store)
-
-
#if defined(CONFIG_HOTPLUG)
-
/* current uevent sequence number */
-
static ssize_t uevent_seqnum_show(struct kobject *kobj,
-
struct kobj_attribute *attr, char *buf)
-
{
-
return sprintf(buf, "%llu\n", (unsigned
long long)uevent_seqnum);
-
}
-
KERNEL_ATTR_RO(uevent_seqnum);
-
-
/* uevent helper program, used during early boo */
-
static ssize_t uevent_helper_show(struct kobject *kobj,
-
struct kobj_attribute *attr, char *buf)
-
{
-
return sprintf(buf, "%s\n", uevent_helper);
-
}
-
static ssize_t uevent_helper_store(struct kobject *kobj,
-
struct kobj_attribute *attr,
-
const char *buf, size_t count)
-
{
-
if (count+1 > UEVENT_HELPER_PATH_LEN)
-
return -ENOENT;
-
memcpy(uevent_helper, buf, count);
-
uevent_helper[count] = '\0';
-
if (count && uevent_helper[count-1] == '\n')
-
uevent_helper[count-1] = '\0';
-
return count;
-
}
-
KERNEL_ATTR_RW(uevent_helper);
- #endif
KERNEL_ATTR_RW(uevent_helper); 对!就是它!我们在include/linux/sysfs.h中
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
是否就和上边的代码联系起来了呢?
我们接着看ksysfs_init:
error = sysfs_create_group(kernel_kobj, &kernel_attr_group);
而kernel_attr_group是什么呢
static struct attribute_group kernel_attr_group = {
.attrs = kernel_attrs,
};
static struct attribute * kernel_attrs[] = {
#if defined(CONFIG_HOTPLUG)
&uevent_seqnum_attr.attr,
&uevent_helper_attr.attr,
#endif
#ifdef CONFIG_PROFILING
&profiling_attr.attr,
#endif
#ifdef CONFIG_KEXEC
&kexec_loaded_attr.attr,
&kexec_crash_loaded_attr.attr,
&vmcoreinfo_attr.attr,
#endif
NULL
};
既然我们知道了原理,就会少一些困惑^^. 而关于call_usermodehelper,有兴趣的可以自行分析学习.