libuv学习笔记(10)
来源:互联网 发布:老外吃中国菜知乎 编辑:程序博客网 时间:2024/03/29 23:12
libuv学习笔记(10)
uv_process_t数据结构与相关函数
数据结构
typedef struct uv_process_s uv_process_t;struct uv_process_s { UV_HANDLE_FIELDS//uv_handle_t的成员,此处不再展开 uv_exit_cb exit_cb;//回调函数 int pid;//进程id //UV_PROCESS_PRIVATE_FIELDS宏展开: struct uv_process_exit_s { UV_REQ_FIELDS//uv_req_t的成员,此处不再展开,用来发送调用关闭回调的请求 } exit_req; BYTE* child_stdio_buffer;//要发送给子进程的文件描述符 int exit_signal;//退出信号 HANDLE wait_handle;//监控子进程是否关闭的句柄,不需要closehandle HANDLE process_handle;//进程句柄 volatile char exit_cb_pending;//进程关闭监控回调是否调用的标记};
进程配置结构体:
typedef struct uv_process_options_s { uv_exit_cb exit_cb; //进程退出后的回调 const char* file;//进程路径 utf8编码 //命令行参数utf8编码。 args[0]应该是进程路径。windows平台下调用CreateProcess函数,并将args参数 //转换为字符串,这可能会导致一些奇怪的问题,参考windows_verbatim_arguments char** args; //设置子进程环境变量 utf8编码 char** env; //工作目录 utf8编码 const char* cwd; //控制uv_spawn函数的标记量 unsigned int flags; //`stdio`成员指向一个uv_stdio_container_t数组,uv_stdio_container_t里面存放将会传递给子进 //程的文件描述符。一般来说,stdio[0]指向stdin, fd 1是stdout, fd 2 是 stderr. //在windows平台下,只有当子进程使用MSVCRT运行时环境时才能支持超过2个的文件描述符 int stdio_count; uv_stdio_container_t* stdio; //windows不支持 uv_uid_t uid; uv_gid_t gid;} uv_process_options_t;
相关函数
1.生成子进程。导出函数,在uv.h中声明,process.c中定义
初始化,内部函数,在uv_spawn中调用
static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); handle->exit_cb = NULL; handle->pid = 0; handle->exit_signal = 0; handle->wait_handle = INVALID_HANDLE_VALUE; handle->process_handle = INVALID_HANDLE_VALUE; handle->child_stdio_buffer = NULL; handle->exit_cb_pending = 0; uv_req_init(loop, (uv_req_t*)&handle->exit_req);//初始化请求,类型为UV_PROCESS_EXIT handle->exit_req.type = UV_PROCESS_EXIT; handle->exit_req.data = handle;}
生成子进程
int uv_spawn(uv_loop_t* loop, uv_process_t* process, const uv_process_options_t* options) { int i; int err = 0; WCHAR* path = NULL, *alloc_path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; STARTUPINFOW startup; PROCESS_INFORMATION info; DWORD process_flags; uv_process_init(loop, process);//初始化uv_process_t process->exit_cb = options->exit_cb;//进程关闭时的回调函数 if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { return UV_ENOTSUP;//windows不支持 } if (options->file == NULL || options->args == NULL) { return UV_EINVAL;//可执行文件路径或者命令行参数为空,直接返回错误 } assert(options->file != NULL); assert(!(options->flags & ~(UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); //将utf8字符串转换为utf16 err = uv_utf8_to_utf16_alloc(options->file, &application); if (err) goto done; //构建命令行参数,UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS表示命令行参数不要用“” err = make_program_args( options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err) goto done; //构建环境变量参数 if (options->env) { err = make_program_env(options->env, &env); if (err) goto done; } //构建工作目录参数 if (options->cwd) { /* Explicit cwd */ err = uv_utf8_to_utf16_alloc(options->cwd, &cwd); if (err) goto done; } else {//没有工作目录,就用当前工作目录 /* Inherit cwd */ DWORD cwd_len, r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { err = GetLastError(); goto done; } cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR)); if (cwd == NULL) { err = ERROR_OUTOFMEMORY; goto done; } r = GetCurrentDirectoryW(cwd_len, cwd); if (r == 0 || r >= cwd_len) { err = GetLastError(); goto done; } } //获取环境变量中的PATH path = find_path(env); if (path == NULL) { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); if (path_len == 0) { err = GetLastError(); goto done; } alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); if (alloc_path == NULL) { err = ERROR_OUTOFMEMORY; goto done; } path = alloc_path; r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { err = GetLastError(); goto done; } } //通过options中的stdio数组构建process->child_stdio_buffer //child_stdio_buffer中至少有3个,最多255,如果option中少于3个,那么child_stdio_buffer中对应 //的多余的文件描述符标记为UV_IGNORE(忽略)。根据options->stdio的类型,构建对应的传递给子进程的文 //件描述符 err = uv__stdio_create(loop, options, &process->child_stdio_buffer); if (err) goto done; //获取全路径(用户传入的可能是相对路径) application_path = search_path(application, cwd, path); if (application_path == NULL) { /* Not found. */ err = ERROR_FILE_NOT_FOUND; goto done; } //构建startup(STARTUPINFOW)结构体 startup.cb = sizeof(startup); startup.lpReserved = NULL; startup.lpDesktop = NULL; startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; //输入输出从定向(如果有的话) startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); if (options->flags & UV_PROCESS_WINDOWS_HIDE) { /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; } else { startup.wShowWindow = SW_SHOWDEFAULT; } process_flags = CREATE_UNICODE_ENVIRONMENT; if (options->flags & UV_PROCESS_DETACHED) { process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; } //创建进程 if (!CreateProcessW(application_path, arguments, NULL, NULL, 1, process_flags, env, cwd, &startup, &info)) { /* CreateProcessW failed. */ err = GetLastError(); goto done; } //获取进程句柄与进程id process->process_handle = info.hProcess; process->pid = info.dwProcessId; //如果子进程是非独立模式,将其分配给全局job对象,这样父进程关闭时也会关闭子进程 if (!(options->flags & UV_PROCESS_DETACHED)) { //uv__init_global_job_handle函数只会调用一次,创建一个作业uv__init_global_job_handle uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); //将子进程放入作业中 if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { DWORD err = GetLastError(); if (err != ERROR_ACCESS_DENIED) uv_fatal_error(err, "AssignProcessToJobObject"); } } //设置所有的命名管道进程间通信的进程id for (i = 0; i < options->stdio_count; i++) { const uv_stdio_container_t* fdopt = &options->stdio[i]; if (fdopt->flags & UV_CREATE_PIPE && fdopt->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) fdopt->data.stream)->ipc) { ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_pid = info.dwProcessId; } } //开始对于进程句柄的监控,进程关闭后,系统会将其进程句柄设为有信号状态 result = RegisterWaitForSingleObject(&process->wait_handle, process->process_handle, exit_wait_callback, (void*)process, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); if (!result) { uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); } //关闭不再使用的进程句柄 CloseHandle(info.hThread); assert(!err); //开始uv_process_t uv__handle_start(process); //清理资源 done: uv__free(application); uv__free(application_path); uv__free(arguments); uv__free(cwd); uv__free(env); uv__free(alloc_path); //清理之前生成的传递给子进程的文件描述符 if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ uv__stdio_destroy(process->child_stdio_buffer); process->child_stdio_buffer = NULL; } return uv_translate_sys_error(err);}
监听到子线程关闭之后的回调函数exit_wait_callback,将会在windows的线程池中调用
static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { uv_process_t* process = (uv_process_t*) data; uv_loop_t* loop = process->loop; assert(didTimeout == FALSE); assert(process); assert(!process->exit_cb_pending); process->exit_cb_pending = 1; //向iocp发送信号 POST_COMPLETION_FOR_REQ(loop, &process->exit_req);}
uv_run中会在收到线程退出信息后,会调用uv_process_reqs处理请求,最终调用uv_process_proc_exit
void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { int64_t exit_code; DWORD status; assert(handle->exit_cb_pending); handle->exit_cb_pending = 0; //如果handle是正在关闭状态,直接关闭handle。比如在监控关闭回调调用未完成时调用了uv_close关闭 //handle /* callback now. */ if (handle->flags & UV__HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); return; } //去掉监视 if (handle->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(handle->wait_handle); handle->wait_handle = INVALID_HANDLE_VALUE; } //停止handle uv__handle_stop(handle); if (GetExitCodeProcess(handle->process_handle, &status)) { exit_code = status; } else { /* Unable to to obtain the exit code. This should never happen. */ exit_code = uv_translate_sys_error(GetLastError()); } //调用回调 if (handle->exit_cb) { handle->exit_cb(handle, exit_code, handle->exit_signal); }}
通过uv_close关闭uv_process_t,最终会调用uv_process_close
void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { uv__handle_closing(handle);//状态变为UV_HANDLE_CLOSING if (handle->wait_handle != INVALID_HANDLE_VALUE) { //注销监视 BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE); if (!r) { /* This should never happen, and if it happens, we can't recover... */ uv_fatal_error(GetLastError(), "UnregisterWaitEx"); } handle->wait_handle = INVALID_HANDLE_VALUE; } //监控进程关闭的回调函数exit_wait_callback还未调用,直接关闭handle,否则需要等到loop处理关闭回调 //请求的时候再关闭handle if (!handle->exit_cb_pending) { uv_want_endgame(loop, (uv_handle_t*)handle); }}
使用libuv创建子进程,可以设定输出、输入重定向,或使用命名管道来进行进程间通信,这部分内容与之后的uv_pipe_t以及uv_stream_t等内容相关。
0 0
- libuv学习笔记(10)
- libuv学习笔记(2)
- libuv学习笔记(3)
- libuv学习笔记(4)
- libuv学习笔记(5)
- libuv学习笔记(6)
- libuv学习笔记(7)
- libuv学习笔记(9)
- libuv学习笔记(11)
- libuv学习笔记(12)
- libuv学习笔记(13)
- libuv学习笔记(14)
- libuv学习笔记(16)
- libuv学习笔记(17)
- libuv学习笔记(18)
- libuv学习笔记(20)
- libuv学习笔记(21)
- libuv学习笔记(22)
- 图解ByteBuffer
- LAMP环境搭建
- 访问者模式
- View 的工作原理
- Android studio 执行debug模式时一直报Socket closed
- libuv学习笔记(10)
- 感冒的一般过程
- Android6.0权限分析
- 软件测试必须要知道十个关键点
- 用PDB库调试Python程序
- T-SQL Part VIII: CROSS APPLY, OUTER APPLY
- Linux 进程及其创建
- HTML判断判断客户端是IOS还是Android
- WebBrowser 遇到类似银行网页需要安装插件才显示的文本框问题