日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

android emulator虚拟设备分析第三篇之pipe上的qemud service

發(fā)布時(shí)間:2025/3/8 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android emulator虚拟设备分析第三篇之pipe上的qemud service 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、概述

本篇和第二篇是強(qiáng)相關(guān)的,需要結(jié)合第二篇一起看。

以boot-properties為例,注意不需要看ANDROID-QEMUD.TXT,這個(gè)是和guest os中的qemud進(jìn)行相關(guān)的,已廢棄。

啟動(dòng)emulator時(shí),有一個(gè)參數(shù)-prop <key>=<value>,用于向guest os中添加屬性。


二、guest os中使用qemud service的方法

實(shí)現(xiàn)代碼是: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


guest os中程序名為qemu-props,由/system/etc/init.goldfish.rc啟動(dòng)。 啟動(dòng)后循環(huán)幾次,嘗試打開boot-properties服務(wù)(qemud_fd = qemud_channel_open( "boot-properties" ))。 如果打開成功,發(fā)送list命令(qemud_channel_send(qemud_fd, "list", -1))給boot-properties。 然后在循環(huán)中讀取啟動(dòng)emulator時(shí)通過(guò)-prop指定的屬性(qemud_channel_recv(qemud_fd, temp, sizeof temp - 1))。 并設(shè)置guest os中的屬性(property_set(temp, q))。 qemud_channel_open,先嘗試打開/dev/qemu_pipe,寫入pipe:qemud:boot-properties。 如果pipe方式失敗,才會(huì)去通過(guò)socket和qemud進(jìn)程通信,寫入boot-properties,期待返回OK。 static __inline__ int qemud_channel_open(const char* name) {int fd;int namelen = strlen(name);char answer[2];char pipe_name[256];/* First, try to connect to the pipe. */snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", name);fd = qemu_pipe_open(pipe_name);if (fd < 0) {D("QEMUD pipe is not available for %s: %s", name, strerror(errno));/* If pipe is not available, connect to qemud control socket */fd = socket_local_client( "qemud",ANDROID_SOCKET_NAMESPACE_RESERVED,SOCK_STREAM );if (fd < 0) {D("no qemud control socket: %s", strerror(errno));return -1;}/* send service name to connect */if (qemud_fd_write(fd, name, namelen) != namelen) {D("can't send service name to qemud: %s",strerror(errno));close(fd);return -1;}/* read answer from daemon */if (qemud_fd_read(fd, answer, 2) != 2 ||answer[0] != 'O' || answer[1] != 'K') {D("cant' connect to %s service through qemud", name);close(fd);return -1;}}return fd; }

qemud_channel_send和qemud_channel_recv是qemu-pipe和qemud所通用的,直接對(duì)fd進(jìn)行讀寫,先讀寫4個(gè)字節(jié),為size,然后讀取具體的內(nèi)容。 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; }

三、注冊(cè)新的qemud service

所有的qemud service都使用pipe:qemud這個(gè)pipe service,是它的子服務(wù)。如何去實(shí)現(xiàn)這種子服務(wù)呢?

emulator里面有兩中結(jié)構(gòu)體QemudService, QemudClient分別表示子服務(wù),以及子服務(wù)的client。

QemudPipe和之前說(shuō)的pipe類似,每次打開/dev/qemu_pipe時(shí),kernel和emulator中都會(huì)產(chǎn)生一個(gè)pipe,對(duì)應(yīng)一個(gè)CHANNEL,在guest os第一次通過(guò)/dev/qemu_pipe發(fā)送數(shù)據(jù)時(shí),會(huì)創(chuàng)建一個(gè)QemudPipe,也就是peer,作為pipe:qemud funcs中的opaque。

pipeConnector_sendBuffers函數(shù)代碼片段:

Pipe* pipe = pcon->pipe;void* peer = svc->funcs.init(pipe->hwpipe, svc->opaque, pipeArgs);if (peer == NULL) {D("%s: Initialization failed for pipe %s!", __FUNCTION__, pipeName);return PIPE_ERROR_INVAL;}/* Do the evil switch now */pipe->opaque = peer;pipe->service = svc;pipe->funcs = &svc->funcs;pipe->args = ASTRDUP(pipeArgs);AFREE(pcon);


3.1、pipe:qemud服務(wù)

代碼為external/qemu/android/emulation/android_qemud.cpp,我在android源碼中沒有找到,在另一個(gè)模擬器的repo中找到了。注意代碼中夾雜著一些guest os中qemud相關(guān)的東西,關(guān)鍵詞serial,不需要看。


初始化代碼如下,_qemudPipe_funcs就是第二篇中所說(shuō)的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中只有兩個(gè)鏈表有用。

