`
Hillen.chan
  • 浏览: 61228 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

如何创建一个进程,如何进程调用进程

阅读更多
java一般用线程就够了,多进程优势在于每个进程互不干扰,劣势在于太耗费内存,任何事情都不是绝对的,在编写Java程序时,有时候需要在Java程序中执行另外一个程序。

1、启动程序

Java提供了两种方法用来启动其它程序:

(1)使用Runtime的exec()方法

(2)使用ProcessBuilder的start()方法

不管在哪种操作系统下,程序具有基本类似的一些属性。一个程序启动后就程序操作系统的一个进程,进程在执行的时候有自己的环境变量、有自己的工作目录。Runtime和ProcessBuilder提供了不同的方式来启动程序,设置启动参数、环境变量和工作目录。

能够在Java中执行的外部程序,必须是一个实际存在的可执行文件,对于shell下的内嵌命令是不能直接执行的。

采用Runtime的exec执行程序时,首先要使用Runtime的静态方法得到一个Runtime,然后调用Runtime的exec方法。可以将要执行的外部程序和启动参数、环境变量、工作目录作为参数传递给exec方法,该方法执行后返回一个Process代表所执行的程序。

Runtime有六个exec方法,其中两个的定义为:

public Process exec(String[] cmdarray, String[] envp, File dir)

public Process exec(String command, String[] envp, File dir)

cmdarray和command为要执行的命令,可以将命令和参数作为一个字符串command传递给exec()方法,也可以将命令和参数一个一个的方在数组cmdarray里传递给exec()方法。

envp为环境变量,以name=value的形式放在数组中。dir为工作目录。

可以不要dir参数,或者不要envp和dir参数,这样就多出了其它4个exec()方法。如果没有dir参数或者为null,那么新启动的进程就继承当前java进程的工作目录。如果没有envp参数或者为null,那么新启动的进程就继承当前java进程的环境变量。

也可以使用ProcessBuilder类启动一个新的程序,该类是后来添加到JDK中的,而且被推荐使用。通过构造函数设置要执行的命令以及参数,或者也可以通过command()方法获取命令信息后在进行设置。通过directory(File directory) 方法设置工作目录,通过environment()获取环境变量信息来修改环境变量。

在使用ProcessBuilder构造函数创建一个新实例,设置环境变量、工作目录后,可以通过start()方法来启动新程序,与Runtime的exec()方法一样,该方法返回一个Process对象代表启动的程序。

ProcessBuilder与Runtime.exec()方法的不同在于ProcessBuilder提供了redirectErrorStream(boolean redirectErrorStream) 方法,该方法用来将进程的错误输出重定向到标准输出里。即可以将错误输出都将与标准输出合并。

2、Process

不管通过那种方法启动进程后,都会返回一个Process类的实例代表启动的进程,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法:

(1) void destroy()
          杀掉子进程。
         一般情况下,该方法并不能杀掉已经启动的进程,不用为好。

(2) int exitValue()
          返回子进程的出口值。
          只有启动的进程执行完成、或者由于异常退出后,exitValue()方法才会有正常的返回值,否则抛出异常。

(3)InputStream getErrorStream()
          获取子进程的错误流。
         如果错误输出被重定向,则不能从该流中读取错误输出。

(4)InputStream getInputStream()
          获取子进程的输入流。
          可以从该流中读取进程的标准输出。

(5)OutputStream getOutputStream()
          获取子进程的输出流。
          写入到该流中的数据作为进程的标准输入。

(6) int waitFor()
          导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。
        

通过该类提供的方法,可以实现与启动的进程之间通信,达到交互的目的。

3、从标准输出和错误输出流读取信息

从启动其他程序的Java进程看,已启动的其他程序输出就是一个普通的输入流,可以通过getInputStream()和getErrorStream来获取。

对于一般输出文本的进程来说,可以将InputStream封装成BufferedReader,然后就可以一行一行的对进程的标准输出进行处理。

4、举例

(1)Runtime.exec()

import java.io.BufferedReader; 
import java.io.File; 
import java.io.InputStreamReader; 

public class Test1 { 
public static void main(String[] args) { 
   try { 
    Process p = null; 
    String line = null; 
    BufferedReader stdout = null; 

    //list the files and directorys under C:\ 
    p = Runtime.getRuntime().exec("CMD.exe /C dir", null, new File("C:\\")); 
    stdout = new BufferedReader(new InputStreamReader(p 
      .getInputStream())); 
    while ((line = stdout.readLine()) != null) { 
     System.out.println(line); 
    } 
    stdout.close(); 

    //echo the value of NAME 
    p = Runtime.getRuntime().exec("CMD.exe /C echo %NAME%", new String[] {"NAME=TEST"});   
    stdout = new BufferedReader(new InputStreamReader(p 
      .getInputStream())); 
    while ((line = stdout.readLine()) != null) { 
     System.out.println(line); 
    } 
    stdout.close(); 
   } catch (Exception e) { 
    e.printStackTrace(); 
   } 
} 

} 


(2)ProcessBuilder
import java.io.BufferedReader; 
import java.io.File; 
import java.io.InputStreamReader; 
import java.util.ArrayList; 
import java.util.List; 

public class Test2 { 
public static void main(String[] args) { 
   try { 
    List<String> list = new ArrayList<String>(); 
    ProcessBuilder pb = null; 
    Process p = null; 
    String line = null; 
    BufferedReader stdout = null; 
   
    //list the files and directorys under C:\ 
    list.add("CMD.EXE"); 
    list.add("/C"); 
    list.add("dir"); 
    pb = new ProcessBuilder(list); 
    pb.directory(new File("C:\\")); 
    p = pb.start(); 
   
    stdout = new BufferedReader(new InputStreamReader(p 
      .getInputStream())); 
    while ((line = stdout.readLine()) != null) { 
     System.out.println(line); 
    } 
    stdout.close(); 

    //echo the value of NAME 
    pb = new ProcessBuilder(); 
    pb.command(new String[] {"CMD.exe", "/C", "echo %NAME%"}); 
    pb.environment().put("NAME", "TEST"); 
    p = pb.start(); 
   
    stdout = new BufferedReader(new InputStreamReader(p 
      .getInputStream())); 
    while ((line = stdout.readLine()) != null) { 
     System.out.println(line); 
    } 
    stdout.close(); 
   } catch (Exception e) { 
    e.printStackTrace(); 
   } 
} 

} 


5、获取进程的返回值

通常,一个程序/进程在执行结束后会向操作系统返回一个整数值,0一般代表执行成功,非0表示执行出现问题。有两种方式可以用来获取进程的返回值。一是利用waitFor(),该方法是阻塞的,执导进程执行完成后再返回。该方法返回一个代表进程返回值的整数值。另一个方法是调用exitValue()方法,该方法是非阻塞的,调用立即返回。但是如果进程没有执行完成,则抛出异常。

6、阻塞的问题

由Process代表的进程在某些平台上有时候并不能很好的工作,特别是在对代表进程的标准输入流、输出流和错误输出进行操作时,如果使用不慎,有可能导致进程阻塞,甚至死锁。

如果将以上事例中的从标准输出重读取信息的语句修改为从错误输出流中读取:

    stdout = new BufferedReader(new InputStreamReader(p
      .getErrorStream()));

那么程序将发生阻塞,不能执行完成,而是hang在那里。

当进程启动后,就会打开标准输出流和错误输出流准备输出,当进程结束时,就会关闭他们。在以上例子中,错误输出流没有数据要输出,标准输出流中有数据输出。由于标准输出流中的数据没有被读取,进程就不会结束,错误输出流也就不会被关闭,因此在调用readLine()方法时,整个程序就会被阻塞。为了解决这个问题,可以根据输出的实际先后,先读取标准输出流,然后读取错误输出流。

但是,很多时候不能很明确的知道输出的先后,特别是要操作标准输入的时候,情况就会更为复杂。这时候可以采用线程来对标准输出、错误输出和标准输入进行分别处理,根据他们之间在业务逻辑上的关系决定读取那个流或者写入数据。

针对标准输出流和错误输出流所造成的问题,可以使用ProcessBuilder的redirectErrorStream()方法将他们合二为一,这时候只要读取标准输出的数据就可以了。

当在程序中使用Process的waitFor()方法时,特别是在读取之前调用waitFor()方法时,也有可能造成阻塞。可以用线程的方法来解决这个问题,也可以在读取数据后,调用waitFor()方法等待程序结束。

总之,解决阻塞的方法应该有两种:

(1)使用ProcessBuilder类,利用redirectErrorStream方法将标准输出流和错误输出流合二为一,在用start()方法启动进程后,先从标准输出中读取数据,然后调用waitFor()方法等待进程结束。

如:
import java.io.BufferedReader; 
import java.io.File; 
import java.io.InputStreamReader; 
import java.util.ArrayList; 
import java.util.List; 

public class Test3 { 
public static void main(String[] args) { 
   try { 
    List<String> list = new ArrayList<String>(); 
    ProcessBuilder pb = null; 
    Process p = null; 
    String line = null; 
    BufferedReader stdout = null; 
   
    //list the files and directorys under C:\ 
    list.add("CMD.EXE"); 
    list.add("/C"); 
    list.add("dir1"); 
    pb = new ProcessBuilder(list); 
    pb.directory(new File("C:\\")); 
    //merge the error output with the standard output 
    pb.redirectErrorStream(true); 
    p = pb.start(); 
   
    //read the standard output 
    stdout = new BufferedReader(new InputStreamReader(p 
      .getInputStream())); 
    while ((line = stdout.readLine()) != null) { 
     System.out.println(line); 
    } 
    int ret = p.waitFor(); 
    System.out.println("the return code is " + ret); 
   
    stdout.close(); 

   } catch (Exception e) { 
    e.printStackTrace(); 
   } 
} 

} 



(2)使用线程
import java.util.*; 
import java.io.*; 

class StreamWatch extends Thread { 
InputStream is; 

String type; 

List<String> output = new ArrayList<String>(); 

boolean debug = false; 

StreamWatch(InputStream is, String type) { 
   this(is, type, false); 
} 

StreamWatch(InputStream is, String type, boolean debug) { 
   this.is = is; 
   this.type = type; 
   this.debug = debug; 
} 

public void run() { 
   try { 
    PrintWriter pw = null; 

    InputStreamReader isr = new InputStreamReader(is); 
    BufferedReader br = new BufferedReader(isr); 
    String line = null; 
    while ((line = br.readLine()) != null) { 
     output.add(line); 
     if (debug) 
      System.out.println(type + ">" + line); 
    } 
    if (pw != null) 
     pw.flush(); 
   } catch (IOException ioe) { 
    ioe.printStackTrace(); 
   } 
} 

public List<String> getOutput() { 
   return output; 
} 
} 

public class Test5 { 
public static void main(String args[]) { 
   try { 
    List<String> list = new ArrayList<String>(); 
    ProcessBuilder pb = null; 
    Process p = null; 

    // list the files and directorys under C:\ 
    list.add("CMD.EXE"); 
    list.add("/C"); 
    list.add("dir1"); 
    pb = new ProcessBuilder(list); 
    pb.directory(new File("C:\\")); 
    p = pb.start(); 

    // process error and output message 
    StreamWatch errorWatch = new StreamWatch(p.getErrorStream(), 
      "ERROR"); 
    StreamWatch outputWatch = new StreamWatch(p.getInputStream(), 
      "OUTPUT"); 

    // start to watch 
    errorWatch.start(); 
    outputWatch.start(); 

    //wait for exit 
    int exitVal = p.waitFor(); 

    //print the content from ERROR and OUTPUT 
    System.out.println("ERROR: " + errorWatch.getOutput()); 
    System.out.println("OUTPUT: " + outputWatch.getOutput()); 
   
    System.out.println("the return code is " + exitVal); 

   } catch (Throwable t) { 
    t.printStackTrace(); 
   } 
} 
} 








7、在Java中执行Java程序

执行一个Java程序的关键在于:

(1)知道JAVA虚拟机的位置,即java.exe或者java的路径

(2)知道要执行的java程序的位置

(3)知道该程序所依赖的其他类的位置

举一个例子,一目了然。

(1)待执行的Java类

public class MyTest { 
public static void main(String[] args) { 
   System.out.println("OUTPUT one"); 
   System.out.println("OUTPUT two"); 
   System.err.println("ERROR 1"); 
   System.err.println("ERROR 2");  
   for(int i = 0; i < args.length; i++) 
   { 
    System.out.printf("args[%d] = %s.", i, args[i]); 
   } 
} 
} 



(2)执行该类的程序


import java.util.*; 
import java.io.*; 

class StreamWatch extends Thread { 
InputStream is; 

String type; 

List<String> output = new ArrayList<String>(); 

boolean debug = false; 

StreamWatch(InputStream is, String type) { 
   this(is, type, false); 
} 

StreamWatch(InputStream is, String type, boolean debug) { 
   this.is = is; 
   this.type = type; 
   this.debug = debug; 
} 

public void run() { 
   try { 
    PrintWriter pw = null; 

    InputStreamReader isr = new InputStreamReader(is); 
    BufferedReader br = new BufferedReader(isr); 
    String line = null; 
    while ((line = br.readLine()) != null) { 
     output.add(line); 
     if (debug) 
      System.out.println(type + ">" + line); 
    } 
    if (pw != null) 
     pw.flush(); 
   } catch (IOException ioe) { 
    ioe.printStackTrace(); 
   } 
} 

public List<String> getOutput() { 
   return output; 
} 
} 

public class Test6 { 
public static void main(String args[]) { 
   try { 
    List<String> list = new ArrayList<String>(); 
    ProcessBuilder pb = null; 
    Process p = null; 
   
    String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; 
    String classpath = System.getProperty("java.class.path"); 
    // list the files and directorys under C:\ 
    list.add(java); 
    list.add("-classpath"); 
    list.add(classpath); 
    list.add(MyTest.class.getName()); 
    list.add("hello"); 
    list.add("world"); 
    list.add("good better best"); 
   
    pb = new ProcessBuilder(list); 
    p = pb.start(); 
   
    System.out.println(pb.command()); 

    // process error and output message 
    StreamWatch errorWatch = new StreamWatch(p.getErrorStream(), 
      "ERROR"); 
    StreamWatch outputWatch = new StreamWatch(p.getInputStream(), 
      "OUTPUT"); 

    // start to watch 
    errorWatch.start(); 
    outputWatch.start(); 

    //wait for exit 
    int exitVal = p.waitFor(); 

    //print the content from ERROR and OUTPUT 
    System.out.println("ERROR: " + errorWatch.getOutput()); 
    System.out.println("OUTPUT: " + outputWatch.getOutput()); 
   
    System.out.println("the return code is " + exitVal); 

   } catch (Throwable t) { 
    t.printStackTrace(); 
   } 
} 
} 

进程调用进程,会涉及进程之间通信,这个时候可以用socket,jms,文件共享来实现,被调用的进程直接debug比较困难,可以通过打印日志和创建文件,输入输出流,管道来实现。


---------------------------原创: http://nannan408.iteye.com
分享到:
评论

相关推荐

    进程间函数调用

    remote-function是一个跨进程通讯库,它可以像调用本进程函数一样调用另外一个进程的函数。 remote-function支持调用普通函数,也支持调用类的成员函数。 remote-function底层使用命令管道进行通讯,内置的流程完成...

    进程的创建编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,

    编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时, 在系统中有一个父进程和两个子进程活动。 让每一个进程在屏幕上显示一个字符:父进程显示“A”; 子进程分别显示字符“b”和“c”。试观察记录...

    (修改版)实现进程的软中断通信。要求:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断

    使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上的中断信号(即按DEL键);当捕捉到中断信号后,父进程用系统调用Kill()向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止: ...

    进程的管道通信编制一段程序,实现进程的管道通信,使用系统调用pipe()建立一个管道文件;两个子进程P1和P2

    编制一段程序,实现进程的管道通信,使用系统调用pipe()建立一个管道文件;两个子进程P1和P2 分别向管道各写一句话: Child1 is sending a message! Child2 is sending a message! 而父进程则从管道中读出来自于...

    linux下的进程管理演示(c语言)

    当此程序运行时,在系统中有一个父进程和两个子进程活动。每个进程在屏幕上显示一个字符,记录屏幕上的显示结果,并分析原因。修改以编写的程序,将每个进程输出一个字符改为每个进程输出一句话。 进程的软中断通信...

    实验2 进程的创建与控制实验.docx

    1. fork 的返回值:fork() 系统调用返回两个值,一个是子进程的进程 ID,另一个是父进程的进程 ID。 2. 进程树:进程树是一种树形结构,用于描述进程之间的父子关系。 3. 进程并发执行:进程并发执行是指多个进程...

    fork一个进程,fork()函数fork()函数

    fork一个进程,fork()函数fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,这个新产生的进程称为子进程。一个进程调用fork()函数后,系统先给新的进程...

    进程的创建控制实验

    内容:编写一段程序,使用系统调用fork()创建两个子进程p1和p2。而且父进程输出字符串“father”,第一个子进程输出字符串“borther1”,第二个子进程输出字符串“borther2” 要求:(1)掌握系统调用fork()的使用...

    操作系统实现进程调用

    指针:进程按顺序排成循环链表,用指针指出下一个进程的进程控制块首地址,最后一个进程中的指针指出第一个进程的进程控制块首地址。 要求运行时间:假设进程需要运行的单位时间数。 已运行时间:假设进程已经运行的...

    linux实验四 进程控制实验

    编写一段程序,使用系统调用fork( )创建两个子进程,在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b” 和“c”。试观察记录屏幕上的显示结果...

    进程的创建

    编写一段程序,使用系统调用fork()创建两个子进程.当此程序运行时,在系统中有一个父进程和两个子进程活动.让每一个进程在屏幕上显示一个字符:父进程显示字符”a”;子进程分别显示字符”b”和字符”c”.试观察记录屏幕...

    匿名管道的实现 管道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。

    管道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。匿名管道...

    Linux 僵尸进程产生原因及解决方法

    Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态。例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些...如果一个进程已经终止,但是它的父进程尚未调用 wai

    weblogic守护进程:监控weblogic进程,当发现进程停止时调用重启脚本重启

    #功能:weblogic守护进程:监控weblogic进程,当发现进程停止时调用重启脚本重启 #参数:进程唯一标识码,例如“weblogic” #依赖脚本:数据库操作脚本dbExecurteSQL.sh,weblogic重启脚本$PSSC_DOMAIN/startPssc.sh...

    进程的管道通信 进程管理

    使用系统调用pipe()建立一个管道,两个子进程分别向管道写信息,父进程则从管道读出来自子进程的信息,显示在屏幕上,记录屏幕的显示结果,分析原因。 任务 编制一段程序,实现进程的管道通信。使用系统调用pipe()...

    进程间相互调用

    C#进程间相互调用,了解进程间调用方法。

    操作系统进程控制

    操作系统实验LInux的进程控制与管理 编写一段程序,使用系统调用fork( )...当此程序运行时,在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一句话和进程ID,试观察记录屏幕上的显示结果,并分析原因。

    024-1跨进程调用CALL+跨进程注入代码.flv

    跨进程调用带多个的参数CALL // myInject_dll.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include"RWA.h" //PVOID 跨进程分配内存(WORD nSize ); //1、获取进程句柄 //2、读写 分配...

    进程通信----软中断

    使用系统调用fork()创建两个子程序,再用系统调用signal()接收父进程从键盘上来的中断信号(即按Ctrl+c键);当有中断信号后,父进程用系统调用Kill()向两个子进程发出信号,子进程有信号后分别输出 Child ...

    进程管理:进程的创建

    编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”:子进程分别显示“b”和“c”。 2. 修改已编写...

Global site tag (gtag.js) - Google Analytics