中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android系統之Binder子系統(下)

發布時間:2020-07-04 12:17:40 來源:網絡 閱讀:430 作者:上帝之子521 欄目:系統運維

????????在上文中分析了 binder 驅動的框架以及它是怎么注冊服務、獲取服務和使用服務的整個過程,接下來就來看看 binder transaction stack 機制。

????????我們在前面也有提到進程 A 向進程 B 相互發數據,模式是進程 A 先發一個 BC_TRANSACTION 給進程 B,進程 B 收到之后給進程 A 回復一個 BR_TRANSACTION 表示數據已經收到了。然后進程 B 向進程 A 發送一個 BC_REPLY 用以發送進程 B 需要向進程 A 回復的相關數據,進程 A 收到數據后向進程 B 發送一個 BR_REPLY 表示回復的數據已經收到。它們是通過驅動來進行數據的交互的,只有這四種模式是涉及到兩進程的,其他的模式只是 app 和驅動的交互,用于改變/報告狀態。

????????那么就有兩個問題了,需要發給誰?回給誰呢?要回答這兩個問題,我們先來看看 test_client(A)和 test_server(B)是怎么工作的。根據我們之前的代碼分析,可知:在 svcmgr_publish 注冊服務時序調用 binder_call 函數,而在 binder_call 函數中構造參數時 cmd 為 BC_TRANSACTION。由此可知 test_client 先發送一個 BC_TRANSACTION 給 test_server。并且構造的是 write 相關的數據,最后調用 ioctl 來發送 BINDER_WRITE_READ。那么我們進到 binder_ioctl 函數中看看。找到 BINDER_WRITE_READ 命令,根據之前的構造的 write 相關的數據可知進入到 binder_thread_write 函數中。在 binder_thread_write 函數中找到 BC_TRANSACTION 命令,我們看到在里面做了一個 binder_transaction 的動作。

????????我們在進到 binder_transaction 函數中看看是怎樣完成的:

???????????a. 一開始,非“雙向傳輸”。所以,數據將放在 test_server 的 binder_proc.todo 鏈表中;

????????????b. 入 A 棧,test_client.binder_thread.transaction_stack 中:.from = test_client、.to_proc = test_server、.to_thread= test_server;

????????????c. 數據放入 test_server.binder_proc.todo 鏈表中,喚醒 test_server.binder_proc.wait 上的線程。

????????對于 test_server 來說,收到一個 BR_TRANSACTION,那么便在 binder_thread_read 函數中做相關的事情:

????????????a. 從 test_server.binder_proc.todo 鏈表中取出數據,進行處理;

????????????b. 入 B 棧,test_server.binder_thread.transaction_stack 中:.from = test_client、.to_proc = test_server、.to_thread= test_server;

????????對于同一個 binder_transaction 來說,通過 .from_parent 放入發送者的棧,通過 .to_parent 放入接收者的棧。

????????然后 B 向 A 發送一個 BC_REPLY,也是在 binder_transaction 函數中完成的:

????????????a. 從棧中取出 test_server.binder_thread.transaction_stack 中的相關數據(.from、.to_proc、.to_thread),由 .from 可知回復給 test_client;

????????????b. 出棧(test_server),即 test_server.binder_thread.transaction_stack = NULL;

????????????c. 數據 copy_from_user 到 test_client;

????????????d. 出棧(test_client),即 test_client.binder_thread.transaction_stack = NULL;

????????????e. 放入 todo 鏈表,并喚醒。

????????最后進程 A 收到數據后向進程 B 發送一個 BR_REPLY。返回給用戶空間,這塊就不涉及到棧的操作了。

????????至此,兩進程的交互過程已完成。

????上面涉及到的相關代碼如下:

int?svcmgr_publish(struct?binder_state?*bs,?uint32_t?target,?const?char?*name,?void?*ptr)
{
????int?status;
????unsigned?iodata[512/4];
????struct?binder_io?msg,?reply;

????bio_init(&msg,?iodata,?sizeof(iodata),?4);
????bio_put_uint32(&msg,?0);??//?strict?mode?header
????bio_put_string16_x(&msg,?SVC_MGR_NAME);
????bio_put_string16_x(&msg,?name);
????bio_put_obj(&msg,?ptr);

????if?(binder_call(bs,?&msg,?&reply,?target,?SVC_MGR_ADD_SERVICE))????//?注冊服務
????????return?-1;

????status?=?bio_get_uint32(&reply);

????binder_done(bs,?&msg,?&reply);

????return?status;
}

binder_call 函數如下

int?binder_call(struct?binder_state?*bs,
????????????????struct?binder_io?*msg,?struct?binder_io?*reply,
????????????????uint32_t?target,?uint32_t?code)
{
????int?res;
????struct?binder_write_read?bwr;
????struct?{
????????uint32_t?cmd;
????????struct?binder_transaction_data?txn;
????}?__attribute__((packed))?writebuf;
????unsigned?readbuf[32];

????if?(msg->flags?&?BIO_F_OVERFLOW)?{
????????fprintf(stderr,"binder:?txn?buffer?overflow\n");
????????goto?fail;
????}

????//?構造參數
????writebuf.cmd?=?BC_TRANSACTION;
????writebuf.txn.target.handle?=?target;
????writebuf.txn.code?=?code;
????writebuf.txn.flags?=?0;
????writebuf.txn.data_size?=?msg->data?-?msg->data0;
????writebuf.txn.offsets_size?=?((char*)?msg->offs)?-?((char*)?msg->offs0);
????writebuf.txn.data.ptr.buffer?=?(uintptr_t)msg->data0;
????writebuf.txn.data.ptr.offsets?=?(uintptr_t)msg->offs0;

????bwr.write_size?=?sizeof(writebuf);
????bwr.write_consumed?=?0;
????bwr.write_buffer?=?(uintptr_t)?&writebuf;

????hexdump(msg->data0,?msg->data?-?msg->data0);
????for?(;;)?{
????????bwr.read_size?=?sizeof(readbuf);
????????bwr.read_consumed?=?0;
????????bwr.read_buffer?=?(uintptr_t)?readbuf;

????????res?=?ioctl(bs->fd,?BINDER_WRITE_READ,?&bwr);????//?調用?ioctl?發數據

????????if?(res?<?0)?{
????????????fprintf(stderr,"binder:?ioctl?failed?(%s)\n",?strerror(errno));
????????????goto?fail;
????????}

????????res?=?binder_parse(bs,?reply,?(uintptr_t)?readbuf,?bwr.read_consumed,?0);
????????if?(res?==?0)?return?0;
????????if?(res?<?0)?goto?fail;
????}

fail:
????memset(reply,?0,?sizeof(*reply));
????reply->flags?|=?BIO_F_IOERROR;
????return?-1;
}

