For more information, see http://blog.csdn.net/z2007b. do not use this document for commercial purposes. The copyright is owned by the Wuwei monk.
In the previous section, we analyzed the kobject_add_internal (& K-> kobj) in the int kset_register (struct kset * k) function. Next, we will analyze it to arouse your memory, the kset_register function is defined as follows:
Int kset_register (struct kset * K)
{
Int err;
If (! K)
Return-einval;
Kset_init (k );
Err = kobject_add_internal (& K-> kobj );
If (ERR)
Return err;
Kobject_uevent (& K-> kobj, kobj_add );
Return 0;
}
Next we need to analyze kobject_uevent (& K-> kobj, kobj_add), which is used to tell the user space that a new friend is coming and what is his name. Let's see how it works.
Int kobject_uevent (struct kobject * kobj, Enum kobject_action action)
{
Return kobject_uevent_env (kobj, action, null );
}
There is a function in it:
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 */
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"
"Event to drop! /N ", kobject_name (kobj), kobj,
_ FUNC __);
Return 0;
}
/* Environment buffer */
ENV = kzarloc (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
* 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) 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 shoshould be handled in userspace */
If (retval =-enobufs)
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;
}
After initialization, we finally called the call_usermodehelper function.
Static inline int
Call_usermodehelper (char * path, char ** argv, char ** envp, Enum umh_wait wait)
{
Struct subprocess_info * Info;
Gfp_t gfp_mask = (wait = umh_no_wait )? Gfp_atomic: gfp_kernel;
Info = call_usermodehelper_setup (path, argv, envp, gfp_mask );
If (Info = NULL)
Return-enomem;
Return call_usermodehelper_exec (Info, wait );
}
Based on the programming experience on Linux, we all know that this program is executed according to the input parameters.
Yes, this is an important role used for interaction with the user space. Later, we will detail it in the appropriate chapters.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/z2007b/archive/2011/05/04/6395333.aspx