IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  리눅스  >

Linux 프로세스 관리 분석

생성, 관리, 스케줄링 및 소멸

developerWorks
문서 옵션
PDF format - Fits A4 and Letter

PDF - Fits A4 and Letter
56KB (11 pages)

Get Adobe® Reader®

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

M. Tim Jones, Consultant Engineer, Emulex Corp.

원문 게재일 : 2008 년 12 월 20 일
번역 게재일 : 2009 년 5 월 05 일

Linux®에서 사용자 공간 프로세스를 생성 및 관리하는 과정에는 UNIX®와 공통되는 많은 원칙뿐만 아니라 Linux만의 고유한 여러 가지 최적화 과정도 있습니다. 이 기사에서는 Linux 프로세스의 수명 주기를 검토하고 사용자 프로세스 생성, 메모리 관리, 스케줄링 및 소멸과 관련된 커널 내부 구조를 살펴봅니다.

Linux는 컴퓨팅 니즈가 지속적으로 변화하는 매우 역동적인 시스템이다. Linux의 컴퓨팅 니즈는 프로세스의 공통 추상화를 중심으로 표현된다. 프로세스의 수명은 짧을 수도 있고(명령행에서 실행한 명령) 길 수도 있다(네트워크 서비스). 이 때문에 프로세스 및 스케줄링에 대한 일반적인 관리가 매우 중요하다.

사용자 공간에서 프로세스는 PID(Process Identifier)로 표현된다. 사용자의 관점에서 PID는 프로세스를 고유하게 식별하는 숫자 값이다. PID는 프로세스가 작동하는 동안에는 바뀌지 않지만 프로세스가 종료된 이후에는 재사용될 수 있으므로 PID를 캐시에 저장하는 것이 항상 이상적이지는 않다.

사용자 공간에서 여러 가지 방법으로 프로세스를 생성할 수 있다. 프로그램을 실행하거나(결과적으로 새 프로세스가 생성됨) 프로그램 내에서 fork 또는 exec 시스템 호출을 호출할 수 있다. fork를 호출하면 하위 프로세스가 생성되고 exec를 호출하면 현재 프로세스 컨텍스트가 새 프로그램으로 대체된다. 이 기사에서는 이 두 메소드의 작동 방법에 대해 설명한다.

이 기사에서는 먼저 프로세스의 커널 표현과 프로세스가 커널에서 관리되는 방법에 대해 설명한 후 하나 이상의 프로세스에서 프로세스를 생성하고 스케줄링하는 다양한 방법을 살펴본 다음 마지막으로 프로세스가 소멸될 때 이루어지는 작업에 대해 설명하면서 프로세스에 대한 설명을 마무리할 것이다.

프로세스 표현

developerWorks에서 Tim Jones의 추가 기사 읽기

Linux 커널 내에서 프로세스는 task_struct라는 약간 큰 구조체로 표현된다. 이 구조체에는 프로세스를 표현하는 데 필요한 모든 데이터와 다른 프로세스(상위 및 하위)와의 관계를 유지하고 계정을 관리하는 데 필요한 여러 가지 데이터가 포함되어 있다. task_struct에 대한 자세한 설명은 이 기사의 범위를 벗어나지만 Listing 1에서 task_struct의 일부분을 보여 준다. 이 코드에는 이 기사에서 살펴볼 특정 요소가 있다. task_struct는 ./linux/include/linux/sched.h에 있다.


Listing 1. task_struct의 일부
	
struct task_struct {

	volatile long state;
	void *stack;
	unsigned int flags;

	int prio, static_prio;

	struct list_head tasks;

	struct mm_struct *mm, *active_mm;

	pid_t pid;
	pid_t tgid;

	struct task_struct *real_parent;

	char comm[TASK_COMM_LEN];

	struct thread_struct thread;

	struct files_struct *files;

	...

};

