天天看點

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>