Source code analysis and detail control of Java executing Linux commands (preliminary preparation for developing "Java command executor")

Keywords: Java Linux

next Source code analysis and detail control of Java executing windows commands (preliminary preparation for developing "Java command executor")_ Jiangnan liquor cooking blog - CSDN blog Say:

1. In Linux systems, there is no / c and / k, only one  / bin/bash -c it and direct  / The difference between bin/bash is that the "- c" command allows bash to execute a string as a complete command, which can extend the influence of sudo to the whole command.

  As you can see,  / bin/bash -c   Followed by   command  , and  / bin/bash   Followed by   Script to execute.

2. Source code analysis of Java executing Linux commands:
Start directly from the start() method of the ProcessBuilder object. The start() method of the ProcessBuilder object returns processimpl.start (cmdarray, environment, dir, redirects, redirecterrorstream) (the same as the windows system). The ProcessImpl.start() method returns new UNIXProcess (tocstring (cmdarray [0]), argblock, args.length, envblock, Envc [0], tocstring (DIR) , std_fds, redirecterrorstream), you can see that the execution of the command finally returns a UNIXProcess object. The constructor defined by the UNIXProcess class is as follows:

    UNIXProcess(final byte[] prog,
                final byte[] argBlock, final int argc,
                final byte[] envBlock, final int envc,
                final byte[] dir,
                final int[] fds,
                final boolean redirectErrorStream)
            throws IOException {

        pid = forkAndExec(launchMechanism.ordinal() + 1,
                          helperpath,
                          prog,
                          argBlock, argc,
                          envBlock, envc,
                          dir,
                          fds,
                          redirectErrorStream);

        try {
            doPrivileged((PrivilegedExceptionAction<Void>) () -> {
                initStreams(fds);
                return null;
            });
        } catch (PrivilegedActionException ex) {
            throw (IOException) ex.getException();
        }
    }

In the constructor of UNIXProcess, use the forkAndExec() method to create a process and return the pid of the process:

Create a process. according to mode Flag, which is accomplished through one of the following mechanisms:
1 - fork(2) and exec(2)
2 - posix_spawn(3P)
3 - vfork(2) and exec(2)
(4 - clone(2) and exec(2) - obsolete and currently disabled in native code)

Formal parameters:
fds – An array of three file descriptors. Indexes 0, 1, and 2 correspond to standard input, standard output, and standard error, respectively. When entering, the value -1 Represents the creation of a pipeline to connect child processes and parent processes. When outputting, non -1 The value of is the parent pipe corresponding to the created pipe fd.  The elements of this array are -1 Not if and only if it is output-1 Time.
            
Return value:
Of child processes pid

private native int forkAndExec(int mode, byte[] helperpath,
                                   byte[] prog,
                                   byte[] argBlock, int argc,
                                   byte[] envBlock, int envc,
                                   byte[] dir,
                                   int[] fds,
                                   boolean redirectErrorStream)

The fork and exec process under Linux consumes a lot of resources. Here I will just briefly say:

As like as two peas, the parent process generates a identical sub process through fork, and the copied sub processes are executed in exec mode to become the process of a sub process.

  Picture from Process control -- fork and exec, system, wait Kramer and matrix blog Park , to fully understand the fork and exec process, this blog post is very detailed.

3. Since the fork and exec creation process consumes a lot of resources, it is not recommended to create a process by executing one command. Will there be a windows like problem when opening a process to execute multiple commands on Linux? The answer is no!!! (ecstatic!!!)

In the Linux system, although the process.getInputStream().read(bytes) method is still blocked in the dead loop reading, the data results generated by executing multiple linux commands are clearly divided and end with - 1. Therefore, we can distinguish and analyze the execution results well. If they all end with - 1, then we use read() The method does not need to use an endless loop. The demonstration is as follows:

        try {
            Process process = new ProcessBuilder()
                    .command("/bin/bash")
                    .start();

            PrintWriter printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(process.getOutputStream())), true);
            printWriter.println("ls");
            printWriter.println("ls -l");
            byte[] bytes = new byte[1024];
            //Read the execution result of the first command
            int len = process.getInputStream().read(bytes);
            System.out.println(new String(bytes, 0, len));
            //Read the execution result of the second command
            len = process.getInputStream().read(bytes);
            System.out.println(new String(bytes, 0, len));
            //Blocking, waiting for new data to be generated
            len = process.getInputStream().read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }

  Although incomplete command execution may lead to Java process blocking, or incomplete stream reading results (these are unavoidable), the problem of command execution and the division of command execution results have been perfectly solved. Therefore, it is feasible to use java to execute system commands and analyze the results. So far, the theoretical exploration is over, The next article is direct practice.

Posted by racerxfactor on Mon, 22 Nov 2021 00:30:18 -0800