Shell多進程執行任務

展現代碼

#!/bin/bash

trap "exec 1000>&-;exec 1000<&-;exit 0" 2

# 分別爲 建立管道文件,文件操做符綁定,刪除管道文件
mkfifo testfifo
exec 1000<>testfifo
rm -rf testfifo

# 對文件操做符進行寫入操做。 
# 經過一個for循環寫入10個空行,這個10就是咱們要定義的後臺線程數量。
for ((n=1; n<=10; n++))
do
    echo >&1000
done

# 建立一個備份目錄
if [[ ! -d back ]]; then
    mkdir back
fi

# 開始時間記錄
start=`date "+%s"`
# 獲取URL總數,若是總數爲0,直接退出
total=`cat urls | wc -l`
if [[ $total == 0 ]]; then
    echo 'urls總數爲空'
    exit 0
fi 

# 遍歷URLS文件,開始執行下載
for ((i=1;i<=$total;i++))
do {
    # 從testfifo中讀取一行
    read -u1000
    {    
        # 增長嘗試次數,最大5次
        for j in {1..5}
        do 
            # 判斷單獨進程文件目錄是否存在,不存在則建立目錄
            download_dir=audio"$i"
            if [[ ! -d $download_dir ]]; then
                mkdir -p $download_dir
            fi
            echo "往目錄${download_dir}中開始下載文件,嘗試次數:${j}"
            # 讀取URLS中的一行,下載文件
            you-get -o $download_dir `sed -n "$i"p urls | tr -d '\r'`

            # 校驗是否有異常,若是沒有異常,則跳出循環,執行外下一條,若是有異常,再次嘗試下載
            if [[ $? != 0 ]]; then
                mv $download_dir/* back
                rm -rf $download_dir
            else
                break
            fi
        done
        # 向文件操做符中寫入一個空行
        echo >&1000
    }&
}
done

# 等待全部任務完成
wait
end=`date "+%s"`
echo "time: `expr $end - $start`"

exec 1000>&-
exec 1000<&-

所謂多進程,就是將一個任務劃分紅多個子任務放在後臺執行。"FIFO"是一種特殊的文件類型,它容許獨立的進程通信. 一個進程打開FIFO文件進行寫操做,而另外一個進程對之進行讀操做, 而後數據即可以如同在shell或者其它地方常見的的匿名管道同樣流線執行。默認狀況下,建立的FIFO的模式爲0666('a+rw')減去umask中設置的位。html

串行、並行

串行任務

爲了比較並行和串行的區別,咱們先寫個串行的腳本:linux

#!/bin/bash

start=`date "+%s"`
for i in {1..10}
do
    echo $i;
    sleep 2
done
end=`date "+%s"`
echo "Time: `expr $end - $start`"

執行結果以下:shell

$ sh series.sh 
1
2
3
4
5
6
7
8
9
10
Time: 21

從結果來開,執行完上面10次任務,每次任務2秒,總耗時21秒,符合代碼的邏輯。bash

並行任務

先將上面的串行任務改爲多線程並行任務。微信

#!/bin/bash

start=`date "+%s"`
for i in {1..10}
do 
    {
        echo $i;
        sleep 2
    }&
done
wait
end=`date "+%s"`
echo "Time: `expr $end - $start`"

執行上面腳本的結果以下:多線程

$ sh paralle.sh 
1
2
3
4
5
6
7
8
9
10
Time: 2

一般,用{}將不佔處理器卻很耗時的任務放包裝一個塊,經過&放置在後臺運行,以達到節約時間的效果。上面並行代碼,咱們把10次任務所有放在後臺執行,每一個人物耗時2秒,因爲並行執行,總耗時也就是Max(任務耗時)=2秒。測試

{
    echo $i;
    sleep 2
}&

在小任務跟前,這種方式運用起來駕輕就熟,可是在任務量過大的時候,這種方式的缺點也就顯而易見了:沒法控制運行在後臺的進程數,不能就10萬個任務就是跑10萬個進程吧。爲了控制進程,咱們引入了管道文件操做符網站

管道、文件操做符

管道

管道就像水管,有流入纔會有流出,水管數水流的通道,管道是數據的通道。管道分爲無名管道和有名管道。url

管道類別 命令 栗子
無名管道 經常使用的` `就是管道,只不過是無名的,能夠直接做爲兩個進程的數據通道 `echo "hello world, I'm a test" grep "test"`
有名管道 mkfilo 能夠建立一個管道文件 mkfiflo testfifo

管道有一個特色,若是管道中沒有數據,那麼取管道數據的操做就會阻塞,直到管道內進入數據,而後讀出後纔會終止這一操做,同理,寫入管道的操做若是沒有讀取操做,這一個動做也會阻塞。spa

管道符寫入阻塞

當經過echo命令往fifotest管道中寫入數據時,因爲沒有任何其餘消費進程對管道操做,因此,該管道阻塞,直到再打開一個窗口且經過cat操做該管道。
管道符寫入讀取

同理,先操做讀取管道也會出現阻塞的狀況。
管道符讀取阻塞

經過以上實驗,看以看到,僅僅一個管道文件彷佛很難實現控制後臺線程數,所以咱們接下來簡單介紹 文件操做符

文件操做符

系統運行起始,就相應設備自動綁定到了 三個文件操做符 分別爲012 對應 stdinstdoutstderr 。在 /proc/self/fd 或者/dev/fd中能夠看到這三個對應文件:

linux fd

輸出到這三個文件的內容都會顯示出來。只是由於顯示器做爲最經常使用的輸出設備而被綁定。

在Linux中,能夠經過exec指令自行定義、綁定文件操做符,文件操做符通常從3~(n-1)均可以隨便使用,此處的nulimit -n的定義值。

ulimit -n

從上圖能夠看出本機的n值爲8192 ,因此文件操做符只能使用0-8192 ,可自行定義的就只能是3-8192

雖然exec和source都是在父進程中直接執行,但exec這個與source有很大的區別,source是執行shell腳本,並且執行後會返回之前的shell。而exec的執行不會返回之前的shell了,而是直接把之前登錄shell做爲一個程序看待,在其上經行復制。

exec可參考此文:《linux 下的 mkfifo、exec 命令使用

代碼分析

第3行:

  • 接受信號 2 (ctrl +C)作的操做。
  • 咱們生成文件描述符並作綁定時,能夠用exec 1000<>testfifo來實現,但關閉時必須分開來寫。
  • >讀的綁定,< 標識寫的綁定 <> 則標識對文件描述符1000的全部操做,其等同於對管道文件testfifo的操做。

第6-8行:  

  • 分別爲 建立管道文件文件操做符綁定刪除管道文件
  • 可能會有疑問,爲何不能直接使用管道文件呢?事實上,這並不是畫蛇添足,剛纔已經說明了管道文件的一個重要特性了,那就是讀寫必須同時存在,缺乏某一種操做,另外一種操做就是阻塞,而綁定文件操做符正好解決了這個問題。

第12-15行:

  • 對文件操做符進行寫入操做。 經過一個 for 循環寫入 10 個空行,這個 10 就是咱們要定義的後臺線程數量
  • 爲何寫入空行而不是 10 個字符呢?這是由於,管道文件的讀取是以爲單位的。
  • 當咱們試圖用 read 讀取管道中的一個字符時,結果是不成功的,上面的例子已經證明了使用cat是能夠讀取的。

第32-61行:

  • 遍歷urls的總行數,循環處理url
  • 25-29行是讀取urls文件的總行數的邏輯(看開篇代碼)。
  • 這裏咱們有$total個任務($total是變量,是讀取的urls的總行數,值大於0),咱們須要保證後臺只有10個進程在同步運行(固然這段代碼有點小遺憾,就是未能根據總行數決定用多少個進程,加入總行數小於10,但咱們建立了10行空字符串,但這並不影響咱們的測試) 。
  • read -u1000 的做用是:讀取一次管道中的一行,在這兒就是讀取一個空行。
  • 減小操做附中的一個空行以後,執行一次任務(固然是放到後臺執行),須要注意的是,這個任務在後臺執行結束之後會向文件操做符中寫入一個空行,這就是重點所在,若是咱們不在某種狀況某種時刻向操做符中寫入空行,那麼結果就是:在後臺放入10個任務以後,因爲操做符中沒有可讀取的空行,致使read -u1000這兒始終停頓。
  • 第38-56行,處理本身的業務,這裏面是經過you-get下載url中的圖片、語音,若是下載失敗,最多嘗試5次。關於you-get參考這篇文章《You-Get:支持 80 多個網站的命令行多媒體下載器》瞭解其更多。

第64-69行:

  • 等待全部進程執行結束。
  • exec 1000>&-exec 1000<&-是關閉 fd1000

該文首發《虛懷若谷》我的博客,轉載前請務必署名,轉載請標明出處。

古之善爲道者,微妙玄通,深不可識。夫惟不可識,故強爲之容:

豫兮若冬涉川,猶兮若畏四鄰,儼兮其若客,渙兮若冰之釋,敦兮其若樸,曠兮其若谷,混兮其若濁。

孰能濁以靜之徐清?孰能安以動之徐生?

保此道不欲盈。夫惟不盈,故能敝而新成。

請關注個人微信公衆號:下雨就像彈鋼琴,Thanks♪(・ω・)ノ
微信二維碼

相關文章
相關標籤/搜索