binder_transaction 函數如下

static?void?binder_transaction(struct?binder_proc?*proc,
???????????????????struct?binder_thread?*thread,
???????????????????struct?binder_transaction_data?*tr,?int?reply)
{
????struct?binder_transaction?*t;
????struct?binder_work?*tcomplete;
????size_t?*offp,?*off_end;
????size_t?off_min;
????struct?binder_proc?*target_proc;
????struct?binder_thread?*target_thread?=?NULL;
????struct?binder_node?*target_node?=?NULL;
????struct?list_head?*target_list;
????wait_queue_head_t?*target_wait;
????struct?binder_transaction?*in_reply_to?=?NULL;
????struct?binder_transaction_log_entry?*e;
????uint32_t?return_error?=?BR_OK;

????e?=?binder_transaction_log_add(&binder_transaction_log);
????e->call_type?=?reply???2?:?!!(tr->flags?&?TF_ONE_WAY);
????e->from_proc?=?proc->pid;
????e->from_thread?=?thread->pid;
????e->target_handle?=?tr->target.handle;
????e->data_size?=?tr->data_size;
????e->offsets_size?=?tr->offsets_size;

????if?(reply)?{
????????in_reply_to?=?thread->transaction_stack;
????????if?(in_reply_to?==?NULL)?{
????????????binder_user_error("binder:?%d:%d?got?reply?transaction?"
??????????????????????"with?no?transaction?stack\n",
??????????????????????proc->pid,?thread->pid);
????????????return_error?=?BR_FAILED_REPLY;
????????????goto?err_empty_call_stack;
????????}
????????binder_set_nice(in_reply_to->saved_priority);
????????if?(in_reply_to->to_thread?!=?thread)?{
????????????binder_user_error("binder:?%d:%d?got?reply?transaction?"
????????????????"with?bad?transaction?stack,"
????????????????"?transaction?%d?has?target?%d:%d\n",
????????????????proc->pid,?thread->pid,?in_reply_to->debug_id,
????????????????in_reply_to->to_proc??
????????????????in_reply_to->to_proc->pid?:?0,
????????????????in_reply_to->to_thread??
????????????????in_reply_to->to_thread->pid?:?0);
????????????return_error?=?BR_FAILED_REPLY;
????????????in_reply_to?=?NULL;
????????????goto?err_bad_call_stack;
????????}
????????thread->transaction_stack?=?in_reply_to->to_parent;
????????target_thread?=?in_reply_to->from;
????????if?(target_thread?==?NULL)?{
????????????return_error?=?BR_DEAD_REPLY;
????????????goto?err_dead_binder;
????????}
????????if?(target_thread->transaction_stack?!=?in_reply_to)?{
????????????binder_user_error("binder:?%d:%d?got?reply?transaction?"
????????????????"with?bad?target?transaction?stack?%d,?"
????????????????"expected?%d\n",
????????????????proc->pid,?thread->pid,
????????????????target_thread->transaction_stack??
????????????????target_thread->transaction_stack->debug_id?:?0,
????????????????in_reply_to->debug_id);
????????????return_error?=?BR_FAILED_REPLY;
????????????in_reply_to?=?NULL;
????????????target_thread?=?NULL;
????????????goto?err_dead_binder;
????????}
????????target_proc?=?target_thread->proc;
????}?else?{
????????if?(tr->target.handle)?{
????????????struct?binder_ref?*ref;
????????????ref?=?binder_get_ref(proc,?tr->target.handle);
????????????if?(ref?==?NULL)?{
????????????????binder_user_error("binder:?%d:%d?got?"
????????????????????"transaction?to?invalid?handle\n",
????????????????????proc->pid,?thread->pid);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_invalid_target_handle;
????????????}
????????????target_node?=?ref->node;
????????}?else?{
????????????target_node?=?binder_context_mgr_node;
????????????if?(target_node?==?NULL)?{
????????????????return_error?=?BR_DEAD_REPLY;
????????????????goto?err_no_context_mgr_node;
????????????}
????????}
????????e->to_node?=?target_node->debug_id;
????????target_proc?=?target_node->proc;
????????if?(target_proc?==?NULL)?{
????????????return_error?=?BR_DEAD_REPLY;
????????????goto?err_dead_binder;
????????}
????????if?(security_binder_transaction(proc->tsk,?target_proc->tsk)?<?0)?{
????????????return_error?=?BR_FAILED_REPLY;
????????????goto?err_invalid_target_handle;
????????}
????????if?(!(tr->flags?&?TF_ONE_WAY)?&&?thread->transaction_stack)?{
????????????struct?binder_transaction?*tmp;
????????????tmp?=?thread->transaction_stack;
????????????if?(tmp->to_thread?!=?thread)?{
????????????????binder_user_error("binder:?%d:%d?got?new?"
????????????????????"transaction?with?bad?transaction?stack"
????????????????????",?transaction?%d?has?target?%d:%d\n",
????????????????????proc->pid,?thread->pid,?tmp->debug_id,
????????????????????tmp->to_proc???tmp->to_proc->pid?:?0,
????????????????????tmp->to_thread??
????????????????????tmp->to_thread->pid?:?0);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_bad_call_stack;
????????????}
????????????while?(tmp)?{
????????????????if?(tmp->from?&&?tmp->from->proc?==?target_proc)
????????????????????target_thread?=?tmp->from;
????????????????tmp?=?tmp->from_parent;
????????????}
????????}
????}
????if?(target_thread)?{
????????e->to_thread?=?target_thread->pid;
????????target_list?=?&target_thread->todo;
????????target_wait?=?&target_thread->wait;
????}?else?{
????????target_list?=?&target_proc->todo;
????????target_wait?=?&target_proc->wait;
????}
????e->to_proc?=?target_proc->pid;

????/*?TODO:?reuse?incoming?transaction?for?reply?*/
????t?=?kzalloc(sizeof(*t),?GFP_KERNEL);
????if?(t?==?NULL)?{
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_alloc_t_failed;
????}
????binder_stats_created(BINDER_STAT_TRANSACTION);

????tcomplete?=?kzalloc(sizeof(*tcomplete),?GFP_KERNEL);
????if?(tcomplete?==?NULL)?{
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_alloc_tcomplete_failed;
????}
????binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);

