Keyword Commands
time
time [-p] pipeline
명령을 실행하는데 걸린 시간을 표시해줍니다. 명령들이 파이프로 연결되거나 { }
, ( )
로 group 되어있을 경우 모든 명령들을 포함합니다. TIMEFORMAT 환경변수를 이용해 출력 포멧을 변경할수 있습니다.
- real
wall clock 시간이라고 하며 프로그램이 시작된 후부터 종료될 때까지를 시계로 잰것과 같습니다. 그러므로 프로그램 실행시 단순히 I/O 를 위해 wait 한 시간이나 sleep 한 시간도 모두 포함됩니다.
$ time { sleep 1; sleep 2 ;}
real 0m3.002s
user 0m0.000s
sys 0m0.000s
- user
user 모드에서 사용한 cpu 시간입니다. wait 한 시간은 포함되지 않습니다.
user + sys
시간이 실질적으로 프로그램이 사용한 cpu 시간이라고 볼 수 있습니다.
- sys
kernel 모드에서 사용한 cpu 시간입니다. 메모리를 할당하거나, 디스크 같은 장치에 접근하는 것은 system call 을 통해서 kernel 모드에서 실행됩니다.
real ≠ user + sys
요즘은 대부분 multi-core cpu 를 사용합니다.
그래서 가령 2 core cpu 를 사용 중이라고 가정했을때, 프로그램이 실행후 종료될때까지 1분이 걸렸고, 각 core 에서 1분의 cpu 시간을 사용했다면 real
은 1분이지만 user + sys
는 2 분이 될수있습니다.
real 1m47.363s
user 2m41.318s
sys 0m4.013s
Exit Status:
종료 상태 값은 pipeline 의 리턴값이 됩니다.
coproc
coproc [NAME] command [redirections]
coproc (coprocess) 는 두 프로세스 간에 양방향 통신을 가능하게 합니다.
coproc 는 command 를 background 로 실행시키는데 이때 명령의 stdin, stdout 을 파이프를 통해 FD 에 연결해서 외부로 부터 데이터 입력을 받고 연산결과를 출력할 수 있습니다.
설정된 FD 는 $COPROC
array 변수에 원소로 등록되므로 프로세스로 데이터를 보내기 위해서는 >& ${COPROC[1]}
, 받기 위해서는 <& ${COPROC[0]}
을 이용할 수 있습니다. background pid 는 $COPROC_PID
변수에 저장됩니다.
# background 로 실행됨
$ coproc while read -r line; do eval expr "$line"; done
[1] 18008
$ echo "1 + 2" >& ${COPROC[1]}
$ read -r res <& ${COPROC[0]}
$ echo $res
3
$ kill $COPROC_PID
위의 명령은 다음과 동일하다고 볼 수 있습니다. 그러니까 coproc 는 파이프를 생성해서 FD 를 연결하고 삭제하는 작업을 자동으로 해준다고 보면 되겠습니다.
$ mkfifo inpipe outpipe
$ exec 3<>inpipe 4<>outpipe
$ while read -r line; do eval expr "$line" > inpipe ; done < outpipe &
[1] 17647
$ echo "1 + 2" >& 4
$ read -r res <& 3
$ echo $res
3
$ kill 17647
$ exec 3>&- 4>&-
$ rm -f inpipe outpipe
다음은 coproc 를 실행했을때 FD 상태입니다. 프로세스 별로 2 개의 FD 가 사용되고 있고, pipe 도 2 개가( 빨강, 파랑 ) 사용되고 있는 것을 볼 수 있습니다.
coproc 에 의해 생성되는 FD 는 subshell 에서 사용할 수 없습니다.( Process Substitution 은 제외 )
coproc 를 이용한 websocket server
사용방법은 스크립트를 실행한 후에 브라우저를 이용해 http://<Host>:6655/
에 접속한 후
shell 명령을 입력하면 됩니다.
사용 포트는 브라우저 접속용으로 6655 번을, websocket 용으로 6656 번을 사용합니다.
종료는 동일하게 exit 으로 합니다.
버퍼 크기를 넘어서는 데이터가 출력될 경우 버퍼가 꽉 찬 상태에서 writer 는 더 이상 쓰기를 진행하지 못하고 reader 는 writer 가 쓰기를 완료하지 않아 읽지 못하는 deadlock 이 발생할 수 있습니다.
#!/bin/bash
#####################################
# #
# WebSocket shell #
# #
#####################################
# Requires bash 4.x, openssl.
# Author: rootshell@corelogics.de
# 스크립트와 같은 process group 에 속하는 프로세스들에게 TERM 신호를 보냅니다.
trap 'kill 0' EXIT
# 실제 websocket 이 연결되어 입,출력이 일어나는 부분입니다.
# ${d[0]} 가 nc 명령의 stdin 에 연결되고 ${d[1]} 가 stdout 에 연결됩니다.
coproc d { nc -l 6656 ;}
# 6655 포트를 리스닝을 하고 있다가 브라우저가 접속하면 페이지를 전송합니다.
nc -N -l 6655 > /dev/null <<\ENDOFPAGE
HTTP/1.1 200 OK
<html>
<head>
<script language="javascript">
var url = location.hostname + ':' + (parseInt(location.port) + 1);
var ws = new WebSocket('ws://' + url + '/test');
ws.onmessage = function (msg) {
document.f.out.value += msg.data + '\n';
document.f.out.scrollTop = document.f.out.scrollHeight;
}
ws.onclose = function () { alert('Connection closed.'); }
function send() {
ws.send('' + document.f.in.value);
document.f.in.value = '';
}
</script>
</head>
<body>
<form name="f">
<textarea name="out" cols="100" rows="25"></textarea>
<br>
Command: <input value="" type="text" size="90" id="in"
onkeypress="if ( event.keyCode == 13 ) { send(); return false; }" />
</form>
</body>
</html>
ENDOFPAGE
# 전송된 페이지의 javascript 이 실행되어 websocket 이 연결되는 부분입니다.
until read -r line
line=$(echo "$line" | tr -d '\r\n')
test -z "$line"
do
[ "${line:0:18}" = "Sec-WebSocket-Key:" ] && key=${line:19}
[ "${line:0:22}" = "Sec-WebSocket-Version:" ] && ver=$line
done <& "${d[0]}"
rkey=$( echo -n ${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11 |
openssl dgst -sha1 -binary |
base64 )
{
echo -ne "HTTP/1.1 101 Switching Protocols\r\n"
echo -ne "Upgrade: websocket\r\n"
echo -ne "Connection: Upgrade\r\n"
echo -ne "Sec-WebSocket-Accept: ${rkey}\r\n"
echo -ne "${ver}\r\n\r\n"
} >& "${d[1]}"
# 사용자가 입력한 명령을 실행하여 결과를 ${d[1]} 로 출력하는 함수입니다.
do_cmd() {
{ echo -e ">>> $1" ; eval "$( echo -e "$1")" ;} 2>&1 |
while read -r line; do
len=$( echo -n "$line" | wc -c )
if [ $len -le 125 ]; then
echo -ne "\x81\x$( printf '%02x' $len )"
elif [ $len -le 65535 ]; then
echo -ne "\x81\x7e\x$( printf '%04x' $len |
sed -r 's#([[:xdigit:]]{2})([[:xdigit:]]{2})#\1\\x\2#' )"
else
echo -ne "\x81\x7f\x$( printf '%016x' $len |
sed -r ':X s#([[:xdigit:]]{2})([[:xdigit:]]{2})#\1\\x\2; tX#' )"
fi
echo -n "$line"
done
} >& "${d[1]}"
# 이제부터 stdin 입력은 ${d[0]} 로 부터 받습니다.
exec <& "${d[0]}"
# while loop 를 이용해 ${d[0]} 로 부터 사용자 입력을 받아 do_cmd() 함수를 호출합니다.
while true; do
reclen=$(( `od -An -j1 -N1 -tdI` - 128 ))
for i in `seq 0 3`; do
mk[i]=$( od -An -N1 -tdI )
done
cmd=""
for i in `seq 0 $((reclen - 1))`; do
bt=$( od -An -N1 -tdI )
bt=$(( bt ^ ${mk[ $((i % 4)) ]} ))
cmd+='\x'$(printf '%02x' $bt)
done
[ exit = "$( echo -e "$cmd" )" ] && exit
do_cmd "$cmd"
done