Java执行Linux命令过程的源码分析及细节把控(开发“Java命令执行器”前期准备)

接着Java执行windows命令过程的源码分析及细节把控(开发“Java命令执行器”前期准备)_江南煮酒的博客-CSDN博客说:

1、在Linux系统中没有/c和/k,只有一个 /bin/bash -c 它和直接 /bin/bash 的区别在于 "-c" 命令可以让 bash 将一个字符串作为完整的命令来执行,这样就可以将 sudo 的影响范围扩展到整条命令。

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rGf5Y2X54Wu6YWS,size_20,color_FFFFFF,t_70,g_se,x_16

 可以看到, /bin/bash -c 后面接 命令 ,而 /bin/bash 后面接 执行的脚本

2、Java执行Linux命令过程的源码分析:
直接从ProcessBuilder对象的start()方法开始,ProcessBuilder对象的start()方法return ProcessImpl.start(cmdarray,environment,dir,redirects,redirectErrorStream)(和windows系统一样),ProcessImpl.start()方法return new UNIXProcess(toCString(cmdarray[0]),argBlock,args.length,envBlock, envc[0],toCString(dir),std_fds,redirectErrorStream),可以看到执行命令最终返回一个UNIXProcess对象,UNIXProcess类定义的构造器如下:

    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();
        }
    }

UNIXProcess的构造器中使用forkAndExec()方法创建一个进程并返回进程的pid:

创建一个进程。 根据mode标志,这是通过以下机制之一完成的:
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)

形参:
fds – 一个包含三个文件描述符的数组。 索引 0、1 和 2 分别对应于标准输入、标准输出和标准错误。 在输入时,值 -1 表示创建一个管道来连接子进程和父进程。 在输出时,非 -1 的值是与已创建管道对应的父管道 fd。 此数组的元素在输入时为 -1 当且仅当它在输出时不为-1 时。
            
返回值:
子进程的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)

在Linux下的fork-and-exec流程是十分消耗资源的,这里我只简单说一下:

父进程通过fork的方式产生一个一模一样的子进程,然后被复制出来的子进程再以exec的方式来执行实际要进行的进程,最终成为一个子进程的存在。

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rGf5Y2X54Wu6YWS,size_20,color_FFFFFF,t_70,g_se,x_16

 图片来源于进程控制——fork-and-exec、system、wait - 克拉默与矩阵 - 博客园,想要全面了解fork-and-exec流程,这篇博文很详细。

3、既然fork-and-exec创建进程十分消耗资源,不建议执行一条命令就创建一个进程,那么在Linux系统上开一个进程执行多条命令会不会出现windows类似的问题,答案是不会!!!(欣喜若狂!!!)

在linux系统中,process.getInputStream().read(bytes)方法虽然在死循环读取中仍然会阻塞,但是执行linux多条命令产生的数据结果,有着明确的划分,并且都是-1结尾,所以对执行结果我们可以很好的区分并分析,竟然都是以-1结尾那么我们使用read()方法也就可以不必用死循环的方式,演示如下:

        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];
            //读取第一条命令的执行结果
            int len = process.getInputStream().read(bytes);
            System.out.println(new String(bytes, 0, len));
            //读取第二条命令的执行结果
            len = process.getInputStream().read(bytes);
            System.out.println(new String(bytes, 0, len));
            //阻塞,等待新的数据产生
            len = process.getInputStream().read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rGf5Y2X54Wu6YWS,size_18,color_FFFFFF,t_70,g_se,x_16

 虽然命令执行不完全可能导致java进程也阻塞,又或者流读取结果不完整等等问题(这些也是无法避免的),但是已经完美解决了命令的执行问题以及命令执行结果的划分问题,所以说使用Java执行系统命令并对结果进行分析是可行的,至此理论探索结束,下一篇直接实操。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值