Spark源碼分析之Spark Shell(下)


繼上次的Spark-shell腳本源碼分析,還剩下后面半段。由於上次涉及了不少shell的基本內容,因此就把trap和stty放在這篇來講述。

上篇回顧:Spark源碼分析之Spark Shell(上)

function main() {
  if $cygwin; then
    # Workaround for issue involving JLine and Cygwin
    # (see http://sourceforge.net/p/jline/bugs/40/).
    # If you're using the Mintty terminal emulator in Cygwin, may need to set the
    # "Backspace sends ^H" setting in "Keys" section of the Mintty options
    # (see https://github.com/sbt/sbt/issues/562).
    stty -icanon min 1 -echo > /dev/null 2>&1
    export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djline.terminal=unix"
    "${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
    stty icanon echo > /dev/null 2>&1
  else
    export SPARK_SUBMIT_OPTS
    "${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
  fi
}

# Copy restore-TTY-on-exit functions from Scala script so spark-shell exits properly even in
# binary distribution of Spark where Scala is not installed
exit_status=127
saved_stty=""

# restore stty settings (echo in particular)
function restoreSttySettings() {
  stty $saved_stty
  saved_stty=""
}

function onExit() {
  if [[ "$saved_stty" != "" ]]; then
    restoreSttySettings
  fi
  exit $exit_status
}

# to reenable echo if we are interrupted before completing.
trap onExit INT

# save terminal settings
saved_stty=$(stty -g 2>/dev/null)
# clear on error so we don't later try to restore them
if [[ ! $? ]]; then
  saved_stty=""
fi

main "$@"

# record the exit status lest it be overwritten:
# then reenable echo and propagate the code.
exit_status=$?
onExit

總結一下,上面的代碼大體上做了三件事:

  • 1 捕獲終端信號,執行退出方法,恢復一些操作
  • 2 保存終端配置,當cygwin時關閉回顯,之后再恢復
  • 3 執行spark-submit,調用repl.Main

下面我們就循序漸進學習下這半段腳本涉及的內容:

什么是trap

trap命令支持捕獲特定的信號,然后執行某個命令。常用的用法有:

trap "commands" signal-list 捕獲到特定的信號,執行commands命令
trap signal-list 捕獲特定的信號,停止當前進程
trap " " signal-list 捕獲特定的信號,什么也不做

支持的信號,可以利用kill- l查詢到:

[root@localnode3 test]# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
SIGHUP     終止進程     終端線路掛斷
SIGINT    終止進程     中斷進程
SIGQUIT   建立CORE文件 終止進程,並且生成core文件
SIGILL    建立CORE文件      非法指令
SIGTRAP   建立CORE文件       跟蹤自陷
SIGBUS    建立CORE文件      總線錯誤
SIGSEGV   建立CORE文件       段非法錯誤
SIGFPE    建立CORE文件      浮點異常
SIGIOT    建立CORE文件      執行I/O自陷
SIGKILL   終止進程     殺死進程
SIGPIPE   終止進程     向一個沒有讀進程的管道寫數據
SIGALARM  終止進程     計時器到時
SIGTERM   終止進程     軟件終止信號
SIGSTOP   停止進程     非終端來的停止信號
SIGTSTP   停止進程     終端來的停止信號
SIGCONT   忽略信號     繼續執行一個停止的進程
SIGURG    忽略信號    I/O緊急信號
SIGIO     忽略信號    描述符上可以進行I/O
SIGCHLD   忽略信號     當子進程停止或退出時通知父進程
SIGTTOU   停止進程     后台進程寫終端
SIGTTIN   停止進程     后台進程讀終端
SIGXGPU   終止進程     CPU時限超時
SIGXFSZ   終止進程     文件長度過長
SIGWINCH  忽略信號     窗口大小發生變化
SIGPROF   終止進程     統計分布圖用計時器到時
SIGUSR1   終止進程     用戶定義信號1
SIGUSR2   終止進程     用戶定義信號2
SIGVTALRM 終止進程     虛擬計時器到時

最常用的信號有四個,SIGHUP,SIGINT,SIGQUIT,SIGTSTP,使用的時候可以簡寫:

trap "" 1 2 3 24 
或
trap "" HUP INT QUIT TSTP 

然后我們回頭看看源碼:

trap onExit INT

這句是說,捕獲INT中斷信號,然就執行onExit方法。onExit中判斷是否恢復終端設置。

exit_status=127
saved_stty=""

# restore stty settings (echo in particular)
function restoreSttySettings() {
  stty $saved_stty
  saved_stty=""
}

