【2017年学习输出内容记录】Andriod Vold进程工作机制分析

Andriod Vold进程工作机制分析


一、Andriod存储系统

MountService是为应用提供服务的Binder类,运行在SystemServer中;StorageManager是MountServer的代理,在用户进程中使用。
Vold是一个守护进程,负责和底层存储系统的交互。
MountService和Vold之间通过socket进行双向通信,MountService向Vold发送操作命令,Vold向MountService发送底层硬件发生变化的通知消息。

Vold进程的主体是VolumeManager对象,管理着系统底层所有Volume对象,实现各种存储相关的操作。
Vold中CommandListener对象负责和MountService中的NativeDemonConnector进行Socket通信;NetlinkHandler对象负责监听来自驱动的Neilink Socket消息;NetlinkManager对象负责创建3个NetlinkHandler对象。



二、Vold进程启动
1、由Kernel发起挂载请求
Kernel通过Netlink发送请求(传递uevent)给NetlinkManager,NetlinkManager通过内部的线程NetlinkHandler交给VolumeManager进行实际操作,然后VolumeManager通过CommandListener通知MountService。

2、由SystemServer发起挂载请求
运行在SystemServer进程的MountService通过NativeDaemonConnector给CommandListener发送请求,CommandListener再通知VolumeManager进行实际操作。

3、Vold进程启动
Vold也是通过init进程启动,它的启动文件定义如下:

system\vold\vold.rc:

* service vold /system/bin/vold \
--blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
--fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
class core
socket vold stream 0660 root mount
socket cryptd stream 0660 root mount
ioprio be 2
writepid /dev/cpuset/foreground/tasks

vold服务放到了core分组,意味着系统启动时它就会被init进程启动。

vold模块的源码及其入口函数main:

system\vold\main.cpp

* int main(int argc, char** argv) {
...
VolumeManager *vm;
CommandListener *cl;
CryptCommandListener *ccl;
NetlinkManager *nm;


parse_args(argc, argv); 解析vold.rc中定义的blkid和fsck相关的参数
...
mkdir("/dev/block/vold", 0755); //创建vold目录
...
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) { //创建VolumeManager对象
LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
}


if (!(nm = NetlinkManager::Instance())) { //创建NetlinkManager对象
LOG(ERROR) << "Unable to create NetlinkManager";
exit(1);
}
...
cl = new CommandListener(); //创建CommandListener对象
ccl = new CryptCommandListener();
vm->setBroadcaster((SocketListener *) cl); //建立vm和cl的联系
nm->setBroadcaster((SocketListener *) cl); //建立nm和cl的联系


if (vm->start()) { //启动VolumeManager
PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}


if (process_config(vm)) { //根据配置文件初始化VolumeManager
PLOG(ERROR) << "Error reading configuration... continuing anyways";
}


