기타/미분류

리눅스 Job 제어

Job과 프로세스

Job 제어는 많은 shell(Bash, Tcsh 포함)에서 제공되어지며, 다수의 명령어나 job을 동시에 제어할 수 있게 해준다. 이것을 더 설명하기 전에 프로세스에 대한 설명이 필요하다.

프로그램을 실행할 때는 언제나 운영되는 프로그램의 단순한 이름으로 시작한다. ‘ps’ 라는 명령어는 현재의 운영되는 프로세스의 목록을 출력해 준다. 예제를 아래와 같이 보인다.

/home/larry# ps

PID TT STAT TIME COMMAND
24 3 S 0:03 (bash)
161 3 R 0:00 ps

/home/larry#

첫 번째 칼럼의 PID 목록은 프로세스 ID이며, 이것은 모든 운영되는 프로세스에게 주어지는 단위 숫자이다. 마지막 칼럼의 COMMAND는 운영되는 명령어의 이름이다. 여기에, 우리는 Larry가 운영하고 있는 프로세서만을 볼 수 있으며, 현재 Larry의 shell인 ‘bash’와 ‘ps’ 명령만이 있다. - 실제로 시스템에 운영되는 많은 프로세스들이 있으며, 이것은 ‘ps -aux’로 볼 수 있다. - 여기서 볼 수 있는 것처럼, ‘bash’는 ‘ps’ 명령어와 동시에 운영됨을 알 수 있다. Larry가 명령어를 입력하면 ‘bash’가 ‘ps’를 실행한 것이다. ‘ps’ 이후는 운영이 끝나고(프로세스 목록의 출력이 끝나고) 제어권이 ‘bash’에게 되돌아오며, 프롬프트가 출력된후 다시 명령어 대기상태가 된다.

운영되는 프로세스는 shell에서 “job”이라고 한다. 프로세스와 job은 상호 변경될 수 있다. 어쨌든, 프로세스는 몇 개의 독립적인 “job”들 사이를 전환할 수 있으며, shell의 job 제어가 또한, "job"이라고 일컬어진다.

대부분의 경우 사용자가 shell에게 마지막 입력한 명령어가 단일 job으로 운영된다. job 제어를 사용하면, 여러 개의 job을 한번에 할 수 있으며, 필요할 때마다 전환할 수 있다. 예를 들어, 문서 파일을 편집하고 있으며, 어떤 다른 일들을 하기 위해 편집을 보류해야 할 경우가 있다고 하자. job 제어를 사용하면, 에디터를 임시적으로 잠시 보류하고, shell 프롬프트로 돌아와서 다른 작업을 할 수 있다. 작업이 끝나면, 에디터를 종료하지만 않았다면 어디에서든지 다시 에디터로 되돌아올 수 있다. 이것은 단순히 한 예에 불과하지만, 실제로 매우 유용하다.

포그라운드와 백그라운드

job은 포그라운드에서와 백그라운드에서 할 수 있다. 포그라운드에서의 job은 한번에 한 job만 할 수 있으며, 키보드로부터 입력을 받고, 화면으로 출력을 보내는 상호작용이 이루어지진다. (물론 입력과 출력을 리디렉트 시킬 수 있다.) 다른 말로 하면, 백그라운드에서의 job은 터미널로부터 입력을 받지 못한다. 따라서, 상호작용이 별로 필요하지 않는 작업에 일반적으로 사용된다.

프로그램 컴파일이나 압축같은 작업은 시간이 소요되며 또한, 처리되는 동안 어떠한 다른 입력을 필요로 하지 않는다. 이런 종류의 작업들을 백그라운드로 실행시키면, 다른 프로그램을 운영할 수 있는 여유를 갖게 된다. 또한, 하나이상의 작업을 백그라운드로 운영할 수 있으며, 포그라운드에서의 처리 속도와의 차이는 거의 나지 않는다.

job은 또한 보류될 수 있다. 보류된 job은 현재 운용되는 job은 아니고, 잠시 멈추는 것이다. job을 보류한 후에 포그라운드에서든지 백그라운드에서든지 그 job을 필요에 따라 계속하게 할 수 있다. 보류된 job은 어떠한 방법으로도 그 상태가 변하지 않으며 강제로 종료시키지 않을 때까지 계속 유지된다.

보류된 job은 인터럽팅된 job과는 같지 않다. 운영되는 프로세스를 인터럽트 시킬 때( 인터럽트 키는 stty 명령으로 설정할 수 있으며, 디폴트는 [ctrl-C]이다.) 는 프로세스를 제거하는(kill) 것이다. job이 제거되면, 그 job을 다시 할 수 없고, 명령을 다시 실행하여야 한다. 어떤 프로그램은 인터럽트를 [ctrl-C]로 즉시 job을 중지하지 못하게 한다. 이것은 프로그램이 실행하기 이전의 깨끗한 상태로 유지하기 위하여 단순히 인터럽트를 중지되는 것을 허용하지 않는 것이다.

