一、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_SWITCHSLOGE("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 = 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_NETstruct uevent_sock *ue_sk;#endifpr_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);elsesubsystem = 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 = 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 (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, "%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;} elseretval = -ENOMEM;}mutex_unlock(&uevent_sock_mutex);#endif/* 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)goto exit;retval = add_uevent_var(env,"PATH=/sbin:/bin:/usr/sbin:/usr/bin");if (retval)goto exit;retval = call_usermodehelper(argv[0], argv,     env->envp, UMH_WAIT_EXEC);}exit:kfree(devpath);kfree(env);return retval;}


/** * kobject_uevent - notify userspace by sending an uevent * * @action: action that is happening * @kobj: struct kobject that the action is happening to * * Returns 0 if kobject_uevent() is completed with success or the * corresponding error when it fails. */int kobject_uevent(struct kobject *kobj, enum kobject_action action){return kobject_uevent_env(kobj, action, NULL);}


3.3 netlink_broadcast_filtered

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;} elseconsume_skb(info.skb2);if (info.delivered) {if (info.congested && (allocation & __GFP_WAIT))yield();return 0;}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):


3.4 do_one_broadcast

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)goto out;if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups ||    !test_bit(p->group - 1, nlk->groups))goto out;if (!net_eq(sock_net(sk), p->net))goto out;if (p->failure) {netlink_overrun(sk);goto out;}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);}}if (p->skb2 == NULL) {netlink_overrun(sk);/* 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)) {kfree_skb(p->skb2);p->skb2 = NULL;} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {netlink_overrun(sk);if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)p->delivery_failure = 1;} else {p->congested |= val;p->delivered = 1;p->skb2 = NULL;}sock_put(sk);out:return 0;}


3.5 netlink_broadcast_deliver

static inline int netlink_broadcast_deliver(struct sock *sk,    struct sk_buff *skb){struct netlink_sock *nlk = nlk_sk(sk);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;}return -1;}


参考文献:

1. http://blog.csdn.net/magicyu2/article/details/6974074

2. http://blog.csdn.net/wangll9/article/details/7346363

更多相关文章

  1. Android:关于Window少为人知的一面
  2. android中常见的错误及解决办法
  3. Android(安卓)Notification通知栏、点击事件、悬浮通知的简单实
  4. Delphi处理Android的路径信息
  5. Android主流Router库对比(ARouter、ActivityRouter)
  6. Android第三方登录-----微信登录接入方法
  7. android 锁屏界面处理
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Mac下mysql 8.0.22 找回密码的方法
  2. mysql 8.0.22 winx64安装配置图文教程
  3. mysql 8.0.22.0 下载安装配置方法图文教
  4. CenOS6.7下mysql 8.0.22 安装配置方法图
  5. 推荐几款MySQL相关工具
  6. Windows10下mysql 8.0.22 安装配置方法图
  7. 浅析MySQL的lru链表
  8. mysql 5.5.27 winx64安装配置方法图文教
  9. mysql 8.0.22 winx64安装配置方法图文教
  10. mysql 8.0.22 下载安装配置方法图文教程