先根據(jù)service name查找子服務(wù)QemudService,然后調(diào)用子服務(wù)的qemud_service_connect_client去創(chuàng)建QemudClient,然后去創(chuàng)建QemudPipe

/* This is a callback that gets invoked when guest is connecting to the service.** Here we will create a new client as well as pipe descriptor representing new* connection.*/ static void* _qemudPipe_init(void* hwpipe, void* _looper, const char* args) {QemudMultiplexer* m = qemud_multiplexer;QemudService* sv = m->services;QemudClient* client;QemudPipe* pipe = NULL;char service_name[512];const char* client_args;size_t srv_name_len;/* 'args' passed in this callback represents name of the service the guest is* connecting to. It can't be NULL. */if (args == NULL) {D("%s: Missing address!", __FUNCTION__);return NULL;}/* 'args' contain service name, and optional parameters for the client that* is about to be created in this call. The parameters are separated from the* service name wit ':'. Separate service name from the client param. */client_args = strchr(args, ':');if (client_args != NULL) {srv_name_len = min(client_args - args, (intptr_t) sizeof(service_name) - 1);client_args++; // Past the ':'if (*client_args == '\0') {/* No actual parameters. */client_args = NULL;}} else {srv_name_len = min(strlen(args), sizeof(service_name) - 1);}memcpy(service_name, args, srv_name_len);service_name[srv_name_len] = '\0';/* Lookup registered service by its name. */while (sv != NULL && strcmp(sv->name, service_name)) {sv = sv->next;}if (sv == NULL) {D("%s: Service '%s' has not been registered!", __FUNCTION__, service_name);return NULL;}/* Create a client for this connection. -1 as a channel ID signals that this* is a pipe client. */client = qemud_service_connect_client(sv, -1, client_args);if (client != NULL) {pipe = static_cast<QemudPipe*>(android_alloc0(sizeof(*pipe)));pipe->hwpipe = hwpipe;pipe->looper = _looper;pipe->service = sv;pipe->client = client;client->ProtocolSelector.Pipe.qemud_pipe = pipe;}return pipe; }

_qemudPipe_sendBuffers是guest通過(guò)/dev/qemu_pipe寫數(shù)據(jù)時(shí),將被調(diào)用的函數(shù),也就是QemudClient接收到數(shù)據(jù)的函數(shù),注意不要把send/recv的概念搞錯(cuò)了。

代碼就是把guest發(fā)送的buffers拼起來(lái),然后調(diào)用QemudClient的接收函數(shù)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讀取數(shù)據(jù)時(shí)被調(diào)用的。

QemudClient寫數(shù)據(jù)時(shí)是寫到自己的ProtocolSelector.Pipe.messages中的,在這個(gè)函數(shù)中把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中是否有數(shù)據(jù)

static unsigned _qemudPipe_poll(void* opaque) {QemudPipe* pipe = static_cast<QemudPipe*>(opaque);QemudClient* client = pipe->client;unsigned ret = 0;if (client != NULL) {ret |= PIPE_POLL_OUT;if (client->ProtocolSelector.Pipe.messages != NULL) {ret |= PIPE_POLL_IN;}} else {D("%s: Unexpected NULL client", __FUNCTION__);}return ret; }

_qemudPipe_wakeOn,發(fā)現(xiàn)ProtocolSelector.Pipe.messages中有數(shù)據(jù)時(shí),會(huì)調(diào)用android_pipe_wake,把pipe添加到dev->signaled鏈表中。

static void _qemudPipe_wakeOn(void* opaque, int flags) {QemudPipe* qemud_pipe = (QemudPipe*) opaque;QemudClient* c = qemud_pipe->client;D("%s: -> %X", __FUNCTION__, flags);if (flags & PIPE_WAKE_READ) {if (c->ProtocolSelector.Pipe.messages != NULL) {android_pipe_wake(c->ProtocolSelector.Pipe.qemud_pipe->hwpipe,PIPE_WAKE_READ);}} }


3.2、qemud service

代碼是external/qemu/android/boot-properties.c,也是在模擬器repo中的


boot_property_init_service去注冊(cè)一個(gè)QemudService,主要函數(shù)就一個(gè)boot_property_service_connect,用于創(chuàng)建新的QemudClient

void boot_property_init_service( void ) {if (!_inited) {QemudService* serv = qemud_service_register( SERVICE_NAME,1, NULL,boot_property_service_connect,boot_property_save,boot_property_load);if (serv == NULL) {derror("could not register '%s' service", SERVICE_NAME);return;}D("registered '%s' qemud service", SERVICE_NAME);_inited = 1;} }

boot_property_service_connect創(chuàng)建新的QemudClient,channel一般都是-1,表示是pipe方式,而不是serial方式(使用guest qemud進(jìn)程)

static QemudClient* boot_property_service_connect( void* opaque,QemudService* serv,int channel,const char* client_param ) {QemudClient* client;client = qemud_client_new( serv, channel, client_param, NULL,boot_property_client_recv,NULL, NULL, NULL );qemud_client_set_framing(client, 1);return client; }

qemud_client_new會(huì)綁定QemudClient的讀寫函數(shù),讀函數(shù)boot_property_client_recv(也就是qemud_client_recv)是在_qemudPipe_sendBuffers中調(diào)用的

循環(huán)執(zhí)行qemud_client_send將數(shù)據(jù)(-prop指定的屬性值的列表)寫到QemudClient的ProtocolSelector.Pipe.messages中,當(dāng)_qemudPipe_recvBuffers函數(shù)執(zhí)行時(shí),從QemudClient的ProtocolSelector.Pipe.messages中倒騰數(shù)據(jù)返回給guest

void boot_property_client_recv( void* opaque,uint8_t* msg,int msglen,QemudClient* client ) {/* the 'list' command shall send all boot properties* to the client, then close the connection.*/if (msglen == 4 && !memcmp(msg, "list", 4)) {BootProperty* prop;for (prop = _boot_properties; prop != NULL; prop = prop->next) {qemud_client_send(client, (uint8_t*)prop->property, prop->length);}/* Send a NUL to signal the end of the list. */qemud_client_send(client, (uint8_t*)"", 1);return;}/* unknown command ? */D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg); }



boot-properties服務(wù)的入口函數(shù)是boot_property_parse_option,emulator在解析-prop參數(shù)時(shí),會(huì)調(diào)用這個(gè)函數(shù)。

獲得name和value后,調(diào)用boot_property_add2(name, namelen, value, valuelen)去添加屬性到屬性列表(_boot_properties)中

void boot_property_parse_option( const char* param ) {char* q = strchr(param,'=');const char* name;const char* value;int namelen, valuelen, ret;if (q == NULL) {dwarning("boot property missing (=) separator: %s", param);return;}name = param;namelen = q - param;value = q+1;valuelen = strlen(name) - (namelen+1);ret = boot_property_add2(name, namelen, value, valuelen);if (ret < 0) {boot_property_raise_warning(ret, name, namelen, value, valuelen);} }


boot_property_add2會(huì)檢查服務(wù)是否已初始化,如果沒有,將調(diào)用boot_property_init_service。如果屬性名和值沒有非法字符,將申請(qǐng)新的屬性:prop = boot_property_alloc(name, namelen, value, valuelen)并添加到屬性列表中

/* Appends a new boot property to the end of the internal list.*/ int boot_property_add2( const char* name, int namelen,const char* value, int valuelen ) {BootProperty* prop;/* check the lengths*/if (namelen > PROPERTY_MAX_NAME)return -1;if (valuelen > PROPERTY_MAX_VALUE)return -2;/* check that there are not invalid characters in the* property name*/const char* reject = " =$*?'\"";int nn;for (nn = 0; nn < namelen; nn++) {if (strchr(reject, name[nn]) != NULL)return -3;}/* init the service */boot_property_init_service();/* add to the end of the internal list */prop = boot_property_alloc(name, namelen, value, valuelen);*_boot_properties_tail = prop;_boot_properties_tail = &prop->next;return 0; }



boot_property_init_service先檢查是否已初始化,如果沒有,將進(jìn)行初始化
QemudService* ?serv = qemud_service_register( SERVICE_NAME,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1, NULL,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boot_property_service_connect,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boot_property_save,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boot_property_load);
第二個(gè)參數(shù)是max_clients,最大客戶數(shù)量
第三個(gè)參數(shù)是serv_opaque,將傳遞給注冊(cè)的serv_connect函數(shù)的第一個(gè)參數(shù)
第四個(gè)參數(shù)是注冊(cè)的serv_connect函數(shù)
第五、第六是保存和恢復(fù)屬性鏈表的函數(shù)

void boot_property_init_service( void ) {if (!_inited) {QemudService* serv = qemud_service_register( SERVICE_NAME,1, NULL,boot_property_service_connect,boot_property_save,boot_property_load);if (serv == NULL) {derror("could not register '%s' service", SERVICE_NAME);return;}D("registered '%s' qemud service", SERVICE_NAME);_inited = 1;} }



總結(jié)

以上是生活随笔為你收集整理的android emulator虚拟设备分析第三篇之pipe上的qemud service的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。