ECLiPSe provides all the necessary built-ins needed to create UNIX processes and establish communication between them. A ECLiPSe process can communicate with other processes via streams and by sending and receiving signals.
The built-ins of the exec group and sh/1 fork a new process and execute the command given as the first argument. Sorted by their versatility, there are:
With sh/1 (or its synonym system/1) it is possible to call and execute any UNIX command from withing ECLiPSe. However it is not possible to communicate with the process. Moreover, the ECLiPSe process just waits until the command has been executed.
The exec group makes it possible to set up communication links with the child process by specifying the Streams argument. It is a list of the form
[Stdin, Stdout, Stderr]
and specifies which ECLiPSe stream should be connected to the stdin, stdout or stderr streams of the child respectively. Unless null is specified, this will establish pipes to be created between the ECLiPSe process and the child. On Berkeley UNIX systems the streams can be specified as sigio(Stream) which will setup the pipe such that the signal sigio is issued every time new data appears on the pipe. Thus, by defining a suitable interrupt handler, it is possible to service this stream in a completely asynchronous way.
The sh/1 and exec/2 built-ins both block the ECLiPSe process until the child has finished. For more sophisticated applications, the processes have to run in parallel and be synchronised explicitly. This can be achieved with exec/3 or exec_group/3. These return immediately after having created the child process and unify its process identifier (Pid) with the their argument. The Pid can be used to
The difference between exec/3 and exec_group/3 is that the latter creates a new process group for the child, such that the child does not get the interrupt, hangup and kill signals that are sent to the parent.
The process identifier of the running ECLiPSe and of its parent process are available as the global flags pid and ppid respectively. They can be accessed using get_flag/2 or env/0.
Here is an example of how to connect the UNIX utility bc (the arbitrary-precision arithmetic language) to a ECLiPSe process. We first create the process with two pipes for the child’s standard input and output. Then, by writing and reading these streams, the processes can communicate in a straightforward way. Note that it is usually necessary to flush the output after writing into a pipe:
[eclipse 1]: exec(bc, [in,out], P). P = 9759 yes. [eclipse 2]: writeln(in, "12345678902321 * 2132"), flush(in). yes. [eclipse 3]: read_string(out, end_of_line, "", _, Result). Result = "26320987419748372" yes.
In this example the child process can be terminated by closing its standard input (in other cases it may be necessary to send a signal). The built-in wait/2 is then used to wait for the process to terminate and to obtain its exit status. Don’t forget to close the ECLiPSe streams that were opend by exec/3:
[eclipse 4]: close(in), wait(P,S). P = 9759 S = 0 More? (;) yes. [eclipse 5]: at_eof(out), close(out). yes.
The UNIX (or the appropriate Windows) signals are all mapped to ECLiPSe interrupts. Their names and numbers may vary on different machines. Refer to the operating system documentation for details.
The way to deal with incoming signals is to define a Prolog or external predicate and declare it as the interrupt handler for this interrupt (using set_interrupt_handler/2). Interrupt handlers can be established for all signals except those that are not allowed to be caught by the process (like e.g., the kill signal 9). For a description of event handling in general see chapter 14.
For explicitly sending signals to other processes kill/2 is provided, which is a direct interface to the UNIX system call kill(2). Note that some signals can be set up to be raised automatically, e.g., sigio can be raised when data arrives on a pipe.