用户空间辅助程序---热插拔
来源:互联网 发布:用ipad开淘宝店铺教程 编辑:程序博客网 时间:2024/06/05 09:10
原文地址: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,有兴趣的可以自行分析学习.
- 用户空间辅助程序---热插拔
- 内核运行用户空间程序
- Xenomai用户空间中断示例程序
- 在内核态执行用户空间程序
- 程序的用户地址空间映射
- 用户空间第一个程序Init
- 在用户空间程序也使用container_of()
- 内核空间调用用户空间程序-kernel_execve分析及总结
- 如何从内核空间调用用户空间程序
- 指针理解辅助程序
- 扫雷辅助程序
- 工作辅助程序
- IPSEC_WHACK - pluto辅助程序
- TE辅助程序
- windbg 如何再内核模式调试用户空间的程序
- xp下用户程序空间分配(1):大致框架
- xp下用户程序空间分配(2):栈
- xp下用户程序空间分配(3):加载文件
- 黑客通过linux bash漏洞借助apache cgi向产品服务器植入木马病毒的实例分享!
- Brackets Sequence
- struts2安装调试(eclipse)
- Hibernate报错 No identifier specified for entity:
- 【win10】第一时间上手体验--不一样的windows
- 用户空间辅助程序---热插拔
- Hive-2-Hive的安装
- 【零起步开发Cocos2dx-3.x-王牌飞行员(二)】从程序的层次结构说起
- 高速PCB设计之过孔注意
- 强大的Vivado IP工具——自定义IP的使用(IP packager)
- 加州理工学院公开课:机器学习与数据挖掘_Three Learning Principles(第十七课)
- 什么是FCLK,ICLK ?
- 3D打印——只有你想不到的,没有 它做不到的
- 模糊查询匹配特殊字符