function onExit() {
  if [[ "$saved_stty" != "" ]]; then
    restoreSttySettings
  fi
  exit $exit_status
}

什么是stty

stty命令可以用來改變終端的顯示,比如說關閉一些按鍵,開啟一些特殊字符的輸入等等。

這個命令最常用的就是下面幾個:

-a,--all   以人可讀的方式打印所有當前設置;-a參數比單獨的stty命令輸出的終端信息更詳細
-g,--save  以stty可讀的方式打印當前所有設置
-F,--file=DEVICE    打開並使用特定的設備((DEVICE)以代替標准輸入(stdin)
--help      顯示幫助並退出
--version   顯示版本並退出

stty  size  打印終端行數和列數

我們先來試試stty size這個命令

40 100

它就是打印出來了終端顯示的行數和列數。

再看看stty -a,看看都有什么內容:

speed 38400 baud; rows 40; columns 100; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O;
min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel
-iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

最開始speed是終端輸入和輸出的速度,單位是(位/秒),常用的有50、75、110、134、200、300、600、1200、1800、2400、4800、9600、19200、19.2、38400、38.4、exta 和 extb。具體還得看硬件是否支持。

后面顯示了終端的基本信息,以及一些常用的按鍵。比如

intr 表示中斷
quit 表示退出
erase 表示擦除最后一個字符
kill 表示擦除當前航
eof 表示文件結束
eol 表示當前行結束
eol2 可以設置兩個字符
swtch 表示切換外殼
start 表示停止輸出后重新開始
stop 表示停止輸出
susp 表示終端停止
rprnt 表示刷新當前行
werase 表示擦除最后一個單詞
lnext 表示輸入下一個字符
flush ?????

再看后面:

min = 1; time = 0;

min = 1通常與icaon搭配使用,表示一次讀操作至少多少個字符
time = 0表示讀超時的時間,N/10秒。

在后面一大堆的內容是stty支持的功能,詳細的可以參考:

控制模式

clocal	假定一行沒有調制解調器控制。
-clocal	假定一行帶有調制解調器控制。
cread	啟用接收器。
-cread	禁用接收器。
cstopb	每個字符選擇兩個停止位。
-cstopb	每個字符選擇一個停止位。
cs5, cs6, cs7, cs8	選擇字符大小。
hup,hupcl	最后關閉時掛起撥號連接。
-hup,-hupcl	最后關閉時不掛起撥號連接。
parenb	啟用奇偶性校驗的生成和檢測。
-parenb	禁用奇偶性校驗的生成和檢測。
parodd	選擇奇校驗。
-parodd	選擇偶校驗。
0	立即掛起電話線路。
speed	將工作站輸入和輸出速度設置為指定的 speed 數(以位/秒為單位)。並不是所有的硬件接口都支持所有的速度。speed 的可能值有:50、75、110、134、200、300、600、1200、1800、2400、4800、9600、19200、19.2、38400、38.4、exta 和  extb。
注:
exta、19200 和19.2 是同義詞;extb、38400 和38.4 是同義詞。
ispeed speed	將工作站輸入速度設置為指定的 speed 數(以位/秒為單位)。並不是所有的硬件接口都支持所有的速度,而且並不是所有的硬件接口都支持該選項。speed 的可能值與speed 選項相同。
ospeed speed	將工作站輸出速度設置為指定的 speed 數(以位/秒為單位)。並不是所有的硬件接口都支持所有的速度,而且並不是所有的硬件接口都支持該選項。speed 的可能值與speed 選項相同。

輸入模式

brkint	中斷時發出 INTR 信號。
-brkint	中斷時不發出 INTR 信號。
icrnl	輸入時將 CR 映射為 NL。
-icrnl	輸入時不將 CR 映射為 NL。
ignbrk	輸入時忽略 BREAK。
-ignbrk	輸入時不忽略 BREAK。
igncr	輸入時忽略 CR。
-igncr	輸入時不忽略 CR。
ignpar	忽略奇偶錯誤。
-ignpar	不忽略奇偶錯誤。
inlcr	輸入時將 NL 映射為 CR。
-inlcr	輸入時不將 NL 映射為 CR。
inpck	啟用奇偶校驗。
-inpck	禁用奇偶校驗。
istrip	將輸入字符剝離到 7 位。
-istrip	不將輸入字符剝離到 7 位。
iuclc	將大寫字母字符映射為小寫。
-iuclc	不將大寫字母字符映射為小寫。
ixany	允許任何字符重新啟動輸出。
-ixany	只允許 START(Ctrl-Q 按鍵順序)重新啟動輸出。
ixoff	當輸入隊列接近空或滿時,發送 START/STOP 字符。
-ixoff	不發送 START/STOP 字符。
ixon	啟用 START/STOP 輸出控制。一旦啟用 START/STOP 輸出控制,您可以按下 Ctrl-S 按鍵順序暫停向工作站的輸出,也可按下 Ctrl-Q 按鍵順序恢復輸出。
-ixon	禁用 START/STOP 輸出控制。
imaxbel	當輸入溢出時,回送 BEL 字符並且廢棄最后的輸入字符。
-imaxbel	當輸入溢出時,廢棄所有輸入。
parmrk	標記奇偶錯誤。
-parmrk	不標記奇偶錯誤。

輸出方式

bs0, bs1	為退格符選擇延遲樣式(bs0 表示沒有延遲)。
cr0, cr1, cr2, cr3	為 CR 字符選擇延遲樣式(cr0 表示沒有延遲)。
ff0, ff1	為換頁選擇延遲樣式(ff0 表示沒有延遲)。
nl0, nl1	為 NL 字符選擇延遲樣式(nl0 表示沒有延遲)。
ofill	使用延遲填充字符。
-ofill	使用延遲定時。
ocrnl	將 CR 字符映射為 NL 字符。
-ocrnl	不將 CR 字符映射為 NL 字符。
olcuc	輸出時將小寫字母字符映射為大寫。
-olcuc	輸出時不將小寫字母字符映射為大寫。
onlcr	將 NL 字符映射為 CR-NL 字符。
-onlcr	不將 NL 字符映射為 CR-NL 字符。
onlret	在終端 NL 執行 CR 功能。
-onlret	在終端 NL 不執行 CR 功能。
onocr	不在零列輸出 CR 字符。
-onocr	在零列輸出 CR 字符。
opost	處理輸出。
-opost	不處理輸出;即忽略所有其它輸出選項。
ofdel	使用 DEL 字符作為填充字符。
-ofdel	使用 NUL 字符作為填充字符。
tab0, tab1, tab2	為水平制表符選擇延遲樣式(tab0 表示沒有延遲)。
tab3	擴展制表符至多個空格。
vt0, vt1	為垂直制表符選擇延遲樣式(vt0 表示沒有延遲)。

本地模式

echo	回送每個輸入的字符。
-echo	不回送字符。
echoctl	以 ^X(Ctrl-X)回送控制字符,X 是將 100 八進制加到控制字符代碼中給出的字符。
-echoctl	不以 ^X(Ctrl-X)回送控制字符。
echoe	以“backspace space backspace”字符串回送 ERASE 字符。
注:
該模式不保持對列位置的跟蹤,因此您可能在擦除制表符和轉義序列等符號時得到意外的結果。
-echoe	不回送 ERASE 字符,只回送退格符。
echok	在 KILL 字符后回送 NL 字符。
-echok	在 KILL 字符后不回送 NL 字符。
echoke	通過擦除輸出行上的每個字符,回送 KILL 字符。
-echoke	只回送 KILL 字符。
echonl	回送 NL 字符。
-echonl	不回送 NL 字符。
echoprt	以 /(斜杠)和 \ (反斜杠) 向后回送擦除的字符。
-echoprt	不以 /(斜杠)和 \ (反斜杠) 向后回送擦除的字符。
icanon	啟用規范輸入(規范輸入允許使用 ERASE 和 KILL 字符進行輸入行的編輯)。請參閱 AIX 5L Version 5.2 Communications Programming Concepts 中的 Line Discipline Module (ldterm) 中關於canonical mode input 的討論。
-icanon	禁用規范輸入。
iexten	指定從輸入數據中識別實現性定義的功能。要識別以下控制字符,需要設置 iexten:eol2、dsusp、reprint、discard、werase、lnext。與這些模式關聯的功能也需要設置iexten:imaxbel、echoke、echoprt、echoctl。
-iexten	指定從輸入數據中識別實現性定義的功能。
isig	啟用對特殊控制字符(INTR、SUSP 和 QUIT)的字符檢查。
-isig	禁用對特殊控制字符(INTR、SUSP 和 QUIT)的字符檢查。
noflsh	不清除 INTR、SUSP 或 QUIT 控制字符之后的緩沖區。
-noflsh	清除 INTR、SUSP 或 QUIT 控制字符之后的緩沖區。
pending	下次讀操作暫掛或輸入到達時,要重新輸入從原始模式轉換為規范模式后被暫掛的輸入。暫掛是一個內部狀態位。
-pending	沒有文本暫掛。
tostop	為背景輸出發出 SIGTOU 信號。
-tostop	不為背景輸出發出 SIGTOU 信號。
xcase	在輸入中回送大寫字符,並在輸出顯示的大寫字符之前加上 \ (反斜杠)。
-xcase	不在輸入時回送大寫字符。

硬件流量控制模式

這些選項是對 《X/Open 可移植性指南,發行版 4》 標准的擴展。

cdxon	輸出時啟用 CD 硬件流量控制模式。
-cdxon	輸出時禁用 CD 硬件流量控制模式。
ctsxon	輸出時啟用 CTS 硬件流量控制模式。
-ctsxon	輸出時禁用 CTS 硬件流量控制模式。
dtrxoff	輸入時啟用 DTR 硬件流量控制模式。
-dtrxoff	輸入時禁用 DTR 硬件流量控制模式。
rtsxoff	輸入時啟用 RTS 硬件流量控制模式。
-rtsxoff	輸入時禁用 RTS 硬件流量控制模式。

組合模式

cooked	請參閱 -raw 選項。
ek	分別將 ERASE 和 KILL 字符設置為 Ctrl-H 和 Ctrl-U 按鍵順序。
evenp	啟用 parenb 和 cs7。
-evenp	禁用 parenb 並設置 cs8。
lcase,LCASE	設置 xcase,iuclc 和olcuc。在工作站只以大寫字符使用。
-lcase,-LCASE	設置 -xcase、-iuclc 和-olcuc。
nl	設置 -icrnl 和-onlcr。
-nl	設置 icrnl、 onlcr、-inlcr、-igncr、-ocrnl和-onlret。
oddp	啟用 parenb、 cs7 和 parodd。
-oddp	禁用 parenb 並設置 cs8。
parity	請參閱 evenp 選項。
-parity	請參閱 -evenp 選項。
sane	將參數重新設置為合理的值。
raw	允許原始模式輸入(不包括輸入處理,例如 erase、kill 或 interrupt);傳回奇偶(校驗)位。
-raw	允許規范輸入方式。
tabs	保留制表符。
-tabs,tab3	打印時將制表符替換為空格。
窗口大小	 
cols n,columns n	將終端(窗口)大小記錄為有 n 列。
rows n	將終端(窗口)大小記錄為有 n 行。
size	將終端(窗口)大小打印到標准輸出(先是行,再是列)中。

stty的小栗子

看完上面的東西,很多人都蒙B了,這么多東西咋用啊?咱們來個小栗子,體驗一下stty的奇妙。

場景,當你遠程ssh機器的時候是不是要輸入密碼?但是輸入的密碼是看不到的,這是怎么做到的?先來看看shell腳本吧!

#!/bin/bash
PASSWD="123"
USER=`whoami`

# save current stty setting
SAVEDSTTY=`stty -g`

# hide input characters
stty -echo

echo -n "Please input passwd:"
read passwd
echo ""
if [ "$PASSWD" = "$passwd" ];then
        echo "Welcome $USER"
else
        echo "Sorry"
fi

# echo input caharacters
stty echo

# restore stty
stty=$SAVEDSTTY

腳本的意思是:先關閉屏幕回顯,即你輸入啥屏幕也不顯示了;然后提示輸出密碼;驗證密碼是否正確給予反饋;打開回顯;恢復終端設置。

看看效果哈:

[root@localnode3 test]# sh stty.sh
Please input passwd:
Sorry
[root@localnode3 test]# sh stty.sh
Please input passwd:
Welcome root
[root@localnode3 test]#

挺有意思吧!

回頭看源碼

有了對stty的了解后,回頭我們看看spark-shell腳本,就清晰明了了。

saved_stty=$(stty -g 2>/dev/null)

首先保存了當前的終端配置。

if [[ ! $? ]]; then
  saved_stty=""
fi

如果收到退出命令,就恢復stty狀態。

然后調用main方法,並傳遞所有的參數main "$@",最后根據返回狀態,判斷是直接終端退出還是恢復之前的終端界面。

回頭再看看main方法就容易理解了:

function main() {
  if $cygwin; then
    stty -icanon min 1 -echo > /dev/null 2>&1
    export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djline.terminal=unix"
    "${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
    stty icanon echo > /dev/null 2>&1
  else
    export SPARK_SUBMIT_OPTS
    "${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
  fi
}

如果是cygwin,先關閉echo回顯,設置讀操作最少1個字符。然后啟動spark-submit 執行org.apache.spark.repl.Main類,並設置應用的名字,傳遞參數。執行完成后,再開啟echo回顯。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM