rsh有兩種使用模式:
rsh $host : 遠程登錄,啟動交互式進程。
rsh $host $command :遠程執行命令,並顯示輸出。
rsh hosthostcommand
rsh $host $command的作用是:
1.在遠程機器上執行命令$command
2.通過網絡連接(socket)重定向當前進程和遠端進程的標准輸入和標准輸出
3.遠端rsh進程在遠端進程結束後結束
4.本地rsh進程讀取遠端進程的標准輸出直到結束(EOF)
深刻理解這個執行過程有助於理解各種“奇怪”的現象和用法。
復制代碼代碼如下:
+ Suspended (tty input)
$ rsh localhost infinite-loop &
[1] + Suspended (tty input) rsh pv007 infinite-loop
$ rsh -n localhost infinite-loop &
# 執行正常
後台執行rsh命令時,提示了和標准輸入相關的錯誤信息。這是因為rsh默認會把當前窗口的標准輸入重定向到遠端進程。
而本地rsh進程作為後台程序運行的話,標准輸入被“阻塞”了。
通過-n選項制定不需要重定向標准輸入(stdin)。
遠端進程的執行
執行命令
復制代碼代碼如下:rsh somehost infinite-loop
在遠端機器上查看相關進程:
復制代碼代碼如下:
$ pstree -a -p 3353
in.rshd,3353
└─csh,3363 -c infinite-loop
└─infinite-loop,3632 /u/szhang/bin/infinite-loop
可以看出,遠端機器上的rshd進程負責啟動遠端進程。而且可以看出是通過csh -c的方式啟動的(這裡用戶的默認Shell是C Shell)。
遠端進程的標准IO
檢查遠端進程的文件描述符:
復制代碼代碼如下:
$ ls -l /proc/3363/fd /proc/3632/fd
/proc/3363/fd:
total 0
lrwx------. 1 Jul 30 23:47 16 -> socket:[1184748899]
lrwx------. 1 Jul 30 23:47 17 -> socket:[1184748899]
l-wx------. 1 Jul 30 23:47 18 -> pipe:[1184749092]
lrwx------. 1 Jul 30 23:47 19 -> socket:[1184748899]
復制代碼代碼如下:
/proc/3632/fd:
total 0
lrwx------. 1 Jul 30 23:47 0 -> socket:[1184748899]
lrwx------. 1 Jul 30 23:47 1 -> socket:[1184748899]
l-wx------. 1 Jul 30 23:47 2 -> pipe:[1184749092]
可以看出遠端裡程的標准輸入輸出是被重定向到socket上的:
1.stdin 和 stdout 共享一個socket連接
2.stderr 則通過一個pipe重定向(重定向到stdout ???)
3.rsh 的返回值
rsh程序自身的返回值表明的是rsh自身的運行狀況,而不是遠端進程的返回值。
獲得遠端進程的返回值
復制代碼代碼如下:
# 遠端是C Shell
$ rsh $host "$command ; echo $status"
復制代碼代碼如下:
# 遠端是Bash Shell
$ rsh $host "$command ; echo $?"
復制代碼代碼如下:
# 遠端Shell類型不確定
$ rsh $host "sh -c '$command ; echo $?'"
啟動遠端進程所用的Shell
由於用於啟動遠端進程的Shell類型是未知的,而有些操作的語法在不同Shell裡是不同的。
比如輸入輸出重定向、命令返回值等。
解決該問題的方法之一是通過明確指定的Shell來啟動真正需要的裡程。比如:
復制代碼代碼如下:
# 不確定遠端Shell的類型,顯式通過Bash Shell來啟動需要的進程
$ rsh -n $host "sh -c '$command > /dev/null 2>&1'"
另一種思路,則是通過一個wrapper程序來啟動真正的命令。
通過rsh在遠端執行後台進程
想在遠端機器上執行後台進程。命令rsh $host "$command &"是不起作用的,會導致本地的rsh進程不能結束。
背後的原因應該是,$command的標准輸入輸出通常仍然綁定在rsh連接的socket上,從而導致本地的rsh進程無法讀取到文件結束符EOF。
知道了原因就知道該怎麼辦了,關鍵是關閉後台進程續定在rsh連接上的標准輸入輸出。
復制代碼代碼如下:
# 如果遠端Shell是C Shell
$ rsh -n $host "$command >& /dev/null &"
復制代碼代碼如下:
# 如果遠端Shell是Bash Shell
$ rsh -n $host "$command > /dev/null 2>&1 &"
復制代碼代碼如下:
# 不確定遠端Shell的類型
$ rsh -n $host "sh -c '$command > /dev/null 2>&1 &'"
但上面這樣重定向的辦法有個缺點是不能得到任何遠端進程的輸出,而有時我們希望獲得一些輸出信息。
這時就需要遠端進程能夠以守護進程(daemon)的方式運行。
這種情況下,rsh命令可以簡單地寫作:$ rsh -n $host "$command &"
遠端後台進程的內容用Tcl表示,大意如下:
復制代碼代碼如下:
#/bin/env tclsh
puts "I am a background job"
puts "This Can Be Seen by Remote rsh Process"
close stdout
close stderr
# rsh連接到此應該結束。
puts "This Can NOT Be Seen by Remote rsh Process"
更進就步,我們可以甚至忽略rsh命令中的後台運行符:$ rsh -n $host "$command"
這時遠端進程需要通過fork的方式結束自己,並啟動真正的後台進程(守護進程)。
rsh進程的阻塞和超時處理
在程序中調用rsh $host $command時可能由於各種奇怪的原因發生rsh進程的阻塞,這不是我們希望看到的。
我們希望設置一個超時(timeout)機制來解決這個問題。
在Tcl程序中的一種實現可以這樣: TODO
TCP Connection連接數過多引起的rsh失敗
監控郵件顯示rsh $host $command命令失敗,錯誤提示為“poll: protocol failure in circuit setup"
懷疑是網絡連接數過多所引起。
rsh $host $command 的網絡連接過程
命令rsh $host連接遠程主機的513端口。
命令rsh $host $command則連接遠程主機的514端口,並隨後發送一個本地端口號給遠程主機,要求遠程主機建立一個新的TCP連接到這個端口(還不清楚這個新的連接有什麼作用)。然後才是傳送命令和等待命令結束。
這樣做的結果就是在rsh $host $command進程過多時,本地開放的端口資源被消耗完了,從而導致新的rsh $host $command失敗。
這是rsh $host的使用則依然正常。
這裡提到的rsh的缺陷,也是建議盡量使用ssh的原因之一。
沒有完全關閉的網絡連接:
在遠程主機上kill掉相關的rsh進程後,會導致TCP連接沒有完全關閉。
netstat命令顯示CLOSE_WAIT狀態,端口資源並沒有釋放出來。
根據配置文件/proc/sys/net/ipv4/tcp_keepalive_time顯示,需要等待2個小時,那些端口才會因為超時而被真正關閉,從而釋放出來。
復制代碼代碼如下:
%> netstat -a | grep localhost
tcp 0 0 localhost:933 localhost:935 CLOSE_WAIT
%> cat /proc/sys/net/ipv4/tcp_keepalive_time
7200 ;# in seconds. = 2 hours
%> echo "net.ipv4.tcp_keepalive_time = 120" >> /etc/sysctl.con