shell实现Unix进程间信息交换的几种方法
本文将介绍在SCO OpenServer5.0.5系统中使用shell语言来实现进程间信息交换的几种方法:使用命名管道实现进程间信息交换
使用kill命令和trap语句实现进程间信息交换
使用点命令“.”实现进程间信息交换
使用export语句实现父进程对子进程的信息传递
一、使用命名管道
命名管道是一种先进先出(FIFO)的数据结构,它允许两个进程通过管道联接实现信息交换。在Unix系统中,命名管道是一种特殊类型的文件,因此可以对命名管道进行读写操作;当然,同样也会有读写和执行等权限的限制。
通过下面的命令可以创建一个命名管道:
/etc/mknod pipe_name p
其中“pipe_name”是要创建的命名管道的名字,参数p 必须出现在命名管道名字之后。
命名管道文件被创建后,一些进程就可以不断地将信息写入命名管道文件里,而另一些进程也可以不断地从命名管道文件中读取信息。对命名管道文件的读写操作是可以同时进行的。下面的例子显示命名管道的工作过程。
进程A、B、C中运行的程序只是一条简单的echo命令,它们不断地把信息写入到命名管道文件/tmp/pipe1中。与此同时,程序中的“read msg” 命令不断地从命名管道文件/tmp/pipe1中读取这些信息,从而实现这些进程间的信息交换。
程序执行时,首先创建命名管道文件,此时程序处于等待状态,直到A、B、C进程中某一个进程往命名管道中写入信息时,程序才继续往下执行。使用rm命令可以删除命名管道文件从而清除已设置的命名管道。
下面是一个用于记录考勤的例子:
在主机上运行的程序/tmp/text产生命名管道/tmp/pipe1,并不断地从命名管道中读取信息送屏幕上显示。
/tmp/text程序:
if [ ! -p /tmp/pipe1 ]
then
/etc/mknode /tmp/pipe1 p
fi
while :
do
read msg
if [ “$msg" = “" ]
then
continue
else
echo “$msg"
fi
done < /tmp/pipe1
在终端上运行的是雇员签到程序/tmp/text1。每个雇员在任何一台终端上键入自己的名字或代码,程序/tmp/text1将把这个名字连同当时的签到时间送入命名管道。
/tmp/text1程序:
tty=‘who am I | awk ‘{print $2}’’
while :
do
echo “Enter your name: \c" > /dev/$tty
read name
today=‘date’
echo “$name\t$today"
done > /tmp/pipe1
当雇员从终端上输入自己的姓名后,运行/tmp/text程序的主机将显示类似下面的结果:
wang Thu Jan 28 09:29:26 BTJ 1999
he Thu Jan 28 09:29:26 BTJ 1999
cheng Thu Jan 28 09:30:26 BTJ 1999
zhang Thu Jan 28 09:31:26 BTJ 1999
二、使用kill命令和trap语句
在Unix系统中,当检测到一个异常的内部状态,或者硬件及外部设备发出请求,或者执行某些指令时,将会向系统中的进程发出信号报告事件产生。当进程捕获到这些信号后,系统便转去执行预先设定的默认程序,完成指定的动作;这些预先设定的默认程序称之为信号的系统陷阱。
在shell中,使用trap语句为信号设置新的陷阱。当shell 捕获到一个信号时(信号11除外,因为shell本身要利用这个信号进行内存分配),它将这个信号传递给所有当前正在执行的程序(父程序和子程序),并分别执行父程序和子程序中已设置的信号陷阱。一旦陷阱程序执行结束,便返回中断点,继续执行原来的程序流程。
trap语句的基本格式:
trap command_list signal_list
command_list: 由一个或多个命令(或命令组)构成的命令列表。当命令列表中含有多个命令时要用单引号或双引号括起来,并且各命令间要用分号隔开。
signal_list:由一个或多个信号值构成的信号列表,各信号值间要用空格分开。
在一个shell程序(父程序)中重新设置信号的陷阱并不改变被这个程序所调用的子程序中同名信号的陷阱。同样,在子程序中设置的信号陷阱也不影响父程序中同名信号的陷阱。
shell在读取trap语句时,要扫描一次命令列表,以便设置陷阱。在捕获信号后,shell再次扫描命令列表,执行已设置好的陷阱程序(命令或命令组)。因此,如果命令列表中含有变量置换或命令置换表达式,shell在第一次扫描命令列表时就会用当前的变量值或命令结果置换这些表达式,使得在捕获到信号而去执行陷阱程序时,陷阱程序已经不是原来设置的陷阱程序了。为了避免这种情况发生,使用单引号而不是使用双引号把trap语句中含有变量置换或命令置换表达式的命令列表括起来;因为单引号可以消除所有字符的特殊含义,这样避免了shell在第一次扫描时执行任何置换或替代操作,直到命令列表被执行时才进行置换或替代。
向一个程序或进程传递信号方法很多,比如在程序执行时按下Ctrl+c键或Del键,将向程序传递一个SIGINT信号,执行该信号的系统陷阱将终止程序执行。使用kill命令传递信号是shell语言编程中最为常用的方法。
kill命令的基本格式是:
kill [ - signal ] PID
通常kill命令用来终止一个进程。但如果使用了带有短划线“-”的信号作为参数时,kill命令就发送该信号给PID指示的一个或多个进程,而不是终止进程。当trap语句捕获到这个信号后便执行设定的信号陷阱程序,实现进程间的相互通讯。
下面的例子显示了程序master和slave1、slave2间如何利用信号机制实现相互通讯的。首先在后台运行程序slave1和slave2,然后运行程序master。在文件/tmp/pro_list中记录了这三个程序的进程号。
程序slave1首先设置信号15的陷阱,然后把自己的当前进程写入文件/tmp/pro_list;在获得master进程号后,进入循环状态。当接收到master发出的信号15时,执行陷阱程序,显示相关信息后,向master发出信号15。
程序slave2执行情况与slave1相似。
程序master也是首先设置信号15的陷阱,然后把自己的当前进程写入文件/tmp/pro_list。在取得所有slave程序进程号后,向这些slave程序发出信号15,然后进入循环等待。当接收到slave1或slave2发出的信号15时,执行陷阱程序,显示相关信息,杀死所有slave进程,清空文件/tmp/pro_list,然后退出。
程序/tmp/slave1:
slave() {
echo “slave1 has received sighal from master"
echo “Request master to kill slave1 process"
kill -15 $master_pid
}
trap slave 15
echo “slave1_pid $$" >> /tmp/pro_list
sleep 1
while :
do
master_pid=‘awk ’$1 ~/master/
{print $2}‘/tmp/pro_list’
if [ “$master_pid" != “" ]
then break
fi
done
while :
do
sleep 1
done
程序/tmp/slave2:
slave() {
echo “slave2 has received sighal from master"
echo “Request master to kill slave2 process"
kill -15 $master_pid
}
trap slave 15
echo “slave2_pid $$" >> /tmp/pro_list
sleep 1
while :
do
master_pid=‘awk ’$1 ~/master/
{print $2}‘/tmp/pro_list’
if [ “$master_pid" != “" ]
then break
fi
done
while :
do
sleep 1
done
程序/tmp/master:
kill_slave() {
echo “Master has received signals
from slave processes"
echo “End all slave processes"
kill -9 $slave_list
>/tmp/pro_list
exit 0
}
trap kill_slave 15
echo “master_pid $$" >> /tmp/pro_list
sleep 1
slave_list=‘awk ’$1 ~/slave/
{print $2}‘/tmp/pro_list’
echo “Current slave processes are:"
echo “$slave_list"
kill -15 $slave_list
while :
do
sleep 1
done
执行程序:
$ cd /tmp
$ ./slave1&
15638
$ ./slave2&
16831
$ ./master
Current slave processes are:
15638
16831
slave1 has received signal 15 from master