天天看点

Android热插拔事件处理详解

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

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的家族关系如下所示:

Android热插拔事件处理详解

上图中的虚线为启动是的调用流程。

 (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中的表示。

     相关数据结构如下图所示:

Android热插拔事件处理详解
Android热插拔事件处理详解

 * 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):

Android热插拔事件处理详解

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;  

Android热插拔事件处理详解

<a target="_blank"></a>