您好,歡迎來到思海網絡,我們將竭誠為您提供優質的服務! 誠征網絡推廣 | 網站備案 | 幫助中心 | 軟件下載 | 購買流程 | 付款方式 | 聯系我們 [ 會員登錄/注冊 ]
促銷推廣
客服中心
業務咨詢
有事點擊這里…  531199185
有事點擊這里…  61352289
點擊這里給我發消息  81721488
有事點擊這里…  376585780
有事點擊這里…  872642803
有事點擊這里…  459248018
有事點擊這里…  61352288
有事點擊這里…  380791050
技術支持
有事點擊這里…  714236853
有事點擊這里…  719304487
有事點擊這里…  1208894568
有事點擊這里…  61352289
在線客服
有事點擊這里…  531199185
有事點擊這里…  61352288
有事點擊這里…  983054746
有事點擊這里…  893984210
當前位置:首頁 >> 技術文章 >> 文章瀏覽
技術文章

Linux Shell多進程并發以及并發數控制

添加時間:2019-5-30 22:43:50  添加: 思海網絡 
本文小編為大家詳細的講解shell多進程并發,在大部分用戶眼中,所謂的多進程 只不過是將多個任務放到后臺執行而已,下面一起來看看吧具體的內容吧。

1. 基礎知識準備

1.1. linux后臺進程

Unix是一個多任務系統,允許多用戶同時運行多個程序。shell的元字符&提供了在后臺運行不需要鍵盤輸入的程序的方法。輸入命令后,其后緊跟&字符,該命令就會被送往到linux后臺執行,而終端又可以繼續輸入下一個命令了。 
比如:

sh a.sh &
sh b.sh &
sh c.sh &
這三個命令就會被同時送往linux后臺執行,在這個程度上,認為這三個命令并發執行了。

1.2. linux文件描述符

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

文件描述符 名稱
0 Standard Input
1 Standard Output
2 Standard Error
每一個文件描述符會對應一個打開文件,同時,不同的文件描述符也可以對應同一個打開文件;同一個文件可以被不同的進程打開,也可以被同一個進程多次打開。

在/proc/PID/fd中,列舉了進程PID所擁有的文件描述符,例如


#!/bin/bash
source /etc/profile;
 
# $表示當前進程的PID
PID=$
 
# 查看當前進程的文件描述符指向
ll /proc/$PID/fd
echo "-------------------";echo
 
# 文件描述符1與文件tempfd1進行綁定
( [ -e ./tempfd1 ] || touch ./tempfd1 ) && exec 1<>./tempfd1
 
# 查看當前進程的文件描述符指向
ll /proc/$PID/fd
echo "-------------------";echo;

[[email protected] learn_linux]$ sh learn_redirect.sh 
total 0
lrwx------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 0 -> /dev/pts/0
lrwx------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 1 -> /dev/pts/0
lrwx------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 2 -> /dev/pts/0
lr-x------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 255 -> /home/ouyangyewei/workspace/learn_linux/learn_redirect.sh
-------------------
 
[[email protected] learn_linux]$ cat tempfd1 
total 0
lrwx------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 0 -> /dev/pts/0
lrwx------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 1 -> /home/ouyangyewei/workspace/learn_linux/tempfd1
lrwx------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 2 -> /dev/pts/0
lr-x------. 1 ouyangyewei ouyangyewei 64 Jan  4 22:17 255 -> /home/ouyangyewei/workspace/learn_linux/learn_redirect.sh
-------------------
上述的例子中第12行,將文件描述符1與文件tempfile進行了綁定,此后,文件描述符1指向了tempfile文件,標準輸出被重定向到了文件tempfile中。

1.3. linux管道
在Unix或類Unix操作系統中,管道是一個由標準輸入輸出鏈接起來的進程集合,因此,每一個進程的輸出將直接作為下一個進程的輸入,

linux管道包含兩種:

匿名管道命名管道
管道有一個特點,如果管道中沒有數據,那么取管道數據的操作就會滯留,直到管道內進入數據,然后讀出后才會終止這一操作;同理,寫入管道的操作如果沒有讀取管道的操作,這一動作就會滯留。

1.3.1. 匿名管道
在Unix或類Unix操作系統的命令行中,匿名管道使用ASCII中垂直線|作為匿名管道符,匿名管道的兩端是兩個普通的,匿名的,打開的文件描述符:一個只讀端和一個只寫端,這就讓其它進程無法連接到該匿名管道。

例如:


cat file | less
為了執行上面的指令,Shell創建了兩個進程來分別執行cat和less。

有一點值得注意的是兩個進程都連接到了管道上,這樣寫入進程cat就將其標準輸出(文件描述符為fd 1)連接到了管道的寫入端,讀取進程less就將其標準輸入(文件描述符為fd 0)連接到了管道的讀入端。實際上,這兩個進程并不知道管道的存在,它們只是從標準文件描述符中讀取數據和寫入數據。shell必須要完成相關的工作。 
1.3.2. 命名管道(FIFO,First In First Out)

命名管道也稱FIFO,從語義上來講,FIFO其實與匿名管道類似,但值得注意:

在文件系統中,FIFO擁有名稱,并且是以設備特俗文件的形式存在的;任何進程都可以通過FIFO共享數據;除非FIFO兩端同時有讀與寫的進程,否則FIFO的數據流通將會阻塞;匿名管道是由shell自動創建的,存在于內核中;而FIFO則是由程序創建的(比如mkfifo命令),存在于文件系統中;匿名管道是單向的字節流,而FIFO則是雙向的字節流;
比如,可以利用FIFO實現單服務器、多客戶端的應用程序: 



有了上面的知識準備,現在可以開始講述,linux多進程并發時,如何控制每次并發的進程數。

2. linux多進程并發數控制
最近小A需要生產2015年全年的KPI數據報表,現在小A已經將生產腳本寫好了,生產腳本一次只能生產指定一天的KPI數據,假設跑一次生產腳本需要5分鐘,那么: 
* 如果是循環順序執行,那么需要時間:5 * 365 = 1825 分鐘,約等于 6 天 
* 如果是一次性放到linux后臺并發執行,365個后臺任務,系統可承受不住哦!

既然不能一次性把365個任務放到linux后臺執行,那么,能不能實現自動地每次將N個任務放到后臺并發執行呢?當然是可以的啦。


#! /bin/bash
source /etc/profile;
 
# -----------------------------
 
tempfifo=$.fifo        # $表示當前執行文件的PID
begin_date=$1           # 開始時間
end_date=$2             # 結束時間
 
if [ $# -eq 2 ] 
then
    if [ "$begin_date" \> "$end_date" ]
    then
        echo "Error! $begin_date is greater than $end_date"
        exit 1;
    fi
else
    echo "Error! Not enough params."
    echo "Sample: sh loop_kpi 2015-12-01 2015-12-07"
    exit 2;
fi
 
# -----------------------------
 
trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo
 
for ((i=1; i<=8; i++))
do
    echo >&1000
done
 
while [ $begin_date != $end_date ]
do
    read -u1000
    {
        echo $begin_date
        hive -f kpi_report.sql --hivevar date=$begin_date
        echo >&1000
    } &
 
    begin_date=`date -d "+1 day $begin_date" +"%Y-%m-%d"`
done
 
wait
echo "done!!!!!!!!!!"
第6~22行:比如:sh loop_kpi_report.sh 2015-01-01 2015-12-01:

$1表示腳本入參的第一個參數,等于2015-01-01

$2表示腳本入參的第二個參數,等于2015-12-01

$#表示腳本入參的個數,等于2

第13行用于比較傳入的兩個日期的大小,\>是轉義

第26行:表示在腳本運行過程中,如果接收到Ctrl+C中斷命令,則關閉文件描述符1000的讀寫,并正常退出

exec 1000>&-;表示關閉文件描述符1000的寫

exec 1000<&-;表示關閉文件描述符1000的讀

trap是捕獲中斷命令

第27~29行:

第27行,創建一個管道文件

第28行,將文件描述符1000與FIFO進行綁定,<讀的綁定,>寫的綁定,<>則標識對文件描述符1000的所有操作等同于對管道文件$tempfifo的操作

第29行,可能會有這樣的疑問:為什么不直接使用管道文件呢?事實上這并非多此一舉,管道的一個重要特性,就是讀寫必須同時存在,缺失某一個操作,另一個操作就是滯留,而第28行的綁定文件描述符(讀、寫綁定)正好解決了這個問題

第31~34行:對文件描述符1000進行寫入操作。通過循環寫入8個空行,這個8就是我們要定義的后臺并發的線程數。為什么是寫空行而不是寫其它字符?因為管道文件的讀取,是以行為單位的

第37~42行:

第37行,read -u1000的作用就是讀取管道中的一行,在這里就是讀取一個空行;每次讀取管道就會減少一個空行

第39~41行,注意到第42行結尾的&嗎?它表示進程放到linux后臺中執行

第41行,執行完后臺任務之后,往文件描述符1000中寫入一個空行。這是關鍵所在了,由于read -u1000每次操作,都會導致管道減少一個空行,當linux后臺放入了8個任務之后,由于文件描述符1000沒有可讀取的空行,將導致read -u1000一直處于等待。


關鍵字:Linux、Shell、多進程、并發數控制
分享到:

頂部 】 【 關閉
版權所有:佛山思海電腦網絡有限公司 ©1998-2019 All Rights Reserved.
聯系電話:(0757)22630313、22633833
公司地址: 廣東省佛山市順德區大良國際商業城A區4座3樓106號   郵編:528300
機房7x24小時服務熱線:(0757)23628508、23628509
中華人民共和國增值電信業務經營許可證: 粵B1.B2-20030321 備案號:粵B2-20030321-1
網站公安備案編號:44060602000007 交互式欄目專項備案編號:200303DD003  
察察 工商 網安 舉報有獎  警警  手機打開網站
30选5今天开奖结果