private Process start(Redirect[] redirects)throws IOException { // Must convert to array first -- a malicious user-supplied // list might try to circumvent the security check. // 读取配置参数 String[] cmdarray = command.toArray(newString[command.size()]); cmdarray = cmdarray.clone();
for (String arg : cmdarray) if (arg == null) thrownewNullPointerException(); // Throws IndexOutOfBoundsException if command is empty Stringprog= cmdarray[0];
SecurityManagersecurity= System.getSecurityManager(); if (security != null) security.checkExec(prog);
// 以管道的方式开启多个进程 publicstatic List<Process> startPipeline(List<ProcessBuilder> builders)throws IOException { // Accumulate and check the builders finalintnumBuilders= builders.size(); List<Process> processes = newArrayList<>(numBuilders); try { RedirectprevOutput=null; // 除了第一个和最后一个,input,和output必须是pipe模式 // 前一个的input是后一个的output for (intindex=0; index < builders.size(); index++) { ProcessBuilderbuilder= builders.get(index); Redirect[] redirects = builder.redirects(); if (index > 0) { // check the current Builder to see if it can take input from the previous if (builder.redirectInput() != Redirect.PIPE) { thrownewIllegalArgumentException("builder redirectInput()" + " must be PIPE except for the first builder: " + builder.redirectInput()); } redirects[0] = prevOutput; } if (index < numBuilders - 1) { // check all but the last stage has output = PIPE if (builder.redirectOutput() != Redirect.PIPE) { thrownewIllegalArgumentException("builder redirectOutput()" + " must be PIPE except for the last builder: " + builder.redirectOutput()); } redirects[1] = newRedirectPipeImpl(); // placeholder for new output } processes.add(builder.start(redirects)); prevOutput = redirects[1]; } } catch (Exception ex) { // Cleanup processes already started processes.forEach(Process::destroyForcibly); processes.forEach(p -> { try { p.waitFor(); // Wait for it to exit } catch (InterruptedException ie) { // If interrupted; continue with next Process Thread.currentThread().interrupt(); } }); throw ex; } return processes; }
// Convert arguments to a contiguous block; it's easier to do // memory management in Java than in C. byte[][] args = newbyte[cmdarray.length-1][]; intsize= args.length; // For added NUL bytes for (inti=0; i < args.length; i++) { args[i] = cmdarray[i+1].getBytes(); size += args[i].length; } byte[] argBlock = newbyte[size]; inti=0; for (byte[] arg : args) { System.arraycopy(arg, 0, argBlock, i, arg.length); i += arg.length + 1; // No need to write NUL bytes explicitly }
privatevoiddestroy(boolean force) { switch (platform) { // linux,bsd,aix平台 case LINUX: case BSD: case AIX: // There is a risk that pid will be recycled, causing us to // kill the wrong process! So we only terminate processes // that appear to still be running. Even with this check, // there is an unavoidable race condition here, but the window // is very small, and OSes try hard to not recycle pids too // soon, so this is quite safe. synchronized (this) { if (!hasExited) // 调用ProcessHandle processHandle.destroyProcess(force); } try { stdin.close(); } catch (IOException ignored) {} try { stdout.close(); } catch (IOException ignored) {} try { stderr.close(); } catch (IOException ignored) {} break; // Solaris平台 case SOLARIS: // There is a risk that pid will be recycled, causing us to // kill the wrong process! So we only terminate processes // that appear to still be running. Even with this check, // there is an unavoidable race condition here, but the window // is very small, and OSes try hard to not recycle pids too // soon, so this is quite safe. synchronized (this) { if (!hasExited) processHandle.destroyProcess(force); try { stdin.close(); if (stdout_inner_stream != null) stdout_inner_stream.closeDeferred(stdout); if (stderr instanceof DeferredCloseInputStream) ((DeferredCloseInputStream) stderr) .closeDeferred(stderr); } catch (IOException e) { // ignore } } break;
@Override public Stream<ProcessHandle> children() { // The native OS code selects based on matching the requested parent pid. // If the original parent exits, the pid may have been re-used for // this newer process. // Processes started by the original parent (now dead) will all have // start times less than the start of this newer parent. // Processes started by this newer parent will have start times equal // or after this parent. return children(pid).filter(ph -> startTime <= ((ProcessHandleImpl)ph).startTime); }
intnext=0; // index of next process to check intcount= -1; // count of subprocesses scanned longppid= pid; // start looking for this parent longppStart=0; // Find the start time of the parent // 寻找当前进程的开启时间 for (inti=0; i < size; i++) { if (pids[i] == ppid) { ppStart = starttimes[i]; break; } } do { // Scan from next to size looking for ppid with child start time // the same or later than the parent. // If found, exchange it with index next // 如果把进程层级关系想成是一个树,这个算法实现的就是向下搜索。 for (inti= next; i < size; i++) { // 寻找当前线程的直接子进程 if (ppids[i] == ppid && ppStart <= starttimes[i]) { swap(pids, i, next); swap(ppids, i, next); swap(starttimes, i, next); next++; } } // 当前进程的下一个子进程 ppid = pids[++count]; // pick up the next pid to scan for // 子进程的开启时间 ppStart = starttimes[count]; // and its start time } while (count < next);
voidos_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { int fd; int cmdlen = 0; char *cmdline = NULL, *cmdEnd = NULL; // used for command line args and exe char *args = NULL; jstring cmdexe = NULL; char fn[32]; structstatstat_buf;
/* * Stat /proc/<pid> to get the user id */ // 格式化字符串 snprintf(fn, sizeof fn, "/proc/%d", pid); // 获取用户信息 if (stat(fn, &stat_buf) == 0) { unix_getUserInfo(env, jinfo, stat_buf.st_uid); JNU_CHECK_EXCEPTION(env); }
/* * Try to open /proc/<pid>/cmdline */ // 字符串拼接 strncat(fn, "/cmdline", sizeof fn - strnlen(fn, sizeof fn) - 1); if ((fd = open(fn, O_RDONLY)) < 0) { return; }
do { // Block to break out of on errors int i, truncated = 0; int count; char *s;
/* * The path name read by readlink() is limited to PATH_MAX characters. * The content of /proc/<pid>/cmdline is limited to PAGE_SIZE characters. */ cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1); if (cmdline == NULL) { break; }
/* * On Linux, the full path to the executable command is the link in * /proc/<pid>/exe. But it is only readable for processes we own. */ snprintf(fn, sizeof fn, "/proc/%d/exe", pid); // 读取链接地址 if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 0) { // null terminate and create String to store for command cmdline[cmdlen] = '\0'; cmdexe = JNU_NewStringPlatform(env, cmdline); (*env)->ExceptionClear(env); // unconditionally clear any exception }
/* * The command-line arguments appear as a set of strings separated by * null bytes ('\0'), with a further null byte after the last * string. The last string is only null terminated if the whole command * line is not exceeding (PAGE_SIZE - 1) characters. */ cmdlen = 0; s = cmdline; while ((count = read(fd, s, pageSize - cmdlen)) > 0) { cmdlen += count; s += count; } if (count < 0) { break; } // We have to null-terminate because the process may have changed argv[] // or because the content in /proc/<pid>/cmdline is truncated. cmdline[cmdlen] = '\0'; if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') { truncated = 1; } elseif (cmdlen == 0) { // /proc/<pid>/cmdline was empty. This usually happens for kernel processes // like '[kthreadd]'. We could try to read /proc/<pid>/comm in the future. } if (cmdlen > 0 && (cmdexe == NULL || truncated)) { // We have no exact command or the arguments are truncated. // In this case we save the command line from /proc/<pid>/cmdline. args = (char*)malloc(pageSize + 1); if (args != NULL) { memcpy(args, cmdline, cmdlen + 1); for (i = 0; i < cmdlen; i++) { if (args[i] == '\0') { args[i] = ' '; } } } } i = 0; if (!truncated) { // Count the arguments cmdEnd = &cmdline[cmdlen]; for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) { s += strnlen(s, (cmdEnd - s)) + 1; } } // 设置参数 unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args); } while (0);
if (cmdline != NULL) { free(cmdline); } if (args != NULL) { free(args); } if (fd >= 0) { close(fd); } }
booleandestroyProcess(boolean force) { if (this.equals(current)) { thrownewIllegalStateException("destroy of current process not allowed"); } return destroy0(pid, startTime, force); }