shell腳本多進程
A. CURL在shell下可以用多線程么
這是多進程吧,連多線程都算不上
php的多並發curl請求(curl_multi_系列函數),我記得沒有什麼限制吧,能貼一下代碼上來嗎?
shell下執行多個curl當然是多進程。甚至於像網路螞蟻那樣分塊HTTP下載單個文件都未必是多線程,因為每個HTTP請求到頭來還是單獨的。
追究到底是用多進程還是多線程來做並發,無關緊要。但可以肯定的是:無論是用shell還是用php,同時啟動多個/usr/bin/curl進程肯定是沒問題的。
樓主應該追究的重點,我估計在於如何多個HTTP請求同時發到伺服器上。由於連接伺服器的時間前後有所不同,所以如果不認真處理,並發的時間一致性恐怕不好。
這個的解決方式是把連接請求一分為二,把創建連接和發送HTTP請求割裂開來操作。做法是讓所有的並發端先連接到伺服器,待連接全部就緒之後,再一起發送HTTP請求。
這個我查了一下,似乎不是curl能做得到的,需要寫程序。
B. shell flock 何時釋放
PHP+shell實現多線程的方法 先寫個簡單的php代碼,這里為了讓腳本執行時間更長,方便看效果,sleep一下,呵呵!先看下test.php的代碼:ls PHP代碼: ?php for ($i=0;$i10;$i++) { echo $i; sleep(10); } ? 在看下shell腳本的代碼,非常簡單 #!/bin/bash for i in 1 2 3 4 5 6 7 8 9 10 do /usr/bin/php -q /var/www/html/test.php done 注意到在請求php代碼的那行有一個符號嗎,這個是關鍵,不加的話是不能進行多線程的,表示講服務推送到後台執行,因此,在 shell的每次的循環中不必等php的代碼全部執行完在請求下一個文件,而是同時進行的,這樣就實現了多線程,下面運行下shell看下效果,這里你將 看到10個test.php進程再跑,再利用linux的定時器,定時請求這個shell,在處理一些需要多線程的任務,例如,批量下載時,非常好用! php中用WEB伺服器實現多線程 假設我們現在運行的是a.php這個文件. 但是我在程序中又請求WEB伺服器運行另一個b.php,那麼這兩個文件將是同時執行的.(PS: 一個鏈接請求發送之後, WEB伺服器就會執行它, 而不管客戶端是否已經退出) 有些時候, 我們想運行的不是另一個文件, 而是本文件中的一部分代碼.該怎麼辦呢? 其實可是通過參數來控制a.php來運行哪一段程序. //a.php,b.php PHP代碼:-------------------------------------------------------------------------------- ?php function runThread() { $fp = fsockopen('localhost', 80, $errno, $errmsg); fputs($fp, GET /b.php?act=brnrn); //這里的第二個參數是HTTP協議中規定的請求頭 //不明白的請看RFC中的定義 fclose($fp); } function a() { $fp = fopen('result_a.log', 'w'); fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . rn); fclose($fp); } function b() { $fp = fopen('result_b.log', 'w'); fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . rn); fclose($fp); } if(!isset($_GET['act'])) $_GET['act'] = 'a'; if($_GET['act'] == 'a') { runThread(); a(); } else if($_GET['act'] == 'b') b(); ? -------------------------------------------------------------------------------- 打開result_a.log 和 result_b.log 比較一下兩個文件的中訪問的時間. 大家會發現, 這兩個的確是在不同線程中運行的.有些時間完全一樣. 上面只是一個簡單的例子, 大家可以改進成其它形式. 既然PHP中也能多線程了, 那麼問題也來了, 那就是同步的問題. 我們知道 PHP本身是不支持多線程的. 所以更不會有什麼像Java 中synchronize的方法了. 那我們該如何做呢. 1. 盡量不訪問同一個資源. 以避免沖突. 但是可以同時像資料庫操作. 因為資料庫是支持並發操作的. 所以在多線程的PHP中不要向同一個文件中寫入數據. 如果必須要寫的話, 用別的方法進行同步.. 如調用 flock對文件進行加鎖等. 或建立臨時文件並在另外的線程中等待這個文件的消失 while(file_exits('xxx')); 這樣就等於這個臨時文件存在時, 表示其實線程正在操作 如果沒有了這個文件, 說明其它線程已經釋放了這個. 2. 盡量不要從runThread在執行fputs後取這個socket中讀取數據. 因為要實現多線程, 需要的用非阻塞模式. 即在像fgets這樣的函數時立即返回.. 所以讀寫數據就會出問題. 如果使用阻塞模式的話, 程序就不算是多線程了. 他要等上面的返回才執行下面的程序. 所以如果需要交換數據最後利用外面文件或數據中完成. 實在想要的話就用socket_set_nonblock($fp) 來實現. 說了這么多, 倒底這個有沒有實際的意義呢? 在什麼時候需要這種用這種方法呢 ? 答案是肯定的. 大家知道. 在一個不斷讀取網路資源的應用中, 網路的速度是瓶頸. 如果采多這種形式就可以同時以多個線程對不同的頁面進行讀取. 本人做的一個能從8848、soaso這些商城網站搜索信息的程序。還有一個從阿里巴巴網站上讀取商業信息和公司目錄的程序也用到了此技術。 因為這兩個程序都是要不斷的鏈接它們的伺服器讀取信息並保存到資料庫。 利用此技術正好消除了在等待響應時的瓶頸。 php模擬實現多線程的三種方法 PHP語言本身是不支持多線程的. 總結了一下網上關於PHP模擬多線程的方法, 總的來說, 都是利用了PHP的好夥伴們本身所具有的多線程能力. PHP的好夥伴指的就是LINUX和APACHE啦, LAMP嘛. 另外, 既然是模擬的, 就不是真正的多線程. 其實只是多進程. 進程和線程是兩個不同的概念. 好了, 以下方法都是從網上找來的. 1. 利用LINUX操作系統 ?php for ($i=0;$i10;$i++) { echo $i; sleep(5); } ? 上面存成test.php, 然後寫一段SHELL代碼 #!/bin/bash for i in 1 2 3 4 5 6 7 8 9 10 do php -q test.php done 2. 利用fork子進程(其實同樣是利用LINUX操作系統) ?php declare(ticks=1); $bWaitFlag = FALSE; /// 是否等待進程結束 $intNum = 10; /// 進程總數 $pids = array(); /// 進程PID數組 echo (Startn); for($i = 0; $i $intNum; $i++) { $pids[$i] = pcntl_fork();/// 產生子進程,而且從當前行之下開試運行代碼,而且不繼承父進程的數據信息 if(!$pids[$i]) { // 子進程進程代碼段_Start $str=; sleep(5+$i); for ($j=0;$j$i;$j++) {$str.=*;} echo $i - . time() . $str n; exit();
C. 如何在一個shell腳本里執行多個任務
你是想多進程的同時執行這些任務嗎?
你試一下這樣能不能達到你想要的效果
#!/bin/bash
{
DATE1=$(date -d "1 day ago" +"%Y%m%d") c=${DATE1} echo $c DATE1=$(date -d "1 day ago" +"%Y%m%d") d=${DATE1} echo $d
}&
就是直接把命令直接放在{}&裡面,注意最後的那個符號
D. 剛開機任務管理器里就有好多進程。解決追加5分
很正常啊 一般會有21個左右進程
大約佔了CPU的1%-3%
taskmgr.exe : Windows任務管理器。按 CTRL+ALT+ DEL或CTRL+SHIFT+ESC 打開。查看現在是運行在系統上的程序。
wnwb.exe :萬能五筆輸入法。
Exploter.EXE :Windows Explorer用於控制Windows圖形Shell,包括開始菜單、任務欄,桌面和文件管理。這是一個用戶的shell,在我們看起來就像任務條,桌面等等。或者說它就是資源管理器,不相信你在運行里執行它看看。它對Windows系統的穩定性還是比較重要的,而紅碼也就是找它的麻煩,在c和d根下創建explorer.exe。
System Idle Process :Windows頁面內存管理進程,該進程擁有0級優先。它作為單線程運行在每個處理器上,並在系統不處理其他線程的時候分派處理器的時間。它的cpu佔用率越大表示可供分配的CPU資源越多,數字越小則表示CPU資源緊張。
E. 如何使用gdb調試多進程
GDB 是 linux 系統上常用的 c/c++ 調試工具,功能十分強大。對於較為復雜的系統,比如多進程系統,如何使用 GDB 調試呢?考慮下面這個三進程系統:
進程
進程
Proc2 是 Proc1 的子進程,Proc3 又是 Proc2 的子進程。如何使用 GDB 調試 proc2 或者 proc3 呢?
實際上,GDB 沒有對多進程程序調試提供直接支持。例如,使用GDB調試某個進程,如果該進程fork了子進程,GDB會繼續調試該進程,子進程會不受干擾地運行下去。如果你事先在子進程代碼里設定了斷點,子進程會收到SIGTRAP信號並終止。那麼該如何調試子進程呢?其實我們可以利用GDB的特點或者其他一些輔助手段來達到目的。此外,GDB 也在較新內核上加入一些多進程調試支持。
接下來我們詳細介紹幾種方法,分別是 follow-fork-mode 方法,attach 子進程方法和 GDB wrapper 方法。
follow-fork-mode
在2.5.60版Linux內核及以後,GDB對使用fork/vfork創建子進程的程序提供了follow-fork-mode選項來支持多進程調試。
follow-fork-mode的用法為:
set follow-fork-mode [parent|child]
parent: fork之後繼續調試父進程,子進程不受影響。
child: fork之後調試子進程,父進程不受影響。
因此如果需要調試子進程,在啟動gdb後:
(gdb) set follow-fork-mode child
並在子進程代碼設置斷點。
此外還有detach-on-fork參數,指示GDB在fork之後是否斷開(detach)某個進程的調試,或者都交由GDB控制:
set detach-on-fork [on|off]
on: 斷開調試follow-fork-mode指定的進程。
off: gdb將控制父進程和子進程。follow-fork-mode指定的進程將被調試,另一個進程置於暫停(suspended)狀態。
注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。
follow-fork-mode/detach-on-fork的使用還是比較簡單的,但由於其系統內核/gdb版本限制,我們只能在符合要求的系統上才能使用。而且,由於follow-fork-mode的調試必然是從父進程開始的,對於fork多次,以至於出現孫進程或曾孫進程的系統,例如上圖3進程系統,調試起來並不方便。
Attach子進程
眾所周知,GDB有附著(attach)到正在運行的進程的功能,即attach <pid>命令。因此我們可以利用該命令attach到子進程然後進行調試。
例如我們要調試某個進程RIM_Oracle_Agent.9i,首先得到該進程的pid
[root@tivf09 tianq]# ps -ef|grep RIM_Oracle_Agent.9i
nobody 6722 6721 0 05:57 ? 00:00:00 RIM_Oracle_Agent.9i
root 7541 27816 0 06:10 pts/3 00:00:00 grep -i rim_oracle_agent.9i
通過pstree可以看到,這是一個三進程系統,oserv是RIM_Oracle_prog的父進程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父進程。
[root@tivf09 root]# pstree -H 6722
通過 pstree 察看進程
通過 pstree 察看進程
啟動GDB,attach到該進程
用 GDB 連接進程
用 GDB 連接進程
現在就可以調試了。一個新的問題是,子進程一直在運行,attach上去後都不知道運行到哪裡了。有沒有辦法解決呢?
一個辦法是,在要調試的子進程初始代碼中,比如main函數開始處,加入一段特殊代碼,使子進程在某個條件成立時便循環睡眠等待,attach到進程後在該代碼段後設上斷點,再把成立的條件取消,使代碼可以繼續執行下去。
至於這段代碼所採用的條件,看你的偏好了。比如我們可以檢查一個指定的環境變數的值,或者檢查一個特定的文件存不存在。以文件為例,其形式可以如下:
void debug_wait(char *tag_file)
{
while(1)
{
if (tag_file存在)
睡眠一段時間;
else
break;
}
}
當attach到進程後,在該段代碼之後設上斷點,再把該文件刪除就OK了。當然你也可以採用其他的條件或形式,只要這個條件可以設置/檢測即可。
Attach進程方法還是很方便的,它能夠應付各種各樣復雜的進程系統,比如孫子/曾孫進程,比如守護進程(daemon process),唯一需要的就是加入一小段代碼。
GDB wrapper
很多時候,父進程 fork 出子進程,子進程會緊接著調用 exec族函數來執行新的代碼。對於這種情況,我們也可以使用gdb wrapper 方法。它的優點是不用添加額外代碼。
其基本原理是以gdb調用待執行代碼作為一個新的整體來被exec函數執行,使得待執行代碼始終處於gdb的控制中,這樣我們自然能夠調試該子進程代碼。
還是上面那個例子,RIM_Oracle_prog fork出子進程後將緊接著執行RIM_Oracle_Agent.9i的二進制代碼文件。我們將該文件重命名為RIM_Oracle_Agent.9i.binary,並新建一個名為RIM_Oracle_Agent.9i的shell腳本文件,其內容如下:
[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary
[root@tivf09 bin]# cat RIM_Oracle_Agent.9i
#!/bin/sh
gdb RIM_Oracle_Agent.binary
當fork的子進程執行名為RIM_Oracle_Agent.9i的文件時,gdb會被首先啟動,使得要調試的代碼處於gdb控制之下。
新的問題來了。子進程是在gdb的控制下了,但還是不能調試:如何與gdb交互呢?我們必須以某種方式啟動gdb,以便能在某個窗口/終端與gdb交互。具體來說,可以使用xterm生成這個窗口。
xterm是X window系統下的模擬終端程序。比如我們在Linux桌面環境GNOME中敲入xterm命令:
xterm
xterm
就會跳出一個終端窗口:
終端
終端
如果你是在一台遠程linux伺服器上調試,那麼可以使用VNC(Virtual Network Computing) viewer從本地機器連接到伺服器上使用xterm。在此之前,需要在你的本地機器上安裝VNC viewer,在伺服器上安裝並啟動VNC server。大多數linux發行版都預裝了vnc-server軟體包,所以我們可以直接運行vncserver命令。注意,第一次運行vncserver時會提示輸入密碼,用作VNC viewer從客戶端連接時的密碼。可以在VNC server機器上使用vncpasswd命令修改密碼。
[root@tivf09 root]# vncserver
New 'tivf09:1 (root)' desktop is tivf09:1
Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/tivf09:1.log
[root@tivf09 root]#
[root@tivf09 root]# ps -ef|grep -i vnc
root 19609 1 0 Jun05 ? 00:08:46 Xvnc :1 -desktop tivf09:1 (root)
-httpd /usr/share/vnc/classes -auth /root/.Xauthority -geometry 1024x768
-depth 16 -rfbwait 30000 -rfbauth /root/.vnc/passwd -rfbport 5901 -pn
root 19627 1 0 Jun05 ? 00:00:00 vncconfig -iconic
root 12714 10599 0 01:23 pts/0 00:00:00 grep -i vnc
[root@tivf09 root]#
Vncserver是一個Perl腳本,用來啟動Xvnc(X VNC server)。X client應用,比如xterm,VNC viewer都是和它通信的。如上所示,我們可以使用的DISPLAY值為tivf09:1。現在就可以從本地機器使用VNC viewer連接過去:
VNC viewer:輸入伺服器
VNC viewer:輸入伺服器
輸入密碼:
VNC viewer:輸入密碼
VNC viewer:輸入密碼
登錄成功,界面和伺服器本地桌面上一樣:
VNC viewer
VNC viewer
下面我們來修改RIM_Oracle_Agent.9i腳本,使它看起來像下面這樣:
#!/bin/sh
export DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary
如果你的程序在exec的時候還傳入了參數,可以改成:
#!/bin/sh
export DISPLAY=tivf09:1.0; xterm -e gdb --args RIM_Oracle_Agent.binary $@
最後加上執行許可權
[root@tivf09 bin]# chmod 755 RIM_Oracle_Agent.9i
現在就可以調試了。運行啟動子進程的程序:
[root@tivf09 root]# wrimtest -l 9i_linux
Resource Type : RIM
Resource Label : 9i_linux
Host Name : tivf09
User Name : mdstatus
Vendor : Oracle
Database : rim
Database Home : /data/oracle9i/920
Server ID : rim
Instance Home :
Instance Name :
Opening Regular Session...
程序停住了。從VNC viewer中可以看到,一個新的gdb xterm窗口在伺服器端打開了
gdb xterm 窗口
gdb xterm窗口
[root@tivf09 root]# ps -ef|grep gdb
nobody 24312 24311 0 04:30 ? 00:00:00 xterm -e gdb RIM_Oracle_Agent.binary
nobody 24314 24312 0 04:30 pts/2 00:00:00 gdb RIM_Oracle_Agent.binary
root 24326 10599 0 04:30 pts/0 00:00:00 grep gdb
運行的正是要調試的程序。設置好斷點,開始調試吧!
注意,下面的錯誤一般是許可權的問題,使用 xhost 命令來修改許可權:
xterm 錯誤
xterm 錯誤
[root@tivf09 bin]# export DISPLAY=tivf09:1.0
[root@tivf09 bin]# xhost +
access control disabled, clients can connect from any host
xhost + 禁止了訪問控制,從任何機器都可以連接過來。考慮到安全問題,你也可以使用xhost + <你的機器名>。
小結
上述三種方法各有特點和優劣,因此適應於不同的場合和環境:
follow-fork-mode方法:方便易用,對系統內核和GDB版本有限制,適合於較為簡單的多進程系統
attach子進程方法:靈活強大,但需要添加額外代碼,適合於各種復雜情況,特別是守護進程
GDB wrapper方法:專用於fork+exec模式,不用添加額外代碼,但需要X環境支持(xterm/VNC)。
F. 如何運行多進程Docker容器
一般來說,Docker容器比較適合運行單個進程。例如,項目"使用多個Docker容器運行Kubernetes",Kubernetes的各個組件分別運行在各個容器之中,每個容器只運行單個進程。
然而,很多時候我們需要在Docker容器中運行多個進程。例如,項目"使用單個Docker容器運行Kubernetes",kubernetes的各個組件均運行在同一個容器中,該容器中運行了多個進程。那麼,如何運行多進程Docker容器?
一種方法是使用Shell腳本,另一種方法是使用進程管理工具Supervisor。kiwenlau/kubernetes-shell和kiwenlau/kubernetes-supervisor分別採用了這兩種方法,用於啟動多個進程來運行Kubernetes的各個組件,從而實現"使用單個Docker容器運行Kubernetes"。下面我將分別介紹兩種不同方法。
使用Shell腳本運行多進程Docker容器
這個方法大家應該會比較熟悉,使用Shell腳本依次啟動Kubernetes的各個組件即可。以下為start-kubernetes.sh
!/bin/bashstart docker daemondocker daemon > /var/log/docker.log 2>&1 & start etcdetcd --data-dir=/var/etcd/data > /var/log/etcd.log 2>&1 & wait for ectd to setupsleep 5 start apiserverkube-apiserver --service-cluster-ip-range=10.0.0.1/24 --insecure-bind-address=0.0.0.0 --etcd_servers=http://127.0.0.1:4001 > /var/log/kube-apiserver.log 2>&1 & wait for apiserver to setupsleep 5 start controller manager, sheler, kubelet and proxykube-controller-manager --master=http://0.0.0.0:8080 > /var/log/kube-controller-manager.log 2>&1 & kube-scheler --master=http://0.0.0.0:8080 > /var/log/kube-scheler.log 2>&1 & kubelet --api_servers=http://0.0.0.0:8080 --address=0.0.0.0 --cluster_dns=10.0.0.10 --cluster_domain="kubernetes.local" --pod-infra-container-image="kiwenlau/pause:0.8.0" > /var/log/kubelet.log 2>&1 & kube-proxy --master=http://0.0.0.0:8080 > /var/log/kube-proxy.log 2>&1 & just keep this script runningwhile [[ true ]]; do sleep 1 done
然後在Dockerfile中,將start-kubernetes.sh指定為Docker容器默認執行的命令即可:
CMD ["start-kubernetes.sh"]
需要注意的一點在於,start-kubernetes.sh腳本將作為Docker容器的1號進程運行,必須始終保持運行。因為Docker容器僅在1號進程運行時保持運行,換言之,Docker容器將在1號進程退出後Exited。由於Kubernetes的各個組件都以後台進程方式執行,我在腳本末尾添加了死循環,以保持start-kubernetes.sh腳本始終處於運行狀態。
just keep this script runningwhile [[ true ]]; do sleep 1 done
使用supervisor運行多進程Docker容器
Supervisor是進程管理工具。這時,需要編寫supervisor的配置文件kubernetes.conf:
[supervisord] nodaemon=true [program:etcd] command=etcd --data-dir=/var/etcd/data autorestart=true stdout_logfile=/var/log/etcd.stdout.log stderr_logfile=/var/log/etcd.stderr.log [program:kube-apiserver] command=kube-apiserver --service-cluster-ip-range=10.0.0.1/24 --insecure-bind-address=0.0.0.0 --etcd_servers=http://127.0.0.1:4001 autorestart=true stdout_logfile=/var/log/kube-apiserver.stdout.log stderr_logfile=/var/log/kube-apiserver.stderr.log [program:kube-controller-manager] command=kube-controller-manager --master=http://0.0.0.0:8080 autorestart=true stdout_logfile=/var/log/controller-manager.stdout.log stderr_logfile=/var/log/controller-manager.stderr.log [program:kube-scheler] command=kube-scheler --master=http://0.0.0.0:8080 autorestart=true stdout_logfile=/var/log/kube-scheler.stdout.log stderr_logfile=/var/log/kube-scheler.stderr.log [program:kubelet] command=kubelet --api_servers=http://0.0.0.0:8080 --address=0.0.0.0 --cluster_dns=10.0.0.10 --cluster_domain="kubernetes.local" --pod-infra-container-image="kiwenlau/pause:0.8.0" autorestart=true stdout_logfile=/var/log/kubelet.stdout.log stderr_logfile=/var/log/kubelet.stderr.log [program:kube-proxy] command=kube-proxy --master=http://0.0.0.0:8080 autorestart=true stdout_logfile=/var/log/kube-proxy.stdout.log stderr_logfile=/var/log/kube-proxy.stderr.log [program:docker] command=docker daemon autorestart=true stdout_logfile=/var/log/docker.stdout.log stderr_logfile=/var/log/docker.stderr.log
可知,將Kubernetes的各個組件的啟動命令設為command即可。autorestart參數設為true,意味著supervisor將負責重啟意外退出的組件。stdout_logfile和stderr_logfile參數則可以用於設置命令的標准輸出文件和標准錯誤輸出文件。
然後在Dockerfile中,將supervisord指定為Docker容器默認執行的命令即可:
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/kubernetes.conf"]
此時, supervisord是Docker容器中的1號進程,也需要始終保持運行狀態。nodaemon設為true時,表示supervisor保持前台運行而非在後台運行。若supervisor在後台運行,則Docker容器也會在執行supervisord命令後立即Exited.
[supervisord] nodaemon=true
總結
使用Shell腳本運行多進程Docker容器,優勢是大家比較熟悉。由於需要保持Docker容器的1號進程始終運行,這一點比較容易出錯。若要實現進程意外退出後自動重啟的話,使用shell腳本比較麻煩。
使用supervisor運行多進程Docker容器,非常方便。另外,保持1號進程保持運行,以及進程意外退出後自動重啟,實現起來都很簡單。
使用多個Docker容器運行Kubernetes
GitHub地址
kiwenlau/single-kubernetes-docker
使用單個Docker容器運行Kubernetes
GitHub地址:
kiwenlau/kubernetes-shell kiwenlau/kubernetes-supervisor
該項目中,我將kubernetes的所有組件:etcd, controller manager, apiserver, scheler, kubelet, proxy以及docker daemon均運行在同一個Docker容器之中。
容器啟動時,各個組件由shell腳本或者supervisor啟動。
G. linux中,shell的功能和特點
用戶可以通過shell與系統打交道,特點是可實現腳本編程。與windows中的cmd窗口類似,比cmd強大。
H. VBA中使用SHELL為什麼打不開文件雙擊能打開
做了一個ACTIVEX.EXE或者是屏蔽BI導致的只能雙擊打開。shell命令是採用的多進程執行方法,即使shell命令還未執行完成,其下面的代碼就已經開始執行了。
I. 關於一個多線程shell腳本的函數調用問題
你這個是不行的 !
首先, shell 沒有多線程的說法.
其實, 你這個偽多進程.
然後, 每個 & 產生一個shell子進程,每個子進程都是獨立的.
再次, 子進程的變數父進程無法獲取到.
所以,你最後 $d , $n 的值都不會變,都是 0 .