shell命名管道FIFO

在shell腳本中,咱們想要實現多進程高併發,最簡單的方法是把命令丟到後臺去,若是量不大的話,沒問題。 可是若是有幾百個進程同一時間丟到後臺去就很恐怖了,對於服務器資源的消耗很是大,甚至致使宕機。
web


那有沒有好的解決方案呢? 固然有!shell


咱們先來學習下面的常識。
數據庫


1 文件描述符bash


文件描述符(縮寫fd)在形式上是一個非負整數。實際上,它是一個索引值,指向內核爲每個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者建立一個新文件時,內核向進程返回一個文件描述符。每個unix進程,都會擁有三個標準的文件描述符,來對應三種不一樣的流:服務器


640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=


除了上面三個標準的描述符外,咱們還能夠在進程中去自定義其餘的數字做爲文件描述符,後面例子中會出現自定義數字。每個文件描述符會對應一個打開文件,同時,不一樣的文件描述符也能夠對應同一個打開文件;同一個文件能夠被不一樣的進程打開,也能夠被同一個進程屢次打開。併發


咱們能夠寫一個測試腳本/tmp/test.sh,內容以下:
less


#!/bin/bash socket

echo "該進程的pid爲$$"ide

exec 1>/tmp/test.log 2>&1函數

ls -l /proc/$$/fd/


執行該腳本 sh /tmp/test.sh,而後查看/tmp/test.log內容爲:


總用量 0

lrwx------ 1 root root 64 11月 22 10:26 0 -> /dev/pts/3

l-wx------ 1 root root 64 11月 22 10:26 1 -> /tmp/test.log

l-wx------ 1 root root 64 11月 22 10:26 2 -> /tmp/test.log

lr-x------ 1 root root 64 11月 22 10:26 255 -> /tmp/test.sh

lrwx------ 1 root root 64 11月 22 10:26 3 -> socket:[196912101]


其中0爲標準輸入,也就是當前終端pts/3,1和2所有指向到了/tmp/test.log,另外兩個數字,我們暫時不關注。


2 命名管道


咱們以前接觸過的管道「1」,其實叫作匿名管道,它左邊的輸出做爲右邊命令的輸入。這個匿名管道只能爲兩邊的命令提供服務,它是沒法讓其餘進程鏈接的。


640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=


實際上,這兩個進程(cat和less)並不知道管道的存在,它們只是從標準文件描述符中讀取數據和寫入數據。


另一種管道叫作命名管道,英文(First In First Out,簡稱FIFO)。


FIFO本質上和匿名管道的功能同樣,只不過它有一些特色:


1)在文件系統中,FIFO擁有名稱,而且是以設備特俗文件的形式存在的;

2)任何進程均可以經過FIFO共享數據;

3)除非FIFO兩端同時有讀與寫的進程,不然FIFO的數據流通將會阻塞;

4)匿名管道是由shell自動建立的,存在於內核中;而FIFO則是由程序建立的(好比mkfifo命令),存在於文件系統中;

5)匿名管道是單向的字節流,而FIFO則是雙向的字節流;


有了上面的基礎知識儲備後,下面咱們來用FIFO來實現shell的多進程併發控制。


需求背景:


領導要求小明備份數據庫服務器裏面的100個庫(數據量在幾十到幾百G),須要以最快的時間完成(5小時內),而且不能影響服務器性能。


需求分析:


因爲數據量比較大,單個庫備份時間少則10幾分鐘,多則幾個小時,咱們算平均每一個庫30分鐘,若一個庫一個庫的去備份,則須要3000分鐘,至關於50個小時。很明顯不可取。但所有丟到後臺去備份,100個併發,數據庫服務器也沒法承受。因此,須要寫一個腳本,可以控制併發數就能夠實現了。


控制併發的shell腳本示例:


#!/bin/sh

function a_sub {

    sleep 2;

    endtime=`date +%s` 

    sumtime=$[$endtime-$starttime]

    echo "我是$i,運行了2秒,整個腳本已經執行了$sumtime秒"

}


starttime=`date +%s`

export starttime

##其中$$爲該進程的pid

tmp_fifofile="/tmp/$$.fifo"

##建立命名管道

mkfifo $tmp_fifofile

##把文件描述符6和FIFO進行綁定

exec 6<>$tmp_fifofile

##綁定後,該文件就能夠刪除了

rm -f $tmp_fifofile


##併發量爲3,用這個數字來控制併發數

thread=3

for ((i=0;i<$thread;i++));

do

    ##寫一個空行到管道里,由於管道文件的讀取以行爲單位

    echo >&6

done


##循環10次,至關於要備份100個庫

for ((i=0;i<10;i++))

do

    ##讀取管道中的一行,每次讀取後,管道都會少一行

    read -u6

    {

        a_sub || {echo "a_sub is failed"}

        ##每次執行完a_sub函數後,再增長一個空行,這樣下面的進程才能夠繼續執行

        echo >&6

    } & ##這裏要放入後臺去,不然併發實現不了

done


##這裏的wait意思是,須要等待以上全部操做(包括後臺的進程)都結束後,再往下執行。

wait


##關閉文件描述符6的寫

exec 6>&-

相關文章
相關標籤/搜索