本文將介紹在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
Request master to kill slave1 process
slave2 has received signal 15 from master
Request master to kill slave2 process
Master has received signals from slave processes
End all slave processes
15638 Killed
16831 Killed
$
三、使用點命令“.”
“.”點命令是shell的一個內部命令,它從指定的shell 文件中讀入所有命令語句並在當前進程中執行。 因此當多個shell進程(父子進程或無關進程均可)共享一組變量值時,就可以將這些變量賦值語句定義到一個shell文件裡,並在需要這些變量值的程序中使用點語句來引用這個shell文件,從而實現變量值共享(對這些變量值的修改僅涉及到這個shell文件)。但要注意的是,這個shell文件不能包括含有位置參數的語句,即不能接受$1、$2等命令行參數。
下面是一個在超市中發布每日商品價格的示范程序片段。發布每日商品價格統一由程序/tmp/jiage來執行,它為每種商品價格賦值,並把相應的賦值語句寫入文件/tmp/jiagebiao中。在各終端上運行的收款程序/tmp/shoukuan將讀入文件 /tmp/jiagebiao中所有賦值語句並在當前進程中執行,從而獲取在程序/tmp/jiage中設定的價格。
價格設定程序/tmp/jiage:
echo “Enter the price of chicken,
duck and fish: \c"
read chicken duck fish
exec 3>/tmp/jiagebiao
echo “chicken_price=$chicken" >&3
echo “duck_price=$duck" >&3
echo “fish_price=$fish" >&3
執行/tmp/jiage程序後,文件/tmp/jiagebiao中將有如下內容:
chicken_price=5.4
duck_price=2.5
fish_price=4.2
收款程序/tmp/shoukuan:
. /tmp/jiagebiao
count=0
while :
do
echo “Enter the trade name and
quantities or input q to sum: \c"
read trade$count quantity$count
eval a=\$trade$count
if [ “$a" = “q" ]
then if [ $count -gt 0 ]
then
count=‘expr $count - 1’
fi
break
fi
count=‘expr $count + 1 ’
done
echo “\n‘date’"
echo “trade name\tquantity\tsum"
while [ “$count" -ge 0 ]
do
eval trade=“\${trade$count}"
eval trade_price=“${trade}_price"
eval danjia=\${$trade_price}
eval quantity=“\${quantity$count}"
sum=‘echo “scale=2; $danjia
*$quantity"|bc’
echo “$trade\t\t$quantity\t\t$sum"
count=‘expr $count - 1 ’
done
在終端上執行程序/tmp/shoukuan將有如下顯示:
Enter the trade name and quantities
or input q to sum: chicken 2
Enter the trade name and quantities
or input q to sum: fish 3
Enter the trade name and quantities
or input q to sum: duck 4
Enter the trade name and quantities
or input q to sum: q
Thu Jan 28 09:29:29 BJT 1999:
duck 4 10
fish 3 12.6
chicken 2 10.8
四、使用export語句
通常shell變量是局部變量,無論是通過賦值操作還是通過命令賦值,其變量值只在當前進程中有效。但是經過export語句說明的shell變量就成為一個全局性變量,其變量名和變量值可以傳遞給子進程及其後代進程。在子進程中可以修改這個變量的值,但並不影響這個變量在父進程中的取值。
下面的例子中,父進程(/tmp/text)將賦值後的變量 pro_name傳遞給子進程(/tmp/text_child),在子進程中對變量pro_name所賦的新值並不影響父進程中該變量的值。
/tmp/text程序:
pro_name=“PARENT"
echo “The variable pro_name is
$pro_name in parent process"
export pro_name
/tmp/text_child
echo “The variable pro_name is
$pro_name after retund to parent process"
/tmp/text_child程序:
echo“The variable pro_name ($pro_name) is
transmited to child process"
pro_name=“CHILD"
echo “To change the variable pro_name to
$pro_name in child process"
執行程序/tmp/text:
$ /tmp/text
The variable pro_name is PARENT in parent process
The variable pro_name (PARENT)
is transmited to child process
To change the variable pro_name to CHILD in child process
The variable pro_name is PARENT after retund to parent process
$