백그라운드 시키기와 Job 제거

간단한 예제로 시작해 보자. 명령어 ‘yes’는 끊임없이 y를 출력하는 명령이다. 이것은 어떤 명령어가 계속적인 질문으로 답을 요구하는 경우, 그것이 보통 yes라는 답이 된다면, 파이프를 사용하여 y를 계속 보내 답을 하는 것으로 유용하게 사용되어 질 수 있다.

  /home/larry# yes
   y
   y
   y

y가 계속 끊임없이 출력된다. 인터럽트 키, 보통 [ctrl-C] 같은 것으로서 프로세스를 제거할 수 있다. 이런 y의 출력들이 귀찮다면, yes의 표준 출력을 /dev/null 로 리디렉트 시켜 보자. /dev/null 은 데이터의 “black hole” 로 작용한다. 어떠한 데이터도 이곳으로 보내면 나타나지 않는다. 이것은 질문을 계속 물어 보는 성가신 프로그램을 조용하게 만드는 아주 효과적인 방법이다.

/home/larry# yes > /dev/null

아무것도 출력되지 않으며, shell 프롬프트로 되돌아오지도 않는다. 이것은 yes가 여전히 운영되고 있으며, 계속 /dev/null 로 ‘y’를 출력하고 있는 것이다. 따라서, job을 제거하기 위해서 인터럽트 키를 눌러야 한다.

가정을 해보자. 우리는 yes의 명령이 계속되기를 바라나, 다른 작업을 하기 위해서 shell 프롬프트로 되돌아오기를 원한다. 그러면, 우리는 백그라운드로 yes를 실행해 놓을 수 있다. 이것은 그것이 계속 운영되고 있는 있는 것이지만 상호 작용할 필요는 없는 것이다.

백그라운드로 프로세스를 놓는 방법은 명령어의 끝에 "&"를 추가하는 것이다.

/home/larry# yes > /dev/null &
[1] 164
/home/larry#

위에서 볼 수 있는 것처럼, shell 프롬프트가 되돌아 왔다. 그런데, “[1] 164”는 무엇일까? 그리고, ‘yes’의 명령이 정말로 실행되고 있는 것일까?

“[1]”은 yes 프로세스의 job 번호를 표시하고 있다. shell은 모든 운영되는 job을 job 번호로 지정한다. yes는 현재 운영되고 있는 오직 하나의 job이기 때문에 job 번호 1인 것이다. "164"는 프로세스 ID 또는 PID 라는 것이며, 이것은 시스템이 job에게 주는 프로세스 번호이다. 이러한 번호들은 모두 job을 가르키는데 사용된다.

백그라운드로 yes 프로세스가 계속적으로 /dev/null에 ‘y’들을 보낼 수 있다. 이 프로세스의 상태를 검사하기 위해서는 셀 명령인 ‘jobs’ 을 사용하면 된다.

/home/larry# jobs
[1]+ Running yes >/dev/null &
/home/larry#

운영중인 것을 확인할 수 있다. 또한 ‘ps’명령어를 사용하여 위에서 검사한 것과 같은 job의 상태를 알 수 있다.

job를 정지시키기 위해서는 ‘kill’ 명령어를 사용해야 한다. 이 명령어는 job 번호나 프로세스 ID 번호를 인자로서 사용한다. 이것은 job 번호는 1이며, 명령어를 사용하기 위해서는 아래와 같이 입력한다.

/home/larry# kill %1

위와 같이 하면 job은 제거될 것이다. job이나 job 번호를 표시하려 할 때는 퍼센트 문자의 위치에 번호를 넣으면 된다.

지금 우리는 job을 제거하였고, ‘jobs’을 사용하고 그것을 다시 검사하고 있다.

  /home/larry# jobs
  [1]+ Terminated yes >/dev/null
  /home/larry#

job은 확실히 제거되었고, ‘jobs’ 명령어를 다시 사용한다면, 아무것도 출력되지 않을 것이다.

또한 프로세스 ID(PID) 번호를 사용하여 job을 제거할 수 있다. PID는 job이 시작할 때 job ID와 함께 출력되는 것이다. 우리의 예제에서, 프로세서 ID는 164이다. 따라서,

  /home/larry# kill 164

은, 아래와 동일한 기능을 수행한다,

  /home/larry# kill %1

프로세스 ID에 의한 job을 언급할 때는 "%"를 사용할 필요가 없다.

Job의 보류와 계속하기

백그라운드 안으로 job을 넣는 또 다른 방법이 있다. job을 보통으로 실행(포그라운드로) 시킬 수 있으며, job을 멈출 수도 있고, 백그라운드로 재실행 시킬 수도 있다. 이것을 아래에 설명한다.

