我们整天在用Android终端,但是我们知道当我们的Android终端在拔掉网线时,Android系统是怎么从kernel一步步的通知到应用层的?而当我们在应用层设置网络参数,应用层的命令和设置信息又是怎么一步步传到kernel的?
在应用到Linux内核之间需要一个桥梁,这个桥梁就是Netd守护进程,我们就从Netd守护进程开始去了解一些Android网络系统的工作流程。
Netd进程是通过init进程启动的,我们来看看它在init.rc中的定义:
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
显然netd启动时创建三个TCP监听socket,其名称分别为netd、dnsproxyd和mdns。当设备启动后,到/dev/socket/下会看见有netd、dnsproxyd和mdns这三个文件。netd守护进程的Socket资源和netd、dnsproxyd和mdns名称绑定起来。netd通过Socket接收并处理来自Framework层中NetworkManagementService或NsdService的命令。netd通过Socket接收并解析来自Kernel的UEvent消息,然后再通过Socket转发给Framework层中对应Service去处理。为了证实我们的想法,我们先来看一下TCP/IP网络进程间通信的流程,只有了解Socket通信的流程我们才更容易理解netd的工作流程:
服务器端 |
客户端 |
1.创建socket |
1.创建socket |
2.bind() |
|
3.listen() |
|
4.accecp() |
|
等待客户端连接…… |
2.connect() |
5.读数据(recv) |
3.写数据(send) |
6.写数据(send) |
4.读数据(recv) |
7.关闭socket(closesocket()) |
5.关闭socket(closesocket()) |
也就是说netd和Framework层,以及netd和kernel之间要通过Socket来通信的话,所做的事情肯定是上面提到的这些。
下面分析netd的main函数
main()
{
//new NetlinkManager对象nm
if (!(nm = NetlinkManager::Instance())) {
ALOGE("Unable to create NetlinkManager");
exit(1);
};
//new CommandListener对象cl,将cl设置成nm(NetlinkManager)的消息发送者(mBroadcaster)
cl = new CommandListener(rangeMap);
nm->setBroadcaster((SocketListener *) cl);
/*
*分析CommandListener的构造函数后发现,CommandListener将创建名为“netd”的监听Socket
*/
//启动nm(NetlinkManager)
if (nm->start()) {
ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
//创建DnsProxyListener对象dpl,并启动监听,DnsProxyListener将会创建名为“dnsproxyd”监听Socket
dpl = new DnsProxyListener(rangeMap);
if (dpl->startListener()) {
ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
exit(1);
}
//创建MDnsSdListener对象mdnsl,并启动监听,MDnsSdListener将会创建名为“mdns”的监听Socket
mdnsl = new MDnsSdListener();
if (mdnsl->startListener()) {
ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
exit(1);
}
//启动cl(CommandListener)监听
if (cl->startListener()) {
ALOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
}
netd的main函数比较简单,就是创建几个重要的成员并启动成员的工作,笔者这里只讲解netd里重要的成员NetlinkManager。下面看NetlinkManager的类图。
显然NetlinkManager里有三个NetlinkHandler和一个SocketListener对象,NetlinkHandler继承于NetlinkListener,而NetlinkListener又继承于SocketListener。由代码nm->setBroadcaster((SocketListener *) cl);可以知道NetlinkManager里的mBroadcaster其实就是由CommandListener向上转型来的,而CommandListener又继承于FrameworkListener,FrameworkListener继承于SocketListener。
分析NetlinkManager的start函数
int NetlinkManager::start() {
//创建接收NETLINK_KOBJECT_UEVENT消息的socket,其值保存在mUeventSock中
if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
return -1;
}
//创建接收RTMGPR_LINK消息的socket,其值保存在mRouteSock中
if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_IFADDR,
NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
return -1;
}
//创建接收NETLINK_NFLOG消息的socket,其值保存在mQuotaSock中
if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
ALOGE("Unable to open quota2 logging socket");
// TODO: return -1 once the emulator gets a new kernel.
}
return 0;
}
NetlinkManager的start函数就是向Kernel注册了三个用于接收UEvent事件的socket。我们接着进到setupSocket里看看,看是不是创建了Socket了,是不是bing了?
NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,
int groups, int format) {
//创建Socket
if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
ALOGE("Unable to create netlink socket: %s", strerror(errno));
return NULL;
}
if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
ALOGE("Unable to bind netlink socket: %s", strerror(errno));
close(*sock);
return NULL;
}
//如果没有猜错在NetlinkHandler的start里一定会调用listen接着另起一线程进行accept和等待客户端的连接以及不停的接收来自kernel的UEvent消息。
NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
if (handler->start()) {
ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
close(*sock);
return NULL;
}
}
int SocketListener::startListener() {
//调用listen
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, mUseCmdNum));
//另起线程
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
}
void SocketListener::runListener() {
while(1) {
//调用select
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
if (errno == EINTR)
continue;
SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
sleep(1);
continue;
} else if (!rc)
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) {
//调用accept
do {
alen = sizeof(addr);
c = accept(mSock, &addr, &alen);
SLOGV("%s got %d from accept", mSocketName, c);
} while (c < 0 && errno == EINTR);
}
}
当我们拔掉终端的网线时,我们监听的Socket会收到RTMGPR_LINK事件。来到这里我们已经知道NetlinkManager是怎么收到来自kernel的UEvent事件的,但是收到这个UEvent事件后,netd又是怎么通知Framework层的呢?
NetlinkHandler接收到的UEvent消息会转换成一个NetlinkEvent对象。NetlinkEvent对象封装了对UEvent消息的解析方法,UEvent消息经解析后将经由mBroadcaster对象传递给Framework层的接收者。我们下面将分析mBroadcaster来验证我们的想法。
上面已经说过了mBroadcaster其实就是CommandListener向上转型得到的东西,我们可以从CommandListener着手分析。
CommandListener::CommandListener(UidMarkMap *map) :
FrameworkListener("netd", true) {
//注册11个命令类对象
registerCmd(new InterfaceCmd());
registerCmd(new IpFwdCmd());
registerCmd(new TetherCmd());
registerCmd(new NatCmd());
registerCmd(new ListTtysCmd());
registerCmd(new PppdCmd());
registerCmd(new SoftapCmd());
registerCmd(new BandwidthControlCmd());
registerCmd(new IdletimerControlCmd());
registerCmd(new ResolverCmd());
registerCmd(new FirewallCmd());
registerCmd(new ClatdCmd());
}
CommandListener的构造函数里最主要的函数是FrameworkListener("netd", true),显然验证了上面说法,这里要创建名为“netd”的监听Socket。在netd的main函数里我们有调用过cl->startListener(),我们到CommandListener的startListener里看看,CommandListener的startListener调用其实就是其父类的父类SocketListener的startListener。
int SocketListener::startListener() {
if (!mSocketName && mSock == -1) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
return -1;
} else if (mSocketName) {
//获取名称为“netd”的监听Socket
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);
}
//调用listen
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, mUseCmdNum));
//创建新线程
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
}
分析到这里下面就不需要详细讲了,因为和刚才创建Socket监听kernel的UEvent事件的过程一样。到这里netd的工作基本已经清楚了,就是获取名称为“netd”的Socket并启动监听,同时创建3个Socket用于监听来自kernel的3中不同的UEvent事件,解析kernel发送过来的UEvent事件,将解析结果通过名称为“netd”的监听Socket发送给Framework层的服务,同时名称为“netd”的Socket也接收来自Framework层的消息(即用户通过Framework层设置网络的命令和信息),把接收到的消息在通过相应的Socket发送给kernel。整个过程中netd进程一直是Socket通信的服务端,而kernel的客户端的代码是怎么样子的,笔者这里不讲,因为那是驱动的事情,而作为另外一个客户端Framework层里的代码到底是什么样子的呢?
public static NetworkManagementService create(Context context) throws InterruptedException {
// NETD_SOCKET_NAME就是我们要找的“netd”
return create(context, NETD_SOCKET_NAME);
}
static NetworkManagementService create(Context context,
String socket) throws InterruptedException {
final NetworkManagementService service = new NetworkManagementService(context, socket);
//启动线程
service.mThread.start();
}
到NetworkManagementService的构造函数看看
private NetworkManagementService(Context context, String socket) {
mConnector = new NativeDaemonConnector(
new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
mThread = new Thread(mConnector, NETD_TAG);
}
我们到NativeDaemonConnector的run函数里去看看,是不是在run函数里,都做了客户端需要做的事情,以接收和发送消息给netd?
public void run() {
listenToSocket() {
//创建Socket
socket = new LocalSocket();
//设置连接地址
LocalSocketAddress address = determineSocketAddress();
//建立连接
socket.connect(address);
//获取输入流
InputStream inputStream = socket.getInputStream();
//获取输出流
mOutputStream = socket.getOutputStream();
}
}
分析到这里,我们已经知道在Framework层和netd通信的服务是NetworkManagementService。读完这篇文章后,大伙应该基本了解Android系统的以太网的工作流程了,关键是我们学到了Android的其中一个设计方法,Framework层怎么和Native层的另外一个进程通信,我们可以模仿上面讲到的方法,用Socket。