linux shell多程序
1 bash後臺執行實現多程序
1.1 command & 後臺執行
釋放終端命令列,將command命令程式掛到後臺繼續執行,這樣使用者就不必等待該程式執行結束後才釋放終端命令列,可以一邊等待command後臺執行一邊做其他的事。但是,不可以關閉當前的終端,掛靠在後臺的command命令是依賴於當前終端的,當關閉當前終端之後,後臺的comman命令也會終止執行。
比如:使用sort指令對一個較大的檔案進行排序
$ sort log > log.sorted &
1.2 nohup command & 後臺執行
這種方式與之前的區別是,使用nohup來管理後臺執行的command程式。這樣可以是後臺的command程式完全脫離終端,當終端關閉時,後臺的command程式會在nohup管理下繼續執行。如果你要執行一個非常耗時的程式時,建議使用nohup來管理後臺程式。這是因為,當程式執行過程中,終端因為網速不穩定或者其他原因突然中斷時,會導致執行中的程式中斷。
使用方式:
$ nohup longtime.sh &
程式執行過程中的正常輸出以及錯誤輸出都被重定向到程式目錄下的nohup.out檔案中。也可以使用如下方式來指定重定向輸出的檔案:
$ nohup longtime.sh > myout 2>&1 &
1.3 後臺任務實現多程序
只需將需要多程序執行的程式塊全部使用command & 轉移到後臺執行即可。
#!/bin/bash
start=`date +"%s"`
for (( i=0; i<10; i++ ))
do
{
echo "multiprocess"
sleep 3
} & #將上述程式塊放到後臺執行
done
wait #等待上述程式結束
end=`date +"%s"`
echo "time: " `expr $end - $start`
使用上述方式,10次執行過程全部放到後臺執行。這種方式雖然可以實現程式的多程序併發執行,但是我們無法控制執行在後臺的程序數。為了實現程序數控制,需要使用管道和檔案描述符。
2 使用fifo管道管理多程序
2.1 管道
管道可用於程式之間的資料共享,但是需要注意的是,這種資料共享是單向的。管道分兩種:匿名管道和有名管道,這裡僅討論有名管道。
管道的特點:
1、需要有兩個程式分別連線管道的兩端,一個程式往管道里寫資料,另一個程式從管道里取資料;
2、只有寫資料的程式時,寫入操作會一直處理阻塞狀態,直到有另外的程式從管道中讀資料;同理當只有讀取的程式時,讀取操作也會一直處於阻塞狀態,直到有另外的程式往管道里寫資料
3、當管道中沒有資料時,取資料的程式會被阻塞,直到寫資料的程式寫資料到管道
有名管道被稱為fifo有如下特點:
1、fifo擁有名稱,並存在於檔案系統之中
2、除非fifo兩端同時存在讀和寫的程式,否則fifo的資料流將會阻塞
3、fifo由mkfifo這樣的命令建立,而匿名管道是由shell自動建立
4、fifo是一個雙向的位元組流,資料流沒有任何格式
2.2 使用fifo管理shell多程序
直接程式碼:
1 #!/bin/bash
2
3 if [ $# -ne 1 ]
4 then
5 echo Usage: split.sh 201610
6 exit 1
7 else
8 month=$1
9 fi
10 len=`echo $month | wc -L`
11 if [ $len != "6" ]
12 then
13 echo "err> The lenght of month is not valid ..."
14 fi
15
16
17 fp_out="/opt/log_combine/"
18 fp_tmp="/opt/log_combine/tmp/"
19 fp_in="/opt/tag_log/tag.log."
20
21 #獲取所有日誌檔案的檔名
22 fns=`ls $fp_in$month*.gz`
23 if ls $fns >> log.split4month
24 then
25 echo "split the log begin!"
26 else
27 echo "err> can't find the file $fns"
28 exit 1
29 fi
30
31 trap "exec 1000>&-;exec 1000<&-;exit 0" 2
32
33 tmpfifo=$$.fifo
34 mkfifo $tmpfifo
35 exec 1000<>$tmpfifo
36 rm -fr $tmpfifo
37
38 thread_num=`grep "processor" /proc/cpuinfo | sort -u | wc -l`
39
40 for (( i=0; i<$thread_num; i++ ))
41 do
42 echo >&1000
43 done
44
45 for fn in $fns
46 do
47 fn_tmp=${fn##*/}
48 read -u1000
49 {
50 echo `date` $fn "begin!"
51 zcat $fn | gawk -F"\001" 'NF==6{
52 if($1~/\./){
53 suf = substr($1, index($1, ".")-1, 1)
54 }else{
55 suf = substr($1, length($1))
56 }
57
58 if(suf~/[0-9a-f]/){
59 print $1"\001"$2"\001"$4"\001"$6 >> "'$fp_tmp$fn_tmp'_" suf "_split"
60 #}else{
61 # print $0
62 }
63 }'
64 echo `date` $fn "end!"
65 echo >&1000
66 } &
67 done
68 echo "done !!!!!!"
- 31行:trap命令,程式接收Ctrl+c終端訊號的處理
- exec 1000>&-:關閉檔案描述符1000的寫
- exec 1000<&-:關閉檔案描述符1000的讀
- 2:接收的是Ctrl+c訊號
- 33-36行:
- 33行:獲取當前程式的程序號
- 34行:mkfifo建立管道,管道名為當前程序的程序號
- 35行:exec命令,將檔案描述符1000與上述管道進行繫結,其中<是讀繫結,>是寫繫結,這裡可以使用<>將讀寫同時與管道進行繫結
- 36行:刪除管道檔案(不太明白為啥在這裡就刪除它)
- 特別說明:這裡為何要將管道檔案和檔案描述符進行繫結,直接使用管道檔案不可以嗎?答案肯定是不可以,以下內容是我自己的理解。要實現多程序控制,就必須使用兩個功能:第一,管道的特殊性質(阻塞功能);第二,通過對檔案描述符的read和echo操作來控制管道中的資料內容(這才是控制多程序數量的關鍵)。為了滿足上述功能,只能是將檔案描述符和fifo繫結,使檔案描述符同時具有檔案讀寫功能以及管道功能
- 38行:獲取當前系統的執行緒數N,以此為並行執行的程序數
- 40-42行:向檔案描述符1000中寫入N的空行,用來控制並行的程序數
- 45-67行:並行開啟N的程序處理日誌檔案
- 48行:要從檔案描述符1000中讀取一行,如果當前檔案描述符1000沒有任何內容,read命令就會被阻塞(由管道特點決定)
- 49-66行:需要並行處理的程式塊,其中66行最後的&表示將這個程式塊放到後臺執行
- 特別說明:最初管道中有N的空行,會有N個read命令順利執行,後續的程式快也可以繼續往下執行,但是後續的一個read命令因為管道中沒有任何內容全部被阻塞,當執行中的某個程式塊執行到65行時,會往管道中寫入一個空行,於是阻塞佇列中的read命令會從管道中讀取到一個空行,後續程式塊被放到後臺得到執行。這就是通過fifo對並行程式進行控制的關鍵。