android热插拔事件处理流程如下图所示:

1. netlinkmanager:
全称是netlinkmanager.cpp位于android 4.x 源码位置/system/vold/netlinkmanager.cpp。该类的主要通过引用netlinkhandler类中的onevent()方法来接收来自内核的事件消息,netlinkhandler位于/system/vold/netlinkhandler.cpp。
2. volumemanager:
全称是volumemanager.cpp位于android 4.x源码位置/system/vold/volumemanager.cpp。该类的主要作用是接收经过netlinkmanager处理过后的事件消息。因为我们这里是sd的挂载,因此经过netlinkmanager处理过后的消息会分为五种,分别是:block,switch,usb_composite,battery,power_supply。这里sd卡挂载的事件是block。
3. directvolume:
位于/system/vold/directvolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:add,removed,change,noaction这四种。后文通过介绍add事件展开。
4. volume:
位于/system/vold/volume.cpp,该类是负责sd卡挂载的主要类。volume.cpp主要负责检查sd卡格式,以及对复合要求的sd卡进行挂载,并通过socket将消息sd卡挂载的消息传递给nativedaemonconnector。
5. commandlistener:
该类位于位于/system/vold/commandlistener.cpp。通过vold socket与nativedaemonconnector通信。
6. nativedaemonconnector:
该类位于frameworks/base/services/java/com.android.server/nativedaemonconnector.java。该类用于接收来自volume.cpp 发来的sd卡挂载消息并向上传递。
7. mountservice:
位于frameworks/base/services/java/com.android.server/mountservice.java。mountservice是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等。在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用。在android系统中这是一个非常重要的类。
8. storagemanaer:
位于frameworks/base/core/java/andriod/os/storage/storagemanager.java。在该类的说明中有提到,该类是系统存储服务的接口。在系统设置中,有storage相关项,同时setting也注册了该类的监听器。而storagemanager又将自己的监听器注册到了mountservice中,因此该类主要用于上层应用获取sd卡状态。
整个过程从kernel检测到sd卡插入事件开始,之前的一些硬件中断的触发以及driver的加载这里并不叙述,一直到sd卡挂载消息更新到“android——系统设置——存储”一项中。
1. kernel发出sd卡插入uevent。
2. netlinkhandler::onevent()接收内核发出的uevent并进行解析。
3. volumemanager::handlblockevent()处理经过第二步处理后的事件。
4. 接下来调用directvolume:: handleblockevent()。
在该方法中主要有两点需要注意:
第一,程序首先会遍历mpath容器,寻找与event对应的sysfs_path是否存在与mpath容器中。
第二,针对event中的action有4种处理方式:add,removed,change,noaction 。
例如:在add action中会有如下操作(因为我们这里所讲的是sd卡的挂载流程,因此以add来说明),首先创建设备节点,其次对disk和partition两种格式的设备分别进行处理。sd卡属于disk类型。
5. 经过上一步之后会调用directvolume::handlediskadded()方法,在该方法中会广播disk insert消息。
6. socketlistener::runlistener会接收directvolume::handlediskadded()广播的消息。该方法主要完成对event中数据的获取,通过socket。(ps:这里的socketlistener.cpp位于android源码/system/core/libsysutils/src/中,后文的framworklistener.cpp也是,之前自己找了很久 t_t)
7. 调用frameworklistener::ondataavailable()方法处理接收到的消息内容。
8. frameworklistener::dispatchcommand()该方法用于分发指令。
9. 在frameworklistener::dispatchcommand()方法中,通过runcommand()方法去调用相应的指令。
10. 在/system/vold/commandlistener.cpp中有runcommand()的具体实现。在该类中可以找到这个方法:commandlistener::volumecmd::runcommand(),从字面意思上来看这个方法就是对volume分发指令的解析。该方法中会执行“mount”函数:vm->mountvolume(arg[2])。
11. mountvolume(arg[2])在volumemanager::mountvolume()中实现,在该方法中调用v->mountvol()。
12. mountvol()方法在volume::mountvol()中实现,该函数是真正的挂载函数。(在该方法中,后续的处理都在该方法中,在mount过程中会广播相应的消息给上层,通过setstate()函数。)
13. setstate(volume::checking);广播给上层,正在检查sd卡,为挂载做准备。
14. fat::check();sd卡检查方法,检查sd卡是否是fat格式。
15. fat::domount()挂载sd卡。
至此,sd的挂载已算初步完成,接下来应该将sd卡挂载后的消息发送给上层,在13中也提到过,在挂载以及检查的过程中其实也有发送消息给上层的。
16. mountservice的构造函数中会开启监听线程,用于监听来自vold的socket信息。
thread thread = new thread(mconnector,vold_tag); thread.start();
17. mconnector是nativedaemonconnector的对象,nativedaemonconnector继承了runnable并override了run方法。在run方法中通过一个while(true)调用listentosocket()方法来实现实时监听。
18. 在listentosocket()中,首先建立与vold通信的socket server端,然后调用mountservice中的ondaemonconnected()方法。(ps:java与native通信可以通过jni,那么native与java通信就需要通过socket来实现了。android中native与frameworks通信 这篇文章中有简介,感兴趣的朋友可以参考一下)
19. ondaemonconnected()方法是在接口inativedaemonconnectorcallbacks中定义的,mountservice实现了该接口并override了ondaemonconnected()方法。该方法开启一个线程用于更新外置存储设备的状态,主要更新状态的方法也在其中实现。
20. 然后回到listentosocket中,通过inputstream来获取vold传递来的event,并存放在队列中。
21. 然后这些event会在ondaemonconnected()通过队列的”队列.take()”方法取出。并根据不同的event调用updatepublicvolumestate()方法,在该方法中调用packagemanagerservice中的updateexteralstate()方法来更新存储设备的状态。(注:这里不太理解packagemanagerservice中的unloadallcontainers(args)方法)
22. 更新是通过packagehelper.getmountservice().finishmediaupdate()方法来实现的。
23. 在updatepublicvolumestate()方法中,更新后会执行如下代码:
bl.mlistener.onstoragestatechanged();
在android源码/packages/apps/settings/src/com.android.settings.deviceinfo/memory.java代码中,实现了storageeventlistener 的匿名内部类,并override了onstoragestatechanged();方法。因此在updatepublicvolumestate()中调用onstoragestatechanged();方法后,memory.java中也会收到。在memory.java中收到以后会在setting界面进行更新,系统设置——存储中会更新sd卡的状态。从而sd卡的挂载从底层到达了上层。
vold的全称是volume daemon。主要负责系统对大容量存储设备(usb/sd)的挂载/卸载任务,它是一个守护进程,该进程支持这些存储外设的热插拔。自android 2.2开始,vold升级为vold 2.0,配置文件路径在android 4.0之后变为/etc/vold.fstab。
vold的工作流程大致可以分为三个部分:创建监听、引导、事件处理。
(1)创建监听
创建监听指的是创建监听链接,一方面用于监听来自内核的uevent,另一方面用于监听来自上层的控制命令,这些命令包括控制sd卡的挂载与卸载,这里所说的链接也就是socket。在android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:
service vold /system/bin/vold
socket vold stream 0660 root mount
iprio be 2
这样系统会在启动的时候创建与上层通信的socket,此socket name为"vold"。
在android 4.0源码/system/vold路径下的main.cpp<netlinkmanager::start():socket(pf_netlink,sock_dgram,netlink_kobject_uevent) >中创建了与内核通信的socket。在main.cpp中通过实例化volumemanager和netlinkmanager时创建。
(2)引导
vold进程启动时候会对现有的外部存储设备进行检查。首先加载并解析vold.fstab,并检查挂载点是否已被挂载。然后执行sd卡的挂载,最后处理usb大容量存储。因为系统是按行解析的,通过查看vold.fstab可以很清楚的知道这一点。
vold.fatab中最重要的语句:
dev_mount sdcard /mnt/sdcard auto /devices/platform/rk29_sdmmc.0/mmc_host/mmc0
dev_mount <lable> <mount_point> <part> <sysfs_path…>
挂载命令 标签 挂载点 第几个分区 设备的sysfs paths
注:
第几个分区:如果为auto则表示第1个分区。
参数之间不能有空格,只能以tab为间隔(注意:这里为了对齐因此采用空格隔开,如果自行修改vold.fstab之后加以空格的话系统会识别不到的)。
如果vold.fstab解析无误,voluemanager将创建directvolume,若vold.fstab解析不存在或者打开失败,vold将会读取linux内核中的参数,此时如果参数中存在sdcard(也就是sd的默认路径),volumemanager则会创建autovolume,如果不存在这个默认路径那么就不会创建。
(3)事件处理
通过对两个socket的监听,完成对事件的处理以及对上层应用的响应。
a) kernel发出uevent
netlinkmanager检测到kernel发出的uevent,解析后调用netlinkhandler::onevent()方法。该方法会分别处理不同的事件,这里重要的事件有:
“block”事件主要指volume的mount、unmount、createasec等。由volumemanager的handleblockevent(evt)来处理,根据多态性最终将会调用autovolume或者directvolume的handleblockevent方法来处理。
“switch”事件主要指volume的connet、disconnet等。根据相关操作,改变设备参数(设备类型、挂载点等)通过commandlistener告知framework层。
b) framework发出控制命令
与a)相反,commandlistener检测到framework层的命令(mountservice发出的命令)调用volumemanager的函数,volumemanager找出对应的volume,调用volume函数去挂载/卸载操作。而volume类中的相关操作最终通过调用linux函数完成。
netlinkmanager负责与kernel交互,通过pf_netlink来现。
vlod启动代码如下(/system/vold/main.cpp):
int main() {
volumemanager *vm;
commandlistener *cl;
netlinkmanager *nm;
slogi("vold 2.1 (the revenge) firing up");
mkdir("/dev/block/vold", 0755);
/* create our singleton managers */
if (!(vm = volumemanager::instance())) {
sloge("unable to create volumemanager");
exit(1);
};
if (!(nm = netlinkmanager::instance())) {
sloge("unable to create netlinkmanager");
cl = new commandlistener();
vm->setbroadcaster((socketlistener *) cl);
nm->setbroadcaster((socketlistener *) cl);
if (vm->start()) {
sloge("unable to start volumemanager (%s)", strerror(errno));
}
/* 解析/etc/vold.fstab文件,
读取type, label, mount_point, part
1) 构建directvolume对象 :如果part为auto, 则调用dv = new directvolume(vm, label, mount_point, -1);
2) 添加vold.fstab中定义的某一挂载项对应的sysfs_path到 directvolume对象的mpaths容器 dv->addpath(sysfs_path);
3) 将这个directvolume 对象添加到 volumemanager对象的容器mvolumes中 vm->addvolume(dv);
*/
if (process_config(vm)) {
sloge("error reading configuration (%s)... continuing anyways", strerror(errno));
/*会调用netlinkmanager类的start()方法,它创建pf_netlink socket,
并开启线程从此socket中读取数据*/
if (nm->start()) {
sloge("unable to start netlinkmanager (%s)", strerror(errno));
#ifdef use_usb_mode_switch
sloge("start misc devices manager...");
miscmanager *mm;
if (!(mm = miscmanager::instance())) {
sloge("unable to create miscmanager");
mm->setbroadcaster((socketlistener *) cl);
if (mm->start()) {
sloge("unable to start miscmanager (%s)", strerror(errno));
g3dev* g3 = new g3dev(mm);
g3->handleusb();
mm->addmisc(g3);
#endif
coldboot("/sys/block"); // 冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add\n” 字符也可以触发sysfs事件,相当执行了一次热插拔。
// coldboot("/sys/class/switch");
/*
* now that we're up, we can respond to commands
*/
if (cl->startlistener()) {
sloge("unable to start commandlistener (%s)", strerror(errno));
// eventually we'll become the monitoring thread
while(1) {
sleep(1000);
slogi("vold exiting");
exit(0);
}
netlinkmanager的家族关系如下所示:
上图中的虚线为启动是的调用流程。
(1) class netlinkmanager(在其start函数中创建了netlinkhandler对象,并把创建的socket作为参数)
(2)class netlinkhandler: public netlinklistener(实现了onevent)
(3) class netlinklistener : public socketlistener (实现了ondataavailable)
(4) class socketlistener(实现了runlistener,在一个线程中通过select查看哪些socket有数据,通过调用ondataavailable来读取数据)
int netlinkmanager::start() {
struct sockaddr_nl nladdr;
int sz = 64 * 1024;
int on = 1;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = af_netlink;
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;
// 创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件
if ((msock = socket(pf_netlink,
sock_dgram,netlink_kobject_uevent)) < 0) {
sloge("unable to create uevent socket: %s", strerror(errno));
return -1;
if (setsockopt(msock, sol_socket, so_rcvbufforce, &sz, sizeof(sz)) < 0) {
sloge("unable to set uevent socket so_recbufforce option: %s", strerror(errno));
if (setsockopt(msock, sol_socket, so_passcred, &on, sizeof(on)) < 0) {
sloge("unable to set uevent socket so_passcred option: %s", strerror(errno));
if (bind(msock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
sloge("unable to bind uevent socket: %s", strerror(errno));
// 利用新创建的socket实例化一个netlinkhandler类对象,netlinkhandler继承了类netlinklistener,
// netlinklistener又继承了类socketlistener
mhandler = new netlinkhandler(msock);
if (mhandler->start()) { //启动netlinkhandler
sloge("unable to start netlinkhandler: %s", strerror(errno));
return 0;
把socket作为参数创建了netlinkhandler对象,然后启动netlinkhandler。
int netlinkhandler::start() {
return this->startlistener();
int socketlistener::startlistener() {
if (!msocketname && msock == -1) {
sloge("failed to start unbound listener");
errno = einval;
} else if (msocketname) {
if ((msock = android_get_control_socket(msocketname)) < 0) {
sloge("obtaining file descriptor socket '%s' failed: %s",
msocketname, strerror(errno));
return -1;
}
if (mlisten && listen(msock, 4) < 0) {
sloge("unable to listen on socket (%s)", strerror(errno));
} else if (!mlisten)
mclients->push_back(new socketclient(msock, false));
if (pipe(mctrlpipe)) {
sloge("pipe failed (%s)", strerror(errno));
if (pthread_create(&mthread, null, socketlistener::threadstart, this)) {
sloge("pthread_create (%s)", strerror(errno));
void *socketlistener::threadstart(void *obj) {
socketlistener *me = reinterpret_cast<socketlistener *>(obj);
me->runlistener();
pthread_exit(null);
return null;
void socketlistener::runlistener() {
socketclientcollection *pendinglist = new socketclientcollection();
while(1) { // 死循环,一直监听
socketclientcollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = -1;
fd_zero(&read_fds); //清空文件描述符集read_fds
if (mlisten) {
max = msock;
fd_set(msock, &read_fds); //添加文件描述符到文件描述符集read_fds
fd_set(mctrlpipe[0], &read_fds); //添加管道的读取端文件描述符到read_fds
if (mctrlpipe[0] > max)
max = mctrlpipe[0];
pthread_mutex_lock(&mclientslock); //对容器mclients的操作需要加锁
for (it = mclients->begin(); it != mclients->end(); ++it) {
int fd = (*it)->getsocket();
fd_set(fd, &read_fds); ////遍历容器mclients的所有成员,调用内联函数getsocket()获取文件描述符,并添加到文件描述符集read_fds
if (fd > max)
max = fd;
pthread_mutex_unlock(&mclientslock);
// 等待文件描述符中某一文件描述符或者说socket有数据到来
if ((rc = select(max + 1, &read_fds, null, null, null)) < 0) {
if (errno == eintr)
continue;
sloge("select failed (%s)", strerror(errno));
sleep(1);
continue;
} else if (!rc)
if (fd_isset(mctrlpipe[0], &read_fds))
break;
if (mlisten && fd_isset(msock, &read_fds)) { //监听套接字处理
struct sockaddr addr;
socklen_t alen;
int c;
do {
alen = sizeof(addr);
c = accept(msock, &addr, &alen); //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,将其添加到mclient容器
} while (c < 0 && errno == eintr);
if (c < 0) {
sloge("accept failed (%s)", strerror(errno));
sleep(1);
}
pthread_mutex_lock(&mclientslock);
mclients->push_back(new socketclient(c, true));
pthread_mutex_unlock(&mclientslock);
/* add all active clients to the pending list first */
pendinglist->clear();
pthread_mutex_lock(&mclientslock);
if (fd_isset(fd, &read_fds)) {
pendinglist->push_back(*it);
/* process the pending list, since it is owned by the thread,
* there is no need to lock it */
while (!pendinglist->empty()) { //非监听套接字处理
/* pop the first item from the list */
it = pendinglist->begin();
socketclient* c = *it;
pendinglist->erase(it);
/* process it, if false is returned and our sockets are
* connection-based, remove and destroy it */
// ****** ondataavailable在netlinklistener中实现*********
if (!ondataavailable(c) && mlisten) {
/* remove the client from our array */
pthread_mutex_lock(&mclientslock);
for (it = mclients->begin(); it != mclients->end(); ++it) {
if (*it == c) {
mclients->erase(it);
break;
}
}
pthread_mutex_unlock(&mclientslock);
/* remove our reference to the client */
c->decref();
delete pendinglist;
socketlistener::runlistener是线程真正执行的函数:mlisten成员用来判定是否监听套接字,netlink套接字属于udp套接字,非监听套接字,该函数的主要功能体现在,如果该套接字有数据到来,就调用函数ondataavailable读取数据。
bool netlinklistener::ondataavailable(socketclient *cli)
{
int socket = cli->getsocket();
ssize_t count;
// 从socket中读取kernel发送来的uevent消息
count = temp_failure_retry(uevent_kernel_multicast_recv(socket, mbuffer, sizeof(mbuffer)));
if (count < 0) {
sloge("recvmsg failed (%s)", strerror(errno));
return false;
netlinkevent *evt = new netlinkevent();
if (!evt->decode(mbuffer, count, mformat)) {
sloge("error decoding netlinkevent");
} else {
onevent(evt); //在netlinkhandler中实现
delete evt;
return true;
void netlinkhandler::onevent(netlinkevent *evt) {
volumemanager *vm = volumemanager::instance();
const char *subsys = evt->getsubsystem();
if (!subsys) {
slogw("no subsystem found in netlink event");
return;
if (!strcmp(subsys, "block")) {
if(ueventonoffflag)
{
slogw("####netlink event block ####");
evt->dump();
vm->handleblockevent(evt);
} else if (!strcmp(subsys, "usb")
|| !strcmp(subsys, "scsi_device")) {
slogw("subsystem found in netlink event");
miscmanager *mm = miscmanager::instance();
mm->handleevent(evt);
/**
* like recv(), but checks that messages actually originate from the kernel.
*/
ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) {
struct iovec iov = { buffer, length };
struct sockaddr_nl addr;
char control[cmsg_space(sizeof(struct ucred))];
struct msghdr hdr = {
&addr,
sizeof(addr),
&iov,
1,
control,
sizeof(control),
0,
ssize_t n = recvmsg(socket, &hdr, 0);
if (n <= 0) {
return n;
if (addr.nl_groups == 0 || addr.nl_pid != 0) {
/* ignoring non-kernel or unicast netlink message */
goto out;
struct cmsghdr *cmsg = cmsg_firsthdr(&hdr);
if (cmsg == null || cmsg->cmsg_type != scm_credentials) {
/* ignoring netlink message with no sender credentials */
struct ucred *cred = (struct ucred *)cmsg_data(cmsg);
if (cred->uid != 0) {
/* ignoring netlink message from non-root user */
return n;
out:
/* clear residual potentially malicious data */
bzero(buffer, length);
errno = eio;
return -1;
用户态创建的netlink sock被kernel保存在:nl_table[sk->sk_protocol].mc_list
kernel态创建的netlink sock被kernel保存在:uevent_sock_list,上面的sk->sk_protocol为uevent_sock_list的协议, 二者只有协议一致才可以发送。
在用户态的socket创建方式(/system/vold/netlinkmanager.cpp):
if ((msock = socket(pf_netlink,
sock_dgram,netlink_kobject_uevent)) < 0) {
sloge("unable to create uevent socket: %s", strerror(errno));
在kernel的socket创建方式(/kernel/lib/kobject_uevent.c):
static int uevent_net_init(struct net *net)
struct uevent_sock *ue_sk;
ue_sk = kzalloc(sizeof(*ue_sk), gfp_kernel);
if (!ue_sk)
return -enomem;
ue_sk->sk = netlink_kernel_create(net, netlink_kobject_uevent,
1, null, null, this_module);
if (!ue_sk->sk) {
printk(kern_err
"kobject_uevent: unable to create netlink socket!\n");
kfree(ue_sk);
return -enodev;
mutex_lock(&uevent_sock_mutex);
list_add_tail(&ue_sk->list, &uevent_sock_list);
mutex_unlock(&uevent_sock_mutex);
从上面的代码可知,此sock被创建之后,被增加到全局变量uevent_sock_list列表中,下面的分析围绕此列表进行。
netlink_kernel_create函数原型:
struct sock *netlink_kernel_create(struct net *net, int unit, unsigned int groups,
void (*input)(struct sk_buff *skb),
struct mutex *cb_mutex, struct module *module)
1) struct net *net:是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量
2) int unit: 表示netlink协议类型,如 netlink_kobject_uevent
3) unsigned int groups: 组类型
4) void (*input)(struct sk_buff *skb):参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被调用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct
sock结构来表示。
5) struct mutex *cb_mutex: 互斥销
6) struct module *module: 一般为this_module
struct sock
用户态socket在kernel中的表示。
相关数据结构如下图所示:
* 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_env() 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;
const struct kset_uevent_ops *uevent_ops;
u64 seq;
int i = 0;
int retval = 0;
#ifdef config_net
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 "
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,
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), gfp_kernel);
if (!env)
/* 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)
retval = add_uevent_var(env, "devpath=%s", devpath);
retval = add_uevent_var(env, "subsystem=%s", subsystem);
/* 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 defined(config_net)
/* send netlink message */
list_for_each_entry(ue_sk, &uevent_sock_list, list) {
struct sock *uevent_sock = ue_sk->sk;
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); //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_filtered(uevent_sock, skb,
0, 1, gfp_kernel,
kobj_bcast_filter,
kobj);
/* enobufs should be handled in userspace */
if (retval == -enobufs)
retval = 0;
} else
retval = -enomem;
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = null;
retval = add_uevent_var(env, "home=/");
if (retval)
retval = add_uevent_var(env,
"path=/sbin:/bin:/usr/sbin:/usr/bin");
retval = call_usermodehelper(argv[0], argv,
env->envp, umh_wait_exec);
exit:
kfree(devpath);
kfree(env);
return retval;
* kobject_uevent - notify userspace by sending an uevent
* returns 0 if kobject_uevent() is completed with success or the
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
return kobject_uevent_env(kobj, action, null);
int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid,
u32 group, gfp_t allocation,
int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
void *filter_data)
struct net *net = sock_net(ssk);
struct netlink_broadcast_data info;
struct hlist_node *node;
struct sock *sk;
skb = netlink_trim(skb, allocation);
info.exclude_sk = ssk;
info.net = net;
info.pid = pid;
info.group = group;
info.failure = 0;
info.delivery_failure = 0;
info.congested = 0;
info.delivered = 0;
info.allocation = allocation;
info.skb = skb;
info.skb2 = null;
info.tx_filter = filter;
info.tx_data = filter_data;
/* while we sleep in clone, do not allow to change socket list */
netlink_lock_table();
// 向nl_table[ssk->sk_protocol].mc_list中的每个sock发送此netlink消息
sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list)
do_one_broadcast(sk, &info);
consume_skb(skb);
netlink_unlock_table();
if (info.delivery_failure) {
kfree_skb(info.skb2);
return -enobufs;
} else
consume_skb(info.skb2);
if (info.delivered) {
if (info.congested && (allocation & __gfp_wait))
yield();
return -esrch;
static struct netlink_table *nl_table;是全局变量,它维护了用户态创建的所有netlink sock,按协议分类,每种协议一个链表mc_list。它在函数netlink_proto_init中被初始化,向nl_table[sk->sk_protocol].mc_list中增加sock的调用流程如下(kernel/net/netlink/af_netlink.c):
static inline int do_one_broadcast(struct sock *sk,
struct netlink_broadcast_data *p)
struct netlink_sock *nlk = nlk_sk(sk);
int val;
if (p->exclude_sk == sk)
if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups ||
!test_bit(p->group - 1, nlk->groups))
if (!net_eq(sock_net(sk), p->net))
if (p->failure) {
netlink_overrun(sk);
sock_hold(sk);
if (p->skb2 == null) {
if (skb_shared(p->skb)) {
p->skb2 = skb_clone(p->skb, p->allocation);
} else {
p->skb2 = skb_get(p->skb);
/*
* skb ownership may have been set when
* delivered to a previous socket.
*/
skb_orphan(p->skb2);
/* clone failed. notify all listeners. */
p->failure = 1;
if (nlk->flags & netlink_broadcast_send_error)
p->delivery_failure = 1;
} else if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) {
kfree_skb(p->skb2);
p->skb2 = null;
} else if (sk_filter(sk, p->skb2)) {
} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
p->congested |= val;
p->delivered = 1;
sock_put(sk);
static inline int netlink_broadcast_deliver(struct sock *sk,
struct sk_buff *skb)
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
!test_bit(0, &nlk->state)) {
skb_set_owner_r(skb, sk);
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk, skb->len);
return atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf;
<a target="_blank"></a>