if (nm->start()) { //启动NetlinkManager
PLOG(ERROR) << "Unable to start NetlinkManager";
exit(1);
}
...
/*
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) { //启动CommandListener,开始监听
PLOG(ERROR) << "Unable to start CommandListener";
exit(1);
}
...
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}


LOG(ERROR) << "Vold exiting";
exit(0);
}

主函数的主要工作是创建三个对象VolumeManager、NetlinkManager、CommandListener,同时将CommandListener对象分别设置到了VolumeManager和NetlinkManager中。因为CommandListener用于和Java层的NativeDaemonConnector对象进行socket通信,所以VolumeManager和NetlinkManager都需要拥有CommandListener对象的引用。接下来,Vold进程具体的工作就会交付给子模块进行处理。

三、由Kernel发起挂载请求
SD卡插入,Kernel发起挂载请求为例。

1、监听驱动发出的消息——Vold的NetlinkListener对象
NetlinkListener对象的主要作用是监听驱动发出的uevent消息。Vold的main函数中调用NetlinkListener类的静态函数Instance()来创建NetlinkListener对象并通过静态变量sInstance来引用,所以vold进程中只有一个NetlinkListener对象。NetlinkListener的构造函数:

system\vold\NetlinkManager.cpp:

* NetlinkManager::NetlinkManager() {
mBroadcaster = NULL; //初始化
}

构造函数只是简单的初始化,Vold的main函数会调用NetlinkManager的setBroadcaster()函数来给变量mBroadcaster重新赋值。然后调用NetlinkManager的start()函数:

* int NetlinkManager::start() {
struct sockaddr_nl nladdr; //定义并初始化socket的地址结构
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;


if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) { //创建PF_NETLINK地址簇的socket,NETLINK_KOBJECT_UEVENT表示该socket将接收内核的Uevent事件
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}


if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { //setsockopt设置socket的选项,此处设置socket的接收缓冲区大小为64 * 1024
SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
goto out;
}


if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { //设置允许接收凭证相关的信息
SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
goto out;
}


if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { //将创建出的socket绑定到之前的地址上,此时socket可以收到Kernel的数据了
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
goto out;
}


mHandler = new NetlinkHandler(mSock); //①创建并②启动一个NetlinkHandler
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
goto out;
}


return 0;


out:
close(mSock);
return -1;
}

NetlinkManager启动后就是创建一个可以接收Kernel消息的socket,并以此socket构建并启动NetlinkHandler。NetlinkHandler则用于监听和接收socket的数据。
①NetlinkHandler的创建:

system\vold\NetlinkHandler.cpp:

* NetlinkHandler::NetlinkHandler(int listenerSocket) :
NetlinkListener(listenerSocket) {
}

NetlinkHandler初始化时,将与Kernel通信的socket描述符传入到父类NetlinkListener中。

system\core\libsysutils\src\NetlinkListener.cpp:

* NetlinkListener::NetlinkListener(int socket) :
SocketListener(socket, false) {
mFormat = NETLINK_FORMAT_ASCII;
}

NetlinkListener又进一步调用其父类SocketListener。

system\core\libsysutils\src\SocketListener.cpp:

* SocketListener::SocketListener(int socketFd, bool listen) {
init(NULL, socketFd, listen, false);
}

* void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
mListen = listen;
mSocketName = socketName;
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();
}

创建完NetlinkHandler后,NetlinkManager调用了NetlinkHandler的start方法。

②NetlinkHandler的启动:

* int NetlinkHandler::start() {
return this->startListener(); //根据继承体系,实际上调用了SocketListener的startListenr函数
}

* int SocketListener::startListener() {
return startListener(4);
}


* int SocketListener::startListener(int backlog) {


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;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
fcntl(mSock, F_SETFD, FD_CLOEXEC);
}


if (mListen && listen(mSock, backlog) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); //利用mSocket构造SocketClient加入到mClients中


if (pipe(mCtrlPipe)) { //pipe系统调用将创建一个匿名管道(资料显示:mCtrlPipe是一个int类型的二元数组,其中mCtrlPipe[0]用于从管道读数据,mCtrlPipe[1]用于往管道写数据)
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}


if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { //创建一个监听线程,线程的执行函数为threadStart
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}


return 0;
}

startListener()函数将开始监听socket,这个函数在NetlinkHandler中会被调用,在CommandListener中也会被调用。startListener()函数首先判断mSocketName是否有值,只有CommandListener对象会对这个变量赋值,它的值就是在init.rc中定义的socket字符串。调用函数android_get_control_socket()的目的就是为了从环境变量中取得socket的值。这样CommandListener对象得到了它需要监听的socket。而对于NetlinkHandler对象而言,它的mSocket不为NULL,前面已经创建出了socket。
NetlinkHandler启动后,创建了一个工作线程,用于接收和处理数据。

* void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);


me->runListener(); //调用SocketListener的runListener函数
pthread_exit(NULL);
return NULL;
}


* void SocketListener::runListener() {


SocketClientCollection pendingList;


while(1) { //无线循环,接收socket收到的数据
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = -1;


FD_ZERO(&read_fds); //将指定的文件描述符集清空,系统分配时默认是不清空的

if (mListen) { //如果需要监听
max = mSock;
FD_SET(mSock, &read_fds); //把mSock加入read_fds
}


FD_SET(mCtrlPipe[0], &read_fds); //把管道mCtrlPipe[0]也加入read_fds
if (mCtrlPipe[0] > max)
max = mCtrlPipe[0];

pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) { //mClients中保存的是NetlinkHandler对象的socket,或者CommandListener接入的socket
// NB: calling out to an other object with mClientsLock held (safe)
int fd = (*it)->getSocket();
FD_SET(fd, &read_fds); //把它也加入到read_fds
if (fd > max) {
max = fd;
}
}
pthread_mutex_unlock(&mClientsLock);
SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { //执行select调用,开始等待socket上的数据,监听是否有数据到来
if (errno == EINTR)
continue; //因为中断退出select,继续
SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
sleep(1); //select出错,休眠一秒后继续
continue;
} else if (!rc)
continue; //如果fd上没有数据到达,继续


if (FD_ISSET(mCtrlPipe[0], &read_fds)) { //FD_ISSET用于测试指定的文件描述符是否在该集合中,前面已经加入了
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
if (c == CtrlPipe_Shutdown) { //如果从管道中读出CtrlPipe_Shutdown,则退出工作线程
break;
}
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) { //如果mSock是服务器端,进入这个分支,NetlinkHandler中的mSock并不是服务器端
sockaddr_storage ss;
sockaddr* addrp = reinterpret_cast<sockaddr*>(&ss);
socklen_t alen;
int c;


do {
alen = sizeof(ss)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值