먼저, 포그라운드로 yes를 실행한다.

  /home/larry# yes > /dev/null

yes가 포그라운드로 실행이 되었기 때문에, shell 프롬프트로 바로 돌아올 수 없다.

지금, ‘[ctrl-C]’로서 job을 인터럽팅하는 대신에, job을 보류해 보자. job을 제거하지 않고, 보류시킨다는 것은 재실행하기 전까지 잠시 멈춘다는 것을 의미한다. 이렇게 하기 위해서는 보류 키를 눌러야 하며, 일반적으로 ‘[ctrl-Z]’이다.

  /home/larry# yes > /dev/null
  [ctrl-Z]
  [1]+ Stopped yes >/dev/null
  /home/larry#

보류된 job은 단순히 실행이 멈춘 것이다. CPU 나 메모리는 job을 위해 전혀 사용되지 않는다. 여기서 아무것도 변하지 않은 보류되기 이전 job 상태로 재실행 할 수 있다.

이 job을 포그라운드로 재실행하기 위해서는 명령어 ‘fg’(forground)를 사용한다.

  /home/larry# fg
  yes >/dev/null

shell은 포그라운드로 바꾸었던 명령어의 이름을 다시 출력한다. 다시 job을 멈추게 하려면 ‘[ctrl-Z]’를 사용한다. 이번엔 명령어 ‘bg’를 사용하여 job을 백그라운드로 놓아 보자. 이것은 처음에 명령어를 입력할때 끝에 "&"를 사용하여 실행했었던 것과 같다.

  /home/larry# bg
  [1]+ yes >/dev/null &
  /home/larry#

위와 같이 하면 프롬프트가 되돌아온다. job은 yes가 실행이 되었음을 보고한다. 먼저 번에 사용하였던 명령어 ‘kill’을 사용하여 job을 제거할 수 있다.

어떻게 job을 멈출 수 있을까? [ctrl-Z]을 사용하면 job이 백그라운드로 실행되기 때문에 작업을 할 수 없다. 이 답은 ‘fg’를 사용하여 포그라운드로 job을 놓아야만 하며, 그래야 멈출 수 있다. 멈춰진 job이나 백그라운드의 job을 ‘fg’를 사용하여 전환할 수 있다.

백그라운드에서의 job과 멈춰진 job은 커다란 차이가 있다. 보류된 job은 CPU 시간이나 메모리를 전혀 사용하지 않으며, 따라서 아무 일도 하지 않는다. 백그라운드로 운영되고 있는 job은 메모리를 사용하며, 또한 다른 작업을 하고 있을 동안에도 완벽하게 이 작업을 수행한다. 어쨌든, 백그라운드에서의 job은 터미널에서 문자를 출력하고자 하며, 어떤 다른 작업을 하고 있다면, 귀찮게 할 것이다. 예를 들어, 아래의 명령같이 입력한다면,

  /home/larry# yes &

/dev/null 로 표준 출력을 리디렉팅하지 않는다면, y의 문자열들은 화면에 어떠한 인터럽트도 받지 않고 출력될 것이다. ‘[ctrl-C]’는 백그라운드의 job 에게 인터럽트를 걸 수 없다. 끊임없는 ‘y’들을 멈추기 위해서는 ‘kill’ 명령어를 사용해야 한다. 아마도, 입력하는 것을 보는 것은 불가능할 것이다.

또 중요한 것은, 명령어 ‘fg’나 ‘bg’를 인자없이 입력하면 최근에 멈추어진 job을 포그라운드나 백그라운드 시킨다. 만일 지금 복수개의 job을 동시에 운영하고 있다면, 특정 job은 주어진 job ID를 ‘fg’나 ‘bg’의 인자로서 사용하여 포그라운드나 백그라운드로 전환 시킬 수 있다.

  /home/larry# fg %2

job 번호 2를 포그라운드로 전환 시킨다.

  /home/larry# bg %3

job 번호 3을 백그라운드로 전환 시킨다.

프로세스 ID를 ‘fg’나 ‘bg’ 명령에서는 사용할 수 없다.

‘fg’, ‘bg’ 명령 없이 job 번호만을 사용할 수 있다. 따라서 아래의 두 명령은 동일하다.

  /home/larry# %2
  /home/larry# fg %2

job제어를 사용하는 것은 shell의 기능을 이용한다는 것을 기억해야 한다. 명령어 ‘fg’, ‘bg’, ‘jobs’는 shell의 명령어들이다. 만일 어떤 이유에서 shell이 job 제어를 제공하지 않는다면, 이러한 명령어들이 유용한지 알아낼 수 없다.

덧붙이면, job 제어의 형태는 Bash나 Tcsh에서 약간 다르다. 사실, 모든 shell이 job 제어를 제공하지 않으나, 리눅스의 대부분의 shell에서는 job 제어를 제공한다.