Listing 1에서는 실행 상태, 스택, 플래그 세트, 상위 프로세스, 실행 스레드(많을 수 있음) 및 열려 있는 파일과 같이 예상했던 여러 항목을 볼 수 있다. 이러한 항목은 기사의 후반부에서 살펴볼 것이므로 여기에서는 몇 가지 항목만 소개한다. state 변수는 태스크의 상태를 나타내는 비트 세트이다. 프로세스의 가장 일반적인 상태로는 실행 중이거나 곧 실행되기 위해 실행 큐에 있는 상태(TASK_RUNNING), 대기 중인 상태(TASK_INTERRUPTIBLE), 대기 중이지만 인터럽트할 수 없는 상태(TASK_UNINTERRUPTIBLE), 중지된 상태(TASK_STOPPED) 및 기타 상태가 있다. 이러한 플래그의 전체 목록은 ./linux/include/linux/sched.h에 있다.

flags라는 단어는 프로세스가 생성 중(PF_STARTING), 종료 중(PF_EXITING) 또는 메모리를 할당하고 있는지(PF_MEMALLOC) 등의 모든 상태를 나타내는 많은 표시기를 정의한다. 실행 파일의 이름(경로 제외)이 comm(명령) 필드에 입력된다.

또한 각 프로세스에는 우선 순위(static_prio)가 지정되지만 프로세스의 실제 우선 순위는 로드 및 기타 요인에 따라 동적으로 결정된다. 우선 순위 값이 낮을수록 실제 우선 순위가 더 높다.

tasks 필드는 연결 목록 기능을 제공한다. 이 필드에는 prev 포인터(이전 태스크를 가리킴)와 next 포인터(다음 태스크를 가리킴)가 포함된다.

프로세스의 주소 공간은 mmactive_mm 필드로 표현된다. mm은 프로세스의 메모리 디스크립터를 나타내는 반면 active_mm은 이전 프로세스의 메모리 디스크립터이다(컨텍스트 전환 시간을 향상시키기 위한 최적화).

마지막으로 thread_struct는 프로세스의 저장 상태를 식별한다. 이 요소는 Linux가 실행 중인 특정 아키텍처에 따라 달라지며 ./linux/include/asm-i386/processor.h에서 이에 대한 예제를 볼 수 있다. 이 구조체에서는 실행 중인 컨텍스트에서 전환되는 프로세스에 대한 저장소(하드웨어 레지스터, 프로그램 카운터 등)를 찾을 수 있다.




위로


프로세스 관리

최대 프로세스 수

Linux에서는 프로세스가 동적으로 할당되기는 하지만 특정 최대값이 사용된다. 이 최대값은 커널에서 max_threads라는 기호로 표시되며 ./linux/kernel/fork.c에서 볼 수 있다. proc 파일 시스템의 /proc/sys/kernel/threads-max를 통해 사용자 공간에서 이 값을 변경할 수 있다.

이제 Linux 내에서 프로세스를 관리하는 방법을 살펴보자. 대부분의 경우, 프로세스는 동적으로 생성되며 동적으로 할당된 task_struct로 표시된다. 여기에서 init 프로세스 자체에는 예외가 적용된다. 즉, 이 프로세스는 항상 존재하며 정적으로 할당된 task_struct로 표시된다. ./linux/arch/i386/kernel/init_task.c에서 이에 대한 예제를 볼 수 있다.

Linux의 모든 프로세스는 두 가지 방법으로 수집된다. 첫 번째 방법은 PID 값에 따라 해시되는 해시 테이블을 사용하는 것이고, 두 번째 방법은 순환 양방향 연결 목록을 사용하는 것이다. 순환 목록은 태스크 목록을 반복하는 데 이상적이다. 목록이 순환형이기 때문에 이 목록에는 헤드나 테일이 없다. 그러나 init_task는 항상 존재하기 때문에 추가 반복을 위한 앵커 포인트로 사용할 수 있다. 이제 이러한 방법에 따라 현재 태스크 세트를 반복하는 예제를 살펴보자.

태스크 목록은 사용자 공간에서 액세스할 수 없지만 모듈 형태로 커널 내에 코드를 삽입하여 이 문제 쉽게 해결할 수 있다. Listing 2에서는 태스크 목록을 반복하여 각 태스크에 대한 간단한 정보(name, pidparent 이름)를 제공하는 매우 간단한 프로그램을 보여 준다. 이 모듈에서는 printk를 사용하여 결과를 출력한다. 출력을 보려면 cat 유틸리티(또는 실시간의 경우 tail -f /var/log/messages)를 사용하여 /var/log/messages 파일을 보아야 한다. next_task 함수는 태스크 목록의 반복을 단순화하는 매크로로 sched.h에 있으며 후속 태스크의 task_struct 참조를 리턴한다.


Listing 2. 태스크 정보를 출력하는 간단한 커널 모듈(procsview.c)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>

int init_module( void )
{
  /* Set up the anchor point */
  struct task_struct *task = &init_task;

  /* Walk through the task list, until we hit the init_task again */
  do {

    printk( KERN_INFO "*** %s [%d] parent %s\n",
		task->comm, task->pid, task->parent->comm );

  } while ( (task = next_task(task)) != &init_task );

  return 0;

}

void cleanup_module( void )
{
  return;
}

Listing 3처럼 Makefile을 사용하여 이 모듈을 컴파일할 수 있다. 컴파일이 완료된 후에는 커널 오브젝트를 insmod procsview.ko를 사용하여 삽입할 수 있으며 rmmod procsview를 사용하여 제거할 수 있다.


Listing 3. 커널 모듈을 빌드하는 Makefile

obj-m += procsview.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

삽입 후에는 /var/log/messages에 다음과 같은 출력이 표시된다. 여기에서 유휴 태스크(swapper)와 init 태스크(pid 1)를 볼 수 있다.

Nov 12 22:19:51 mtj-desktop kernel: [8503.873310] *** swapper [0] parent swapper
Nov 12 22:19:51 mtj-desktop kernel: [8503.904182] *** init [1] parent swapper
Nov 12 22:19:51 mtj-desktop kernel: [8503.904215] *** kthreadd [2] parent swapper
Nov 12 22:19:51 mtj-desktop kernel: [8503.904233] *** migration/0 [3] parent kthreadd
...

또한 현재 실행 중인 태스크도 식별할 수 있다. Linux는 현재 실행 중인 프로세스(task_struct 유형)인 current라는 기호를 유지한다. init_module의 끝에 다음 행을 추가하면

printk( KERN_INFO, "Current task is %s [%d], current->comm, current->pid );

다음과 같은 결과가 출력된다.

Nov 12 22:48:45 mtj-desktop kernel: [10233.323662] Current task is insmod [6538]

init_module 함수가 insmod 명령의 실행 컨텍스트 내에서 실행되기 때문에 현재 태스크는 insmod이다. current 기호는 실제로 get_current 함수를 참조하며 아키텍처 관련 헤더(예: ./linux/include/asm-i386/current.h)에서 찾을 수 있다.




위로


프로세스 생성

시스템 호출 함수

시스템 호출에는 하나의 패턴이 있다. 많은 경우, 시스템 호출은 sys_*라는 이름을 가지고 있으며 호출을 구현한 일부 초기 기능(오류 검사 또는 사용자 공간 활동)을 제공한다. 실제 작업은 do_*라는 이름의 다른 함수에 위임되기도 한다.

이제 사용자 공간에서 프로세스를 생성하는 과정을 살펴보자. 사용자 공간 태스크와 커널 태스크 모두 기본 메커니즘은 do_fork라는 함수를 사용하여 새 프로세스를 생성한다는 점에서 동일하다. 커널 스레드를 생성하는 경우, 커널은 일부 초기화 작업을 수행하는 kernel_thread 함수(./linux/arch/i386/kernel/process.c 참조)를 호출한 다음 do_fork를 호출한다.

사용자 공간 프로세스를 생성하는 경우에도 비슷한 작업이 수행된다. 사용자 공간에서 프로그램은 fork를 호출하게 되며, 이로 인해 sys_fork라는 커널 함수(./linux/arch/i386/kernel/process.c 참조)에 대한 시스템 호출이 발생한다. 그림 1에서는 이러한 함수들 간의 관계를 보여 준다.


그림 1. 프로세스 생성과 관련된 함수 계층 구조
프로세스 생성과 관련된 함수 계층 구조

그림 1을 보면 do_fork가 프로세스 생성의 기초라는 것을 알 수 있다. do_fork 함수는 파트너 함수인 copy_process 함수와 함께 ./linux/kernel/fork.c에 있다.

do_fork 함수는 먼저 새 PID를 할당하는 alloc_pidmap을 호출한다. 그런 다음 do_fork는 디버거가 상위 프로세스를 추적하고 있는지 여부를 확인한다. 추적하고 있을 경우에는 분기를 준비하기 위해 CLONE_PTRACE 플래그가 clone_flags에 설정된다. 그런 다음 do_fork 함수는 copy_process를 호출하여 플래그, 스택, 레지스터, 상위 프로세스 및 새로 할당된 PID를 전달한다.

copy_process 함수에서는 새 프로세스가 상위 프로세스의 사본으로 생성된다. 이 함수는 나중에 처리되는 프로세스 시작을 제외한 모든 작업을 수행한다. copy_process의 첫 번째 단계에서는 CLONE 플래그의 유효성을 검증하여 플래그의 일관성을 확인하는 작업이 수행된다. 일관되지 않을 경우에는 EINVAL 오류가 리턴된 후 LSM(Linux Security Module)을 통해 현재 태스크에서 새 태스크를 생성할 수 있는지 여부가 확인된다. SELinux(Security-Enhanced Linux) 컨텍스트에서의 LSM에 대한 자세한 설명은 참고자료 섹션에서 볼 수 있다.

그런 다음 dup_task_struct 함수(./linux/kernel/fork.c에 있음)가 호출되면서 새 task_struct를 할당하고 현재 프로세스의 디스크립터를 복사한다. 새 스레드 스택이 설정되면 일부 상태 정보가 초기화되고 copy_process에게 제어가 돌아온다. copy_process로 돌아오면 새 task_struct에 대한 다양한 초기화를 비롯한 여러 가지 제한 및 보안 검사와 함께 일부 설정 작업이 수행된다. 그런 다음 열려 있는 파일 디스크립터를 복사하고(copy_files), 신호 정보를 복사하고(copy_sighandcopy_signal), 프로세스 메모리를 복사하고(copy_mm) 마지막으로 스레드를 복사하는(copy_thread) 일련의 copy 함수가 호출되면서 프로세스의 개별 특성이 복사된다.

그런 다음 새 태스크가 프로세서에 할당된다. 이때 해당 프로세서에서 프로세스를 실행할 수 있는지에 대한 추가 검사(cpus_allowed)가 수행된다. 새 프로세스에게 상위 프로세스의 우선 순위가 상속되면 약간의 추가 설정 작업이 수행된 후 do_fork에게 제어가 돌아온다. 이 시점에서 새 프로세스는 존재하기는 하지만 아직 실행되지는 않고 있다. do_fork 함수에서 wake_up_new_task가 호출되면서 이 문제가 해결된다. 이 함수는 ./linux/kernel/sched.c에 있으며 일부 스케줄러 설정 정보를 초기화하고 새 프로세스를 실행 큐에 넣은 다음 실행할 수 있도록 깨운다. 마지막으로 do_fork로 돌아오면 PID 값이 호출자에게 리턴되고 프로세스가 완료된다.




위로


프로세스 스케줄링

프로세스는 Linux에 존재하는 동안 잠재적으로 Linux 스케줄러를 통해 스케줄링될 수 있다. 이 기사의 범위를 벗어나기는 하지만 Linux 스케줄러는 task_struct 참조가 존재하는 각 우선 순위 레벨에 대한 목록 세트를 유지한다. 태스크는 로드 및 이전 프로세스 실행 히스토리를 기반으로 실행하기에 가장 적합한 프로세스를 결정하는 schedule 함수(./linux/kernel/sched.c에 있음)를 통해 호출된다. Linux 버전 2.6 스케줄러에 대한 자세한 설명은 참고자료에서 볼 수 있다.




위로


프로세스 소멸

프로세스 소멸은 정상적인 프로세스 종료, 신호 또는 exit 함수 호출과 같은 여러 가지 이벤트에 의해 발생할 수 있다. 하지만 프로세스 종료가 발생하면 프로세스는 커널 함수 do_exit(./linux/kernel/exit.c에 있음)에 대한 호출을 통해 종료된다. 그림 2에서 이 프로세스를 보여 준다.


그림 2. 프로세스 소멸과 관련된 함수 계층 구조
프로세스 소멸과 관련된 함수 계층 구조

do_exit는 공유되지 않은 모든 리소스에 대한 현재 프로세스의 모든 참조를 운영 체제에서 제거하기 위해 사용한다. 소멸 프로세스는 먼저 PF_EXITING 플래그를 설정하여 해당 프로세스가 종료 중임을 나타낸다. 프로세스가 제거되는 동안 커널의 다른 프로세스는 이 표시를 보고 해당 프로세스를 조작하지 않게 된다. 프로세스를 실행하는 동안 포함된 다양한 리소스를 프로세스에서 분리하는 주기가 exit_mm(메모리 페이지 제거) 및 exit_keys(스레드별 세션 및 프로세스 보안 키 삭제)를 포함한 일련의 호출을 통해 수행된다. do_exit 함수에서 프로세스 삭제와 관련된 다양한 계정 작업이 수행된 후 exit_notify를 통해 일련의 통지(예: 상위 프로세스에게 신호를 보내 하위 프로세스의 종료를 알림)가 수행된다. 마지막으로 프로세스 상태가 PF_DEAD로 변경된 후 실행할 다음 프로세스를 선택하기 위해 schedule 함수가 호출된다. 상위 프로세스에게 신호를 보내야 하거나 프로세스가 추적되고 있는 경우에는 태스크가 완전히 사라지지 않는다. 신호를 보내지 않아도 되는 경우에는 release_task 호출을 통해 프로세스에 사용된 메모리가 실제로 회수된다.




위로


추가 주제

Linux는 진화를 거듭하고 있으며 앞으로 프로세스 관리 분야에서 혁신적인 기능과 최적화된 성능을 보여 줄 것이다. Linux는 UNIX 원칙을 충실히 따르면서도 계속해서 경계를 넓혀가고 있다. 새로운 프로세서 아키텍처, SMP(Symmetrical Multiprocessing), 및 가상화가 커널의 프로세스 관리 분야에서 새로운 발전을 이끌 것이다. 한 가지 예로, Linux 버전 2.6에서 도입된 새 O(1) 스케줄러는 많은 수의 태스크를 처리하는 시스템에 필요한 확장성을 제공한다. 또 다른 예로, NPTL(Native POSIX Thread Library)을 사용하는 업데이트된 스레딩 모델을 사용하면 이전 LinuxThreads 모델에 비해 효율적인 스레드 작업이 가능하다. 이러한 혁신과 개선된 사항에 대한 자세한 정보는 참고자료에서 볼 수 있다.



참고자료

교육

제품 및 기술 얻기
  • developerWorks에서 직접 다운로드할 수 있는 IBM 시험판 소프트웨어를 사용하여 Linux와 관련된 후속 개발 프로젝트를 구현해 볼 수 있다.


토론


필자소개

M. Tim Jones

M. Tim Jones는 임베디드 펌웨어 아키텍트이자 Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming(현재 2판), AI Application Programming(현재 2판) 및 BSD Sockets Programming from a Multilanguage Perspective의 저자이다. 정지 위성을 위한 커널 개발에서 시작해 임베디드 시스템 아키텍처와 네트워크 프로토콜 개발에 이르기까지 다양한 분야에 대한 공학 지식을 가지고 있다. 콜로라도주 롱몬트 소재의 Emulex Corp.에서 컨설턴트 엔지니어로 활약하고 있다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


IBM, IBM 로고, ibm.com, DB2, developerWorks, Lotus, Rational, Tivoli 및 WebSphere는 미국 또는 기타 국가에서 사용되는 International Business Machines Corporation의 상표 또는 등록 상표이다. 이와 함께 기타 IBM 상표가 기재된 용어가 상표 기호(® 또는 ™)와 함께 이 정보에 처음 표시된 경우, 이와 같은 기호는 이 정보를 발행할 때 미국에서 IBM이 소유한 등록 상표 또는 일반 법적 상표이다. 또한 이러한 상표는 기타 국가에서 등록 상표 또는 일반 법적 상표이다. 현재 IBM 상표 목록은 웹 "저작권 및 상표 정보"에 있다. Linux는 미국 또는 기타 국가에서 사용되는 Linus Torvalds의 상표이다. UNIX는 미국 또는 기타 국가에서 사용되는 The Open Group의 등록 상표이다. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의