1. 程式人生 > >Android熱插拔事件處理流程--Vold

Android熱插拔事件處理流程--Vold

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

一、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卡狀態。

三、典型流程描述 (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

1. Vold簡介

     Vold的全稱是volume daemon。主要負責系統對大容量儲存裝置(USB/SD)的掛載/解除安裝任務,它是一個守護程序,該程序支援這些儲存外設的熱插拔。自Android 2.2開始,Vold升級為vold 2.0,配置檔案路徑在Android 4.0之後變為/etc/vold.fstab。

2.Vold工作流程

    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函式完成。

五、Vold使用者態

1. NetlinkManager

    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");        exit(1);    };    cl = new CommandListener();    vm->setBroadcaster((SocketListener *) cl);    nm->setBroadcaster((SocketListener *) cl);    if (vm->start()) {        SLOGE("Unable to start VolumeManager (%s)", strerror(errno));        exit(1);    }    /* 解析/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));        exit(1);    }#ifdef USE_USB_MODE_SWITCH SLOGE("Start Misc devices Manager..."); MiscManager *mm;    if (!(mm = MiscManager::Instance())) {        SLOGE("Unable to create MiscManager");        exit(1);    };    mm->setBroadcaster((SocketListener *) cl);    if (mm->start()) {        SLOGE("Unable to start MiscManager (%s)", strerror(errno));        exit(1);    } 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));        exit(1);    }    // 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來讀取資料)

 2. NetlinkManager::start()

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));        return -1;    }    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));        return -1;    }    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {        SLOGE("Unable to bind uevent socket: %s", strerror(errno));        return -1;    }    // 利用新建立的socket例項化一個NetlinkHandler類物件,NetlinkHandler繼承了類NetlinkListener,        // NetlinkListener又繼承了類SocketListener        mHandler = new NetlinkHandler(mSock);    if (mHandler->start()) {  //啟動NetlinkHandler        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));        return -1;    }    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;        return -1;    } 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));        return -1;    } else if (!mListen)        mClients->push_back(new SocketClient(mSock, false));    if (pipe(mCtrlPipe)) {        SLOGE("pipe failed (%s)", strerror(errno));        return -1;    }    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {        SLOGE("pthread_create (%s)", strerror(errno));        return -1;    }    return 0;}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)            continue;        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);                continue;            }            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);        for (it = mClients->begin(); it != mClients->end(); ++it) {            int fd = (*it)->getSocket();            if (FD_ISSET(fd, &read_fds)) {                pendingList->push_back(*it);            }        }        pthread_mutex_unlock(&mClientsLock);        /* 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讀取資料。

3. NetlinkListener::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;}

4. NetlinkHandler::onEvent

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);#ifdef USE_USB_MODE_SWITCH    } else if (!strcmp(subsys, "usb")     || !strcmp(subsys, "scsi_device")) {      SLOGW("subsystem found in netlink event");     MiscManager *mm = MiscManager::Instance();     mm->handleEvent(evt);#endif    }}

 

5. uevent_kernel_multicast_recv

/** * 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 */        goto out;    }    struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);    if (cred->uid != 0) {        /* ignoring netlink message from non-root user */        goto out;    }    return n;out:    /* clear residual potentially malicious data */    bzero(buffer, length);    errno = EIO;    return -1;}


 六、與Vold相關的Kernel態

  • 使用者態建立的netlink sock被kernel儲存在:nl_table[sk->sk_protocol].mc_list
  • Kernel態建立的netlink sock被kernel儲存在:uevent_sock_list,上面的sk->sk_protocol為uevent_sock_list的協議, 二者只有協議一致才可以傳送。

1. 建立kernel態sock

  •  在使用者態的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));        return -1;    }
  • 在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); return 0;}

      從上面的程式碼可知,此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中的表示。

2. 相關資料結構

     相關資料結構如下圖所示:

3. 傳送訊息給使用者空間

  3.1 傳送訊息流程圖

 

3.2 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_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 = NULLconst char *subsystem; struct kobject *top_kobj; struct kset *kset; const struct kset_uevent_ops *uevent_ops; u64 seq; int i = 0int retval = 0;#ifdef CONFIG_NET struct uevent_sock *ue_sk;#endif 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 = 1else 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 */ mutex_lock(&uevent_sock_mutex); 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, "%[email protected]%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 = netlin