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回显。

参考

本文转自博客园xingoo的博客,原文链接:Spark源码分析之Spark Shell(下),如需转载请自行联系原博主。

时间: 2024-10-18 09:15:06

Spark源码分析之Spark Shell(下)的相关文章

Spark源码分析之Spark Shell(上)

终于开始看Spark源码了,先从最常用的spark-shell脚本开始吧.不要觉得一个启动脚本有什么东东,其实里面还是有很多知识点的.另外,从启动脚本入手,是寻找代码入口最简单的方法,很多开源框架,其实都可以通过这种方式来寻找源码入口. 先来介绍一下Spark-shell是什么? Spark-shell是提供给用户即时交互的一个命令窗口,你可以在里面编写spark代码,然后根据你的命令立即进行运算.这种东西也被叫做REPL,(Read-Eval-Print Loop)交互式开发环境. 先来粗略的

Spark源码分析之Spark-submit和Spark-class

有了前面spark-shell的经验,看这两个脚本就容易多啦.前面总结的Spark-shell的分析可以参考: Spark源码分析之Spark Shell(上) Spark源码分析之Spark Shell(下) Spark-submit if [ -z "${SPARK_HOME}" ]; then export SPARK_HOME="$(cd "`dirname "$0"`"/..; pwd)" fi # disable

Spark源码分析之六:Task调度(二)

        话说在<Spark源码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这个方法针对接收到的ReviveOffers事件进行处理.代码如下: // Make fake resource offers on all executors     // 在所有的executors上提供假的资源(抽象的资源,也就是资源的对象信息,我是这么理解的)     private def makeOffers() {  

Spark源码分析之四:Stage提交

        各位看官,上一篇<Spark源码分析之Stage划分>详细讲述了Spark中Stage的划分,下面,我们进入第三个阶段--Stage提交.         Stage提交阶段的主要目的就一个,就是将每个Stage生成一组Task,即TaskSet,其处理流程如下图所示:         与Stage划分阶段一样,我们还是从handleJobSubmitted()方法入手,在Stage划分阶段,包括最好的ResultStage和前面的若干ShuffleMapStage均已生成,那

Spark源码分析 – Shuffle

参考详细探究Spark的shuffle实现, 写的很清楚, 当前设计的来龙去脉   Hadoop Hadoop的思路是, 在mapper端每次当memory buffer中的数据快满的时候, 先将memory中的数据, 按partition进行划分, 然后各自存成小文件, 这样当buffer不断的spill的时候, 就会产生大量的小文件  所以Hadoop后面直到reduce之前做的所有的事情其实就是不断的merge, 基于文件的多路并归排序, 在map端的将相同partition的merge到

Spark源码分析 – BlockManager

参考, Spark源码分析之-Storage模块 对于storage, 为何Spark需要storage模块?为了cache RDD  Spark的特点就是可以将RDD cache在memory或disk中,RDD是由partitions组成的,对应于block  所以storage模块,就是要实现RDD在memory和disk上的persistent功能 首先每个节点都有一个BlockManager, 其中有一个是Driver(master), 其余的都是slave  master负责trac

Spark源码分析之二:Job的调度模型与运行反馈

        在<Spark源码分析之Job提交运行总流程概述>一文中,我们提到了,Job提交与运行的第一阶段Stage划分与提交,可以分为三个阶段:         1.Job的调度模型与运行反馈:         2.Stage划分:         3.Stage提交:对应TaskSet的生成.         今天,我们就结合源码来分析下第一个小阶段:Job的调度模型与运行反馈.         首先由DAGScheduler负责将Job提交到事件队列eventProcessLoop

Spark源码分析之七:Task运行(一)

        在Task调度相关的两篇文章<Spark源码分析之五:Task调度(一)>与<Spark源码分析之六:Task调度(二)>中,我们大致了解了Task调度相关的主要逻辑,并且在Task调度逻辑的最后,CoarseGrainedSchedulerBackend的内部类DriverEndpoint中的makeOffers()方法的最后,我们通过调用TaskSchedulerImpl的resourceOffers()方法,得到了TaskDescription序列的序列Seq

Spark源码分析之五:Task调度(一)

        在前四篇博文中,我们分析了Job提交运行总流程的第一阶段Stage划分与提交,它又被细化为三个分阶段:         1.Job的调度模型与运行反馈:         2.Stage划分:         3.Stage提交:对应TaskSet的生成.         Stage划分与提交阶段主要是由DAGScheduler完成的,而DAGScheduler负责Job的逻辑调度,主要职责也即DAG图的分解,按照RDD间是否为shuffle dependency,将整个Job划分