????t->debug_id?=?++binder_last_id;
????e->debug_id?=?t->debug_id;

????if?(reply)
????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????"binder:?%d:%d?BC_REPLY?%d?->?%d:%d,?"
?????????????????"data?%p-%p?size?%zd-%zd\n",
?????????????????proc->pid,?thread->pid,?t->debug_id,
?????????????????target_proc->pid,?target_thread->pid,
?????????????????tr->data.ptr.buffer,?tr->data.ptr.offsets,
?????????????????tr->data_size,?tr->offsets_size);
????else
????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????"binder:?%d:%d?BC_TRANSACTION?%d?->?"
?????????????????"%d?-?node?%d,?data?%p-%p?size?%zd-%zd\n",
?????????????????proc->pid,?thread->pid,?t->debug_id,
?????????????????target_proc->pid,?target_node->debug_id,
?????????????????tr->data.ptr.buffer,?tr->data.ptr.offsets,
?????????????????tr->data_size,?tr->offsets_size);

????if?(!reply?&&?!(tr->flags?&?TF_ONE_WAY))
????????t->from?=?thread;
????else
????????t->from?=?NULL;
#if?defined(CONFIG_MACH_P4NOTE)?||?defined(CONFIG_MACH_SP7160LTE)?||?defined(CONFIG_MACH_TAB3)?||?defined(CONFIG_MACH_KONA)
????/*?workaround?code?for?invalid?binder?proc?*/
????if?(!proc->tsk)?{
????????binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
?????????????????"binder:?%d:%d?invalid?proc\n",
?????????????????proc->pid,?thread->pid);
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_binder_alloc_buf_failed;
????}
#endif
????t->sender_euid?=?proc->tsk->cred->euid;
????t->to_proc?=?target_proc;
????t->to_thread?=?target_thread;
????t->code?=?tr->code;
????t->flags?=?tr->flags;
????t->priority?=?task_nice(current);
????t->buffer?=?binder_alloc_buf(target_proc,?tr->data_size,
????????tr->offsets_size,?!reply?&&?(t->flags?&?TF_ONE_WAY));
????if?(t->buffer?==?NULL)?{
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_binder_alloc_buf_failed;
????}
????t->buffer->allow_user_free?=?0;
????t->buffer->debug_id?=?t->debug_id;
????t->buffer->transaction?=?t;
????t->buffer->target_node?=?target_node;
????if?(target_node)
????????binder_inc_node(target_node,?1,?0,?NULL);

????offp?=?(size_t?*)(t->buffer->data?+?ALIGN(tr->data_size,?sizeof(void?*)));

????if?(copy_from_user(t->buffer->data,?tr->data.ptr.buffer,?tr->data_size))?{
????????binder_user_error("binder:?%d:%d?got?transaction?with?invalid?"
????????????"data?ptr\n",?proc->pid,?thread->pid);
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_copy_data_failed;
????}
????if?(copy_from_user(offp,?tr->data.ptr.offsets,?tr->offsets_size))?{
????????binder_user_error("binder:?%d:%d?got?transaction?with?invalid?"
????????????"offsets?ptr\n",?proc->pid,?thread->pid);
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_copy_data_failed;
????}
????if?(!IS_ALIGNED(tr->offsets_size,?sizeof(size_t)))?{
????????binder_user_error("binder:?%d:%d?got?transaction?with?"
????????????"invalid?offsets?size,?%zd\n",
????????????proc->pid,?thread->pid,?tr->offsets_size);
????????return_error?=?BR_FAILED_REPLY;
????????goto?err_bad_offset;
????}
????off_end?=?(void?*)offp?+?tr->offsets_size;
????off_min?=?0;
????for?(;?offp?<?off_end;?offp++)?{
????????struct?flat_binder_object?*fp;
????????if?(*offp?>?t->buffer->data_size?-?sizeof(*fp)?||
????????????*offp?<?off_min?||
????????????t->buffer->data_size?<?sizeof(*fp)?||
????????????!IS_ALIGNED(*offp,?sizeof(void?*)))?{
????????????binder_user_error("%d:%d?got?transaction?with?invalid?offset,?%zd?(min?%zd,?max?%zd)\n",
???????????????????????????????proc->pid,?thread->pid,?*offp,?off_min,
???????????????????????????????(t->buffer->data_size?-?sizeof(*fp)));
????????????return_error?=?BR_FAILED_REPLY;
????????????goto?err_bad_offset;
????????}
????????fp?=?(struct?flat_binder_object?*)(t->buffer->data?+?*offp);
????????off_min?=?*offp?+?sizeof(struct?flat_binder_object);
????????switch?(fp->type)?{
????????case?BINDER_TYPE_BINDER:
????????case?BINDER_TYPE_WEAK_BINDER:?{
????????????struct?binder_ref?*ref;
????????????struct?binder_node?*node?=?binder_get_node(proc,?fp->binder);
????????????if?(node?==?NULL)?{
????????????????node?=?binder_new_node(proc,?fp->binder,?fp->cookie);
????????????????if?(node?==?NULL)?{
????????????????????return_error?=?BR_FAILED_REPLY;
????????????????????goto?err_binder_new_node_failed;
????????????????}
????????????????node->min_priority?=?fp->flags?&?FLAT_BINDER_FLAG_PRIORITY_MASK;
????????????????node->accept_fds?=?!!(fp->flags?&?FLAT_BINDER_FLAG_ACCEPTS_FDS);
????????????}
????????????if?(fp->cookie?!=?node->cookie)?{
????????????????binder_user_error("binder:?%d:%d?sending?u%p?"
????????????????????"node?%d,?cookie?mismatch?%p?!=?%p\n",
????????????????????proc->pid,?thread->pid,
????????????????????fp->binder,?node->debug_id,
????????????????????fp->cookie,?node->cookie);
????????????????goto?err_binder_get_ref_for_node_failed;
????????????}
????????????if?(security_binder_transfer_binder(proc->tsk,?target_proc->tsk))?{
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_binder_get_ref_for_node_failed;
????????????}
????????????ref?=?binder_get_ref_for_node(target_proc,?node);
????????????if?(ref?==?NULL)?{
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_binder_get_ref_for_node_failed;
????????????}
????????????if?(fp->type?==?BINDER_TYPE_BINDER)
????????????????fp->type?=?BINDER_TYPE_HANDLE;
????????????else
????????????????fp->type?=?BINDER_TYPE_WEAK_HANDLE;
????????????fp->handle?=?ref->desc;
????????????binder_inc_ref(ref,?fp->type?==?BINDER_TYPE_HANDLE,
???????????????????????&thread->todo);

????????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????????"????????node?%d?u%p?->?ref?%d?desc?%d\n",
?????????????????????node->debug_id,?node->ptr,?ref->debug_id,
?????????????????????ref->desc);
????????}?break;
????????case?BINDER_TYPE_HANDLE:
????????case?BINDER_TYPE_WEAK_HANDLE:?{
????????????struct?binder_ref?*ref?=?binder_get_ref(proc,?fp->handle);
????????????if?(ref?==?NULL)?{
????????????????binder_user_error("binder:?%d:%d?got?"
????????????????????"transaction?with?invalid?"
????????????????????"handle,?%ld\n",?proc->pid,
????????????????????thread->pid,?fp->handle);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_binder_get_ref_failed;
????????????}
????????????if?(security_binder_transfer_binder(proc->tsk,?target_proc->tsk))?{
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_binder_get_ref_failed;
????????????}
????????????if?(ref->node->proc?==?target_proc)?{
????????????????if?(fp->type?==?BINDER_TYPE_HANDLE)
????????????????????fp->type?=?BINDER_TYPE_BINDER;
????????????????else
????????????????????fp->type?=?BINDER_TYPE_WEAK_BINDER;
????????????????fp->binder?=?ref->node->ptr;
????????????????fp->cookie?=?ref->node->cookie;
????????????????binder_inc_node(ref->node,?fp->type?==?BINDER_TYPE_BINDER,?0,?NULL);
????????????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????????????"????????ref?%d?desc?%d?->?node?%d?u%p\n",
?????????????????????????ref->debug_id,?ref->desc,?ref->node->debug_id,
?????????????????????????ref->node->ptr);
????????????}?else?{
????????????????struct?binder_ref?*new_ref;
????????????????new_ref?=?binder_get_ref_for_node(target_proc,?ref->node);
????????????????if?(new_ref?==?NULL)?{
????????????????????return_error?=?BR_FAILED_REPLY;
????????????????????goto?err_binder_get_ref_for_node_failed;
????????????????}
????????????????fp->handle?=?new_ref->desc;
????????????????binder_inc_ref(new_ref,?fp->type?==?BINDER_TYPE_HANDLE,?NULL);
????????????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????????????"????????ref?%d?desc?%d?->?ref?%d?desc?%d?(node?%d)\n",
?????????????????????????ref->debug_id,?ref->desc,?new_ref->debug_id,
?????????????????????????new_ref->desc,?ref->node->debug_id);
????????????}
????????}?break;

????????case?BINDER_TYPE_FD:?{
????????????int?target_fd;
????????????struct?file?*file;

????????????if?(reply)?{
????????????????if?(!(in_reply_to->flags?&?TF_ACCEPT_FDS))?{
????????????????????binder_user_error("binder:?%d:%d?got?reply?with?fd,?%ld,?but?target?does?not?allow?fds\n",
????????????????????????proc->pid,?thread->pid,?fp->handle);
????????????????????return_error?=?BR_FAILED_REPLY;
????????????????????goto?err_fd_not_allowed;
????????????????}
????????????}?else?if?(!target_node->accept_fds)?{
????????????????binder_user_error("binder:?%d:%d?got?transaction?with?fd,?%ld,?but?target?does?not?allow?fds\n",
????????????????????proc->pid,?thread->pid,?fp->handle);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_fd_not_allowed;
????????????}

????????????file?=?fget(fp->handle);
????????????if?(file?==?NULL)?{
????????????????binder_user_error("binder:?%d:%d?got?transaction?with?invalid?fd,?%ld\n",
????????????????????proc->pid,?thread->pid,?fp->handle);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_fget_failed;
????????????}
????????????if?(security_binder_transfer_file(proc->tsk,?target_proc->tsk,?file)?<?0)?{
????????????????fput(file);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_get_unused_fd_failed;
????????????}
????????????target_fd?=?task_get_unused_fd_flags(target_proc,?O_CLOEXEC);
????????????if?(target_fd?<?0)?{
????????????????fput(file);
????????????????return_error?=?BR_FAILED_REPLY;
????????????????goto?err_get_unused_fd_failed;
????????????}
????????????task_fd_install(target_proc,?target_fd,?file);
????????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????????"????????fd?%ld?->?%d\n",?fp->handle,?target_fd);
????????????/*?TODO:?fput??*/
????????????fp->handle?=?target_fd;
????????}?break;

????????default:
????????????binder_user_error("binder:?%d:%d?got?transactio"
????????????????"n?with?invalid?object?type,?%lx\n",
????????????????proc->pid,?thread->pid,?fp->type);
????????????return_error?=?BR_FAILED_REPLY;
????????????goto?err_bad_object_type;
????????}
????}
????if?(reply)?{
????????BUG_ON(t->buffer->async_transaction?!=?0);
????????binder_pop_transaction(target_thread,?in_reply_to);
????}?else?if?(!(t->flags?&?TF_ONE_WAY))?{
????????BUG_ON(t->buffer->async_transaction?!=?0);
????????t->need_reply?=?1;
????????t->from_parent?=?thread->transaction_stack;
????????thread->transaction_stack?=?t;
????}?else?{
????????BUG_ON(target_node?==?NULL);
????????BUG_ON(t->buffer->async_transaction?!=?1);
????????if?(target_node->has_async_transaction)?{
????????????target_list?=?&target_node->async_todo;
????????????target_wait?=?NULL;
????????}?else
????????????target_node->has_async_transaction?=?1;
????}
????t->work.type?=?BINDER_WORK_TRANSACTION;
????list_add_tail(&t->work.entry,?target_list);
????tcomplete->type?=?BINDER_WORK_TRANSACTION_COMPLETE;
????list_add_tail(&tcomplete->entry,?&thread->todo);
????if?(target_wait)
????????wake_up_interruptible(target_wait);
????return;

err_get_unused_fd_failed:
err_fget_failed:
err_fd_not_allowed:
err_binder_get_ref_for_node_failed:
err_binder_get_ref_failed:
err_binder_new_node_failed:
err_bad_object_type:
err_bad_offset:
err_copy_data_failed:
????binder_transaction_buffer_release(target_proc,?t->buffer,?offp);
????t->buffer->transaction?=?NULL;
????binder_free_buf(target_proc,?t->buffer);
err_binder_alloc_buf_failed:
????kfree(tcomplete);
????binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
err_alloc_tcomplete_failed:
????kfree(t);
????binder_stats_deleted(BINDER_STAT_TRANSACTION);
err_alloc_t_failed:
err_bad_call_stack:
err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
err_no_context_mgr_node:
????binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
?????????????"binder:?%d:%d?transaction?failed?%d,?size?%zd-%zd\n",
?????????????proc->pid,?thread->pid,?return_error,
?????????????tr->data_size,?tr->offsets_size);

????{
????????struct?binder_transaction_log_entry?*fe;
????????fe?=?binder_transaction_log_add(&binder_transaction_log_failed);
????????*fe?=?*e;
????}

????BUG_ON(thread->return_error?!=?BR_OK);
????if?(in_reply_to)?{
????????thread->return_error?=?BR_TRANSACTION_COMPLETE;
????????binder_send_failed_reply(in_reply_to,?return_error);
????}?else
????????thread->return_error?=?return_error;
}

binder_thread_write 函數如下

int?binder_thread_write(struct?binder_proc?*proc,?struct?binder_thread?*thread,
????????????void?__user?*buffer,?int?size,?signed?long?*consumed)
{
????uint32_t?cmd;
????void?__user?*ptr?=?buffer?+?*consumed;
????void?__user?*end?=?buffer?+?size;

????while?(ptr?<?end?&&?thread->return_error?==?BR_OK)?{
????????if?(get_user(cmd,?(uint32_t?__user?*)ptr))
????????????return?-EFAULT;
????????ptr?+=?sizeof(uint32_t);
????????if?(_IOC_NR(cmd)?<?ARRAY_SIZE(binder_stats.bc))?{
????????????binder_stats.bc[_IOC_NR(cmd)]++;
????????????proc->stats.bc[_IOC_NR(cmd)]++;
????????????thread->stats.bc[_IOC_NR(cmd)]++;
????????}
????????switch?(cmd)?{
????????/*?省略無關代碼?*/
????????case?BC_TRANSACTION:
????????case?BC_REPLY:?{
????????????struct?binder_transaction_data?tr;

????????????if?(copy_from_user(&tr,?ptr,?sizeof(tr)))
????????????????return?-EFAULT;
????????????ptr?+=?sizeof(tr);
????????????binder_transaction(proc,?thread,?&tr,?cmd?==?BC_REPLY);
????????????break;
????????}
????????/*?省略無關代碼?*/
????????default:
????????????printk(KERN_ERR?"binder:?%d:%d?unknown?command?%d\n",
???????????????????proc->pid,?thread->pid,?cmd);
????????????return?-EINVAL;
????????}
????????*consumed?=?ptr?-?buffer;
????}
????return?0;
}

binder_thread_read 函數如下

