android emulator虚拟设备分析第三篇之pipe上的qemud service
一、概述
本篇和第二篇是強相關的,需要結合第二篇一起看。
以boot-properties為例,注意不需要看ANDROID-QEMUD.TXT,這個是和guest os中的qemud進行相關的,已廢棄。
啟動emulator時,有一個參數-prop <key>=<value>,用于向guest os中添加屬性。
二、guest os中使用qemud service的方法
實現代碼是:http://androidxref.com/5.1.0_r1/xref/device/generic/goldfish/qemu-props/qemu-props.c,用到了頭文件:http://androidxref.com/5.1.0_r1/xref/hardware/libhardware/include/hardware/qemud.h
qemud_channel_send和qemud_channel_recv是qemu-pipe和qemud所通用的,直接對fd進行讀寫,先讀寫4個字節,為size,然后讀取具體的內容。 static __inline__ int qemud_channel_send(int fd, const void* msg, int msglen) {char header[5];if (msglen < 0)msglen = strlen((const char*)msg);if (msglen == 0)return 0;snprintf(header, sizeof header, "%04x", msglen);if (qemud_fd_write(fd, header, 4) != 4) {D("can't write qemud frame header: %s", strerror(errno));return -1;}if (qemud_fd_write(fd, msg, msglen) != msglen) {D("can4t write qemud frame payload: %s", strerror(errno));return -1;}return 0; }static __inline__ int qemud_channel_recv(int fd, void* msg, int msgsize) {char header[5];int size, avail;if (qemud_fd_read(fd, header, 4) != 4) {D("can't read qemud frame header: %s", strerror(errno));return -1;}header[4] = 0;if (sscanf(header, "%04x", &size) != 1) {D("malformed qemud frame header: '%.*s'", 4, header);return -1;}if (size > msgsize)return -1;if (qemud_fd_read(fd, msg, size) != size) {D("can't read qemud frame payload: %s", strerror(errno));return -1;}return size; }
三、注冊新的qemud service
所有的qemud service都使用pipe:qemud這個pipe service,是它的子服務。如何去實現這種子服務呢?
emulator里面有兩中結構體QemudService, QemudClient分別表示子服務,以及子服務的client。
QemudPipe和之前說的pipe類似,每次打開/dev/qemu_pipe時,kernel和emulator中都會產生一個pipe,對應一個CHANNEL,在guest os第一次通過/dev/qemu_pipe發送數據時,會創建一個QemudPipe,也就是peer,作為pipe:qemud funcs中的opaque。
pipeConnector_sendBuffers函數代碼片段:
3.1、pipe:qemud服務
代碼為external/qemu/android/emulation/android_qemud.cpp,我在android源碼中沒有找到,在另一個模擬器的repo中找到了。注意代碼中夾雜著一些guest os中qemud相關的東西,關鍵詞serial,不需要看。
初始化代碼如下,_qemudPipe_funcs就是第二篇中所說的svc->funcs,從第二次通信開始,qemu_pipe都使用這些funcs去讀寫。
/* QEMUD pipe functions.*/ static const AndroidPipeFuncs _qemudPipe_funcs = {_qemudPipe_init,_qemudPipe_closeFromGuest,_qemudPipe_sendBuffers,_qemudPipe_recvBuffers,_qemudPipe_poll,_qemudPipe_wakeOn,_qemudPipe_save,_qemudPipe_load, };/* Initializes QEMUD pipe interface.*/ static void _android_qemud_pipe_init(void) {static bool _qemud_pipe_initialized = false;if (!_qemud_pipe_initialized) {android_pipe_add_type("qemud", looper_getForThread(), &_qemudPipe_funcs);_qemud_pipe_initialized = true;} }static bool isInited = false;void android_qemud_init(CSerialLine* sl) {D("%s", __FUNCTION__);/* We don't know in advance whether the guest system supports qemud pipes,* so we will initialize both qemud machineries, the legacy (over serial* port), and the new one (over qemu pipe). Then we let the guest to connect* via one, or the other. */_android_qemud_serial_init(sl);_android_qemud_pipe_init();isInited = true; }_qemudPipe_init是建立連接后,初始化QemudPipe的代碼。
QemudMultiplexer中只有兩個鏈表有用。
先根據service name查找子服務QemudService,然后調用子服務的qemud_service_connect_client去創建QemudClient,然后去創建QemudPipe
_qemudPipe_sendBuffers是guest通過/dev/qemu_pipe寫數據時,將被調用的函數,也就是QemudClient接收到數據的函數,注意不要把send/recv的概念搞錯了。
代碼就是把guest發送的buffers拼起來,然后調用QemudClient的接收函數qemud_client_recv去處理。
/* Called when the guest has sent some data to the client.*/ static int _qemudPipe_sendBuffers(void* opaque,const AndroidPipeBuffer* buffers,int numBuffers) {QemudPipe* pipe = static_cast<QemudPipe*>(opaque);QemudClient* client = pipe->client;size_t transferred = 0;if (client == NULL) {D("%s: Unexpected NULL client", __FUNCTION__);return -1;}if (numBuffers == 1) {/* Simple case: all data are in one buffer. */D("%s: %s", __FUNCTION__, quote_bytes((char*) buffers->data, buffers->size));qemud_client_recv(client, buffers->data, buffers->size);transferred = buffers->size;} else {/* If there are multiple buffers involved, collect all data in one buffer* before calling the high level client. */uint8_t* msg, * wrk;int n;for (n = 0; n < numBuffers; n++) {transferred += buffers[n].size;}msg = static_cast<uint8_t*>(malloc(transferred));wrk = msg;for (n = 0; n < numBuffers; n++) {memcpy(wrk, buffers[n].data, buffers[n].size);wrk += buffers[n].size;}D("%s: %s", __FUNCTION__, quote_bytes((char*) msg, transferred));qemud_client_recv(client, msg, transferred);free(msg);}return transferred; }_qemudPipe_recvBuffers是guest想從/dev/qemu_pipe讀取數據時被調用的。
QemudClient寫數據時是寫到自己的ProtocolSelector.Pipe.messages中的,在這個函數中把QemudClient中的ProtocolSelector.Pipe.messages倒騰到buffers中。
/* Called when the guest is reading data from the client.*/ static int _qemudPipe_recvBuffers(void* opaque, AndroidPipeBuffer* buffers, int numBuffers) {QemudPipe* pipe = static_cast<QemudPipe*>(opaque);QemudClient* client = pipe->client;QemudPipeMessage** msg_list;AndroidPipeBuffer* buff = buffers;AndroidPipeBuffer* endbuff = buffers + numBuffers;size_t sent_bytes = 0;size_t off_in_buff = 0;if (client == NULL) {D("%s: Unexpected NULL client", __FUNCTION__);return -1;}msg_list = &client->ProtocolSelector.Pipe.messages;if (*msg_list == NULL) {/* No data to send. Let it block until we wake it up with* PIPE_WAKE_READ when service sends data to the client. */return PIPE_ERROR_AGAIN;}/* Fill in goldfish buffers while they are still available, and there are* messages in the client's message list. */while (buff != endbuff && *msg_list != NULL) {QemudPipeMessage* msg = *msg_list;/* Message data fiting the current pipe's buffer. */size_t to_copy = min(msg->size - msg->offset, buff->size - off_in_buff);memcpy(buff->data + off_in_buff, msg->message + msg->offset, to_copy);/* Update offsets. */off_in_buff += to_copy;msg->offset += to_copy;sent_bytes += to_copy;if (msg->size == msg->offset) {/* We're done with the current message. Go to the next one. */*msg_list = msg->next;free(msg);}if (off_in_buff == buff->size) {/* Current pipe buffer is full. Continue with the next one. */buff++;off_in_buff = 0;}}D("%s: -> %u (of %u)", __FUNCTION__, sent_bytes, buffers->size);return sent_bytes; }_qemudPipe_poll,PIPE_POLL_OUT總是有效,PIPE_POLL_IN需要看QemudClient的ProtocolSelector.Pipe.messages中是否有數據
_qemudPipe_wakeOn,發現ProtocolSelector.Pipe.messages中有數據時,會調用android_pipe_wake,把pipe添加到dev->signaled鏈表中。
3.2、qemud service
代碼是external/qemu/android/boot-properties.c,也是在模擬器repo中的
boot_property_init_service去注冊一個QemudService,主要函數就一個boot_property_service_connect,用于創建新的QemudClient
boot_property_service_connect創建新的QemudClient,channel一般都是-1,表示是pipe方式,而不是serial方式(使用guest qemud進程)
qemud_client_new會綁定QemudClient的讀寫函數,讀函數boot_property_client_recv(也就是qemud_client_recv)是在_qemudPipe_sendBuffers中調用的
循環執行qemud_client_send將數據(-prop指定的屬性值的列表)寫到QemudClient的ProtocolSelector.Pipe.messages中,當_qemudPipe_recvBuffers函數執行時,從QemudClient的ProtocolSelector.Pipe.messages中倒騰數據返回給guest
boot-properties服務的入口函數是boot_property_parse_option,emulator在解析-prop參數時,會調用這個函數。
獲得name和value后,調用boot_property_add2(name, namelen, value, valuelen)去添加屬性到屬性列表(_boot_properties)中
boot_property_add2會檢查服務是否已初始化,如果沒有,將調用boot_property_init_service。如果屬性名和值沒有非法字符,將申請新的屬性:prop = boot_property_alloc(name, namelen, value, valuelen)并添加到屬性列表中
boot_property_init_service先檢查是否已初始化,如果沒有,將進行初始化
QemudService* ?serv = qemud_service_register( SERVICE_NAME,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1, NULL,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boot_property_service_connect,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boot_property_save,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boot_property_load);
第二個參數是max_clients,最大客戶數量
第三個參數是serv_opaque,將傳遞給注冊的serv_connect函數的第一個參數
第四個參數是注冊的serv_connect函數
第五、第六是保存和恢復屬性鏈表的函數
總結
以上是生活随笔為你收集整理的android emulator虚拟设备分析第三篇之pipe上的qemud service的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多线程的概述(一)
- 下一篇: IntelliJ IDEA中文乱码解决办