static?int?binder_thread_read(struct?binder_proc?*proc,
??????????????????struct?binder_thread?*thread,
??????????????????void??__user?*buffer,?int?size,
??????????????????signed?long?*consumed,?int?non_block)
{
????void?__user?*ptr?=?buffer?+?*consumed;
????void?__user?*end?=?buffer?+?size;

????int?ret?=?0;
????int?wait_for_proc_work;

????if?(*consumed?==?0)?{
????????if?(put_user(BR_NOOP,?(uint32_t?__user?*)ptr))
????????????return?-EFAULT;
????????ptr?+=?sizeof(uint32_t);
????}

retry:
????wait_for_proc_work?=?thread->transaction_stack?==?NULL?&&
????????????????list_empty(&thread->todo);

????if?(thread->return_error?!=?BR_OK?&&?ptr?<?end)?{
????????if?(thread->return_error2?!=?BR_OK)?{
????????????if?(put_user(thread->return_error2,?(uint32_t?__user?*)ptr))
????????????????return?-EFAULT;
????????????ptr?+=?sizeof(uint32_t);
????????????if?(ptr?==?end)
????????????????goto?done;
????????????thread->return_error2?=?BR_OK;
????????}
????????if?(put_user(thread->return_error,?(uint32_t?__user?*)ptr))
????????????return?-EFAULT;
????????ptr?+=?sizeof(uint32_t);
????????thread->return_error?=?BR_OK;
????????goto?done;
????}


????thread->looper?|=?BINDER_LOOPER_STATE_WAITING;
????if?(wait_for_proc_work)
????????proc->ready_threads++;

????binder_unlock(__func__);

????if?(wait_for_proc_work)?{
????????if?(!(thread->looper?&?(BINDER_LOOPER_STATE_REGISTERED?|
????????????????????BINDER_LOOPER_STATE_ENTERED)))?{
????????????binder_user_error("binder:?%d:%d?ERROR:?Thread?waiting?"
????????????????"for?process?work?before?calling?BC_REGISTER_"
????????????????"LOOPER?or?BC_ENTER_LOOPER?(state?%x)\n",
????????????????proc->pid,?thread->pid,?thread->looper);
????????????wait_event_interruptible(binder_user_error_wait,
?????????????????????????binder_stop_on_user_error?<?2);
????????}
????????binder_set_nice(proc->default_priority);
????????if?(non_block)?{
????????????if?(!binder_has_proc_work(proc,?thread))
????????????????ret?=?-EAGAIN;
????????}?else
????????????ret?=?wait_event_interruptible_exclusive(proc->wait,?binder_has_proc_work(proc,?thread));
????}?else?{
????????if?(non_block)?{
????????????if?(!binder_has_thread_work(thread))
????????????????ret?=?-EAGAIN;
????????}?else
????????????ret?=?wait_event_interruptible(thread->wait,?binder_has_thread_work(thread));
????}

????binder_lock(__func__);

????if?(wait_for_proc_work)
????????proc->ready_threads--;
????thread->looper?&=?~BINDER_LOOPER_STATE_WAITING;

????if?(ret)
????????return?ret;

????while?(1)?{
????????uint32_t?cmd;
????????struct?binder_transaction_data?tr;
????????struct?binder_work?*w;
????????struct?binder_transaction?*t?=?NULL;

????????if?(!list_empty(&thread->todo))
????????????w?=?list_first_entry(&thread->todo,?struct?binder_work,?entry);
????????else?if?(!list_empty(&proc->todo)?&&?wait_for_proc_work)
????????????w?=?list_first_entry(&proc->todo,?struct?binder_work,?entry);
????????else?{
????????????if?(ptr?-?buffer?==?4?&&?!(thread->looper?&?BINDER_LOOPER_STATE_NEED_RETURN))?/*?no?data?added?*/
????????????????goto?retry;
????????????break;
????????}

????????if?(end?-?ptr?<?sizeof(tr)?+?4)
????????????break;

????????switch?(w->type)?{
????????case?BINDER_WORK_TRANSACTION:?{
????????????t?=?container_of(w,?struct?binder_transaction,?work);
????????}?break;
????????case?BINDER_WORK_TRANSACTION_COMPLETE:?{
????????????cmd?=?BR_TRANSACTION_COMPLETE;
????????????if?(put_user(cmd,?(uint32_t?__user?*)ptr))
????????????????return?-EFAULT;
????????????ptr?+=?sizeof(uint32_t);

????????????binder_stat_br(proc,?thread,?cmd);
????????????binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
?????????????????????"binder:?%d:%d?BR_TRANSACTION_COMPLETE\n",
?????????????????????proc->pid,?thread->pid);

????????????list_del(&w->entry);
????????????kfree(w);
????????????binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
????????}?break;
????????case?BINDER_WORK_NODE:?{
????????????struct?binder_node?*node?=?container_of(w,?struct?binder_node,?work);
????????????uint32_t?cmd?=?BR_NOOP;
????????????const?char?*cmd_name;
????????????int?strong?=?node->internal_strong_refs?||?node->local_strong_refs;
????????????int?weak?=?!hlist_empty(&node->refs)?||?node->local_weak_refs?||?strong;
????????????if?(weak?&&?!node->has_weak_ref)?{
????????????????cmd?=?BR_INCREFS;
????????????????cmd_name?=?"BR_INCREFS";
????????????????node->has_weak_ref?=?1;
????????????????node->pending_weak_ref?=?1;
????????????????node->local_weak_refs++;
????????????}?else?if?(strong?&&?!node->has_strong_ref)?{
????????????????cmd?=?BR_ACQUIRE;
????????????????cmd_name?=?"BR_ACQUIRE";
????????????????node->has_strong_ref?=?1;
????????????????node->pending_strong_ref?=?1;
????????????????node->local_strong_refs++;
????????????}?else?if?(!strong?&&?node->has_strong_ref)?{
????????????????cmd?=?BR_RELEASE;
????????????????cmd_name?=?"BR_RELEASE";
????????????????node->has_strong_ref?=?0;
????????????}?else?if?(!weak?&&?node->has_weak_ref)?{
????????????????cmd?=?BR_DECREFS;
????????????????cmd_name?=?"BR_DECREFS";
????????????????node->has_weak_ref?=?0;
????????????}
????????????if?(cmd?!=?BR_NOOP)?{
????????????????if?(put_user(cmd,?(uint32_t?__user?*)ptr))
????????????????????return?-EFAULT;
????????????????ptr?+=?sizeof(uint32_t);
????????????????if?(put_user(node->ptr,?(void?*?__user?*)ptr))
????????????????????return?-EFAULT;
????????????????ptr?+=?sizeof(void?*);
????????????????if?(put_user(node->cookie,?(void?*?__user?*)ptr))
????????????????????return?-EFAULT;
????????????????ptr?+=?sizeof(void?*);

????????????????binder_stat_br(proc,?thread,?cmd);
????????????????binder_debug(BINDER_DEBUG_USER_REFS,
?????????????????????????"binder:?%d:%d?%s?%d?u%p?c%p\n",
?????????????????????????proc->pid,?thread->pid,?cmd_name,?node->debug_id,?node->ptr,?node->cookie);
????????????}?else?{
????????????????list_del_init(&w->entry);
????????????????if?(!weak?&&?!strong)?{
????????????????????binder_debug(BINDER_DEBUG_INTERNAL_REFS,
?????????????????????????????"binder:?%d:%d?node?%d?u%p?c%p?deleted\n",
?????????????????????????????proc->pid,?thread->pid,?node->debug_id,
?????????????????????????????node->ptr,?node->cookie);
????????????????????rb_erase(&node->rb_node,?&proc->nodes);
????????????????????kfree(node);
????????????????????binder_stats_deleted(BINDER_STAT_NODE);
????????????????}?else?{
????????????????????binder_debug(BINDER_DEBUG_INTERNAL_REFS,
?????????????????????????????"binder:?%d:%d?node?%d?u%p?c%p?state?unchanged\n",
?????????????????????????????proc->pid,?thread->pid,?node->debug_id,?node->ptr,
?????????????????????????????node->cookie);
????????????????}
????????????}
????????}?break;
????????case?BINDER_WORK_DEAD_BINDER:
????????case?BINDER_WORK_DEAD_BINDER_AND_CLEAR:
????????case?BINDER_WORK_CLEAR_DEATH_NOTIFICATION:?{
????????????struct?binder_ref_death?*death;
????????????uint32_t?cmd;

????????????death?=?container_of(w,?struct?binder_ref_death,?work);
????????????if?(w->type?==?BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
????????????????cmd?=?BR_CLEAR_DEATH_NOTIFICATION_DONE;
????????????else
????????????????cmd?=?BR_DEAD_BINDER;
????????????if?(put_user(cmd,?(uint32_t?__user?*)ptr))
????????????????return?-EFAULT;
????????????ptr?+=?sizeof(uint32_t);
????????????if?(put_user(death->cookie,?(void?*?__user?*)ptr))
????????????????return?-EFAULT;
????????????ptr?+=?sizeof(void?*);
????????????binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
?????????????????????"binder:?%d:%d?%s?%p\n",
??????????????????????proc->pid,?thread->pid,
??????????????????????cmd?==?BR_DEAD_BINDER??
??????????????????????"BR_DEAD_BINDER"?:
??????????????????????"BR_CLEAR_DEATH_NOTIFICATION_DONE",
??????????????????????death->cookie);

????????????if?(w->type?==?BINDER_WORK_CLEAR_DEATH_NOTIFICATION)?{
????????????????list_del(&w->entry);
????????????????kfree(death);
????????????????binder_stats_deleted(BINDER_STAT_DEATH);
????????????}?else
????????????????list_move(&w->entry,?&proc->delivered_death);
????????????if?(cmd?==?BR_DEAD_BINDER)
????????????????goto?done;?/*?DEAD_BINDER?notifications?can?cause?transactions?*/
????????}?break;
????????}

????????if?(!t)
????????????continue;

????????BUG_ON(t->buffer?==?NULL);
????????if?(t->buffer->target_node)?{
????????????struct?binder_node?*target_node?=?t->buffer->target_node;
????????????tr.target.ptr?=?target_node->ptr;
????????????tr.cookie?=??target_node->cookie;
????????????t->saved_priority?=?task_nice(current);
????????????if?(t->priority?<?target_node->min_priority?&&
????????????????!(t->flags?&?TF_ONE_WAY))
????????????????binder_set_nice(t->priority);
????????????else?if?(!(t->flags?&?TF_ONE_WAY)?||
?????????????????t->saved_priority?>?target_node->min_priority)
????????????????binder_set_nice(target_node->min_priority);
????????????cmd?=?BR_TRANSACTION;
????????}?else?{
????????????tr.target.ptr?=?NULL;
????????????tr.cookie?=?NULL;
????????????cmd?=?BR_REPLY;
????????}
????????tr.code?=?t->code;
????????tr.flags?=?t->flags;
????????tr.sender_euid?=?t->sender_euid;

????????if?(t->from)?{
????????????struct?task_struct?*sender?=?t->from->proc->tsk;
????????????tr.sender_pid?=?task_tgid_nr_ns(sender,
????????????????????????????current->nsproxy->pid_ns);
????????}?else?{
????????????tr.sender_pid?=?0;
????????}

????????tr.data_size?=?t->buffer->data_size;
????????tr.offsets_size?=?t->buffer->offsets_size;
????????tr.data.ptr.buffer?=?(void?*)t->buffer->data?+
????????????????????proc->user_buffer_offset;
????????tr.data.ptr.offsets?=?tr.data.ptr.buffer?+
????????????????????ALIGN(t->buffer->data_size,
????????????????????????sizeof(void?*));

????????if?(put_user(cmd,?(uint32_t?__user?*)ptr))
????????????return?-EFAULT;
????????ptr?+=?sizeof(uint32_t);
????????if?(copy_to_user(ptr,?&tr,?sizeof(tr)))
????????????return?-EFAULT;
????????ptr?+=?sizeof(tr);

????????binder_stat_br(proc,?thread,?cmd);
????????binder_debug(BINDER_DEBUG_TRANSACTION,
?????????????????"binder:?%d:%d?%s?%d?%d:%d,?cmd?%d"
?????????????????"size?%zd-%zd?ptr?%p-%p\n",
?????????????????proc->pid,?thread->pid,
?????????????????(cmd?==?BR_TRANSACTION)???"BR_TRANSACTION"?:
?????????????????"BR_REPLY",
?????????????????t->debug_id,?t->from???t->from->proc->pid?:?0,
?????????????????t->from???t->from->pid?:?0,?cmd,
?????????????????t->buffer->data_size,?t->buffer->offsets_size,
?????????????????tr.data.ptr.buffer,?tr.data.ptr.offsets);

????????list_del(&t->work.entry);
????????t->buffer->allow_user_free?=?1;
????????if?(cmd?==?BR_TRANSACTION?&&?!(t->flags?&?TF_ONE_WAY))?{
????????????t->to_parent?=?thread->transaction_stack;
????????????t->to_thread?=?thread;
????????????thread->transaction_stack?=?t;
????????}?else?{
????????????t->buffer->transaction?=?NULL;
????????????kfree(t);
????????????binder_stats_deleted(BINDER_STAT_TRANSACTION);
????????}
????????break;
????}

done:

????*consumed?=?ptr?-?buffer;
????if?(proc->requested_threads?+?proc->ready_threads?==?0?&&
????????proc->requested_threads_started?<?proc->max_threads?&&
????????(thread->looper?&?(BINDER_LOOPER_STATE_REGISTERED?|
?????????BINDER_LOOPER_STATE_ENTERED))?/*?the?user-space?code?fails?to?*/
?????????/*spawn?a?new?thread?if?we?leave?this?out?*/)?{
????????proc->requested_threads++;
????????binder_debug(BINDER_DEBUG_THREADS,
?????????????????"binder:?%d:%d?BR_SPAWN_LOOPER\n",
?????????????????proc->pid,?thread->pid);
????????if?(put_user(BR_SPAWN_LOOPER,?(uint32_t?__user?*)buffer))
????????????return?-EFAULT;
????}
????return?0;
}

??????? 前面我們分析的是 binder transaction stack 機制,接下來看下 transaction stack 機制雙向服務。

????????此時如果有 3 個進程:P1 進程提供 S1 服務,線程用 t1, t1' ... 表示;P2 進程提供 S2 服務,線程用 t2, t2' ... 表示;P3 進程提供 S3 服務,線程用 t3, t3' ... 表示;假設 t1 ==> t2 ==> t3,那么 t3 把數據發給誰?那么 t3 把數據放到 P1 的 binder_proc.todo 鏈表,讓 P1 使用新線程 t1' 來處理,還是把數據放到 t1 的 binder_thread.todo 鏈表,讓 t1 來處理呢?

??????? 從進程 A 到進程 B:

????????????1. 進程 A 發送 BC_TRANSACTION,TR 通過 from_parent 入棧;

????????????2. 進程 B 回復 BR_TRANSACTION,TR 通過 to_parent 入棧;

????????????3. 進程 B 發送 BC_REPLY,TR 通過 to_parent 入棧,TR通 過 from_parent 出棧;

????????????4. 進程 A 回復 BR_REPLY;

????????從 t1 ==> t2: t1.sp -> TR1.from_parent -> NULL;

????????????TR1.from = t1, TR1.to_proc = P2;

????????從 t2 ==> t3: t2.sp -> TR1.to_parent -> NULL;t2.sp -> TR2.from_parent -> TR1.to_parent -> NULL;

????????????TR1.from = t1, TR1.to_proc = P2;

????????從 t3 ==> ?: t3.sp -> TR2.to_parent -> NULL;我們先來分析下 binder_transaction 函數中下面這段代碼

while?(tmp)?{
????if?(tmp->from?&&?tmp->from->proc?==?target_proc)
????????target_thread?=?tmp->from;
????tmp?=?tmp->from_parent;
}

????????????a. TR2 -> from = t2 -> proc = P2;

????????????b. tmp = TR1

????????????c. TR1 -> from = t1 -> proc = P1;target_thread = t1。

????????從這里我們就可以知道 t3 發給的是 t1,因此 TR2.from_parent -> TR1.from_parent -> NULL;t3.sp -> TR3.from_parent -> TR2.to_parent -> NULL;

????????那么 t1 收到 t3 的 BR_TRANSACTION,t1.sp -> TR3.to_parent -> TR1.from_parent -> NULL;

????????t1 發出 BC_REPLY,t1.sp -> TR1.from_parent -> NULL 入棧;t3.sp -> TR2.to_parent -> NULL 出棧;

????????t3 收到 BR_REPLY,處理 TR2,發出 BC_REPLY。t3.sp -> TR2.to_parent -> NULL;從 t3.sp 中,取出 TR2,TR2.from = t2,所以回復 t2;t3.sp -> NULL 出棧,t2.sp -> TR1.to_parent -> NULL。

????????t2 收到 BR_REPLY,處理 TR1,發出 BR_REPLY。從 t2.sp 中取出棧頂,TR1.from = t1,所以回復 t1;t2.sp = NULL,t1.sp = NULL;

????????t1 收到 BR_REPLY,處理完畢。

????????

????????那么 binder_server 的多線程是怎么工作的,client 發出請求,server 提供服務。如果 server 忙不過來時,創建多線程:

????????????1. 驅動判斷是否“忙不過來”;

????????????2. 驅動向 APP 發請求,創建新線程;

????????????3. APP 創建新線程。

????????代碼如下:

if?(proc->requested_threads?+?proc->ready_threads?==?0?&&
????proc->requested_threads_started?<?proc->max_threads?&&
????(thread->looper?&?(BINDER_LOOPER_STATE_REGISTERED?|
????BINDER_LOOPER_STATE_ENTERED))?/*?the?user-space?code?fails?to?*/
?????/*spawn?a?new?thread?if?we?leave?this?out?*/)?{
????proc->requested_threads++;
????binder_debug(BINDER_DEBUG_THREADS,
?????????"binder:?%d:%d?BR_SPAWN_LOOPER\n",
?????????proc->pid,?thread->pid);
????if?(put_user(BR_SPAWN_LOOPER,?(uint32_t?__user?*)buffer))
????????return?-EFAULT;
}

????驅動向 APP 發出“創建新線程請求”的條件:

????????1. proc->requested_treads = 0;“未處理的新線程請求”= 0

????????2. proc->requesteds = 0,空閑線程數為 0

????????3. 已啟動的線程數 < max_threads

??? 那么怎么寫 APP 呢?

????????1. 設置 max_threads

????????2. 收到 BR_SPAWN_LOOPER 后,創建新線程

????????3. 新線程發出 ioctl:BC_REGISTER_LOOPER

????????4. 像主線程一樣,進入一個循環:read driver,處理。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

游戏| 任丘市| 胶州市| 金沙县| 元阳县| 邢台市| 同仁县| 虞城县| 清流县| 策勒县| 中阳县| 泾阳县| 日照市| 康马县| 垣曲县| 固原市| 长兴县| 阳东县| 炉霍县| 五家渠市| 山阴县| 木兰县| 万安县| 桑日县| 柞水县| 锦屏县| 揭阳市| 英山县| 乐陵市| 准格尔旗| 兴隆县| 漾濞| 涪陵区| 浦城县| 康保县| 南开区| 比如县| 莲花县| 宽城| 浙江省| 龙山县|