Linux Foundation Training

[LFS201] Processes

Sara.H 2020. 6. 29. 18:40

프로세스 속성 

* 실행되고 있는 프로그램 

* Context (State) : 프로세스간 CPU 시간을 공유하려면 Context switching 이 이루어져야 한다. (커널의 중요한 역할 중 하나)

* Permissions 

* Associated Resources : 모든 프로세스는 메모리, 파일 핸들등이 할당되어 있다. 

 

Process Permissions 

어떤 유저가 실행시켰는지에 따라서 프로세스의 권한이 형성된다. 뿐만 아니라 어떤 유저가 프로그램 파일을 소유하는지에 따라서도 권한이 형성될 수 도 있다. "s" execute bit 으로 표시된 프로그램들은 real user id 와 다른 effective user id 를 갖고 있다. 이 프로그램들은 setuid 프로그램이라 지칭하며 프로그램을 소유하는 유저의 아이디를 갖고 실행된다. 반면, non-setuid 프로그램들은 그 프로그램을 실행하는 유저의 권한으로 실행된다. setuid 프로그램인데, 소유자가 root 인 경우 이것은 보안 문제로 간주된다. 

passwd 프로그램은 setuid 프로그램의 예시이다. 이 프로그램은 어떤 유저든 실행할 수 있다. 유저가 이 프로그램을 실행하면 프로세스는 root permission 을 갖고 실행되며, 이로써 쓰기가 제한된 파일에 쓰기를 할 수 있다. /etc/passwd 와 /etc/shadow 에 유저의 비밀번호가 유지된다. 

 

Process Resource Isolation 

프로세스가 시작되면 해당 프로세스는 자신만의 유저 공간에 고립되어 있다. 이는 시스템의 보안 향상과 안정성에 기여한다. 프로세스는 하드웨어에 직접적으로 접근할 수 없다. 하드웨어는 커널이 관리하므로 프로세스는 system call 을 사용해서 간접적으로 접근해야 한다. 

 

Controlling Processes with ulimit 

ulimit 을 사용해서 자원 한계를 지정할 수 있다. 만약 많은 클라이언트들을 핸들링 하는 서버가 있다 하면 열린 파일이 1024로 제한된 것이 약간 부족할 수 도 있으므로 제한을 늘리면 된다. 

 

두 가지 종류의 limit 

* Hard : 루트 유저만이 설정할 수 있는 최대값. 유저가 자원 리밋을 높일 수 도 있다. 

$ ulimit -H -n 

4096 

* Soft : 현재의 제한 값. 유저가 수정할 수 있지만 하드 리밋을 넘을 수는 없다. 

$ ulimit -S -n 

1024 

 

특정 리밋을 설정하는 방법 

$ ulimit [options] [limit] 

예를 들어 

$ ulimit -n 1600 

=> 파일 디스크립터 제한을 1600개로 늘릴 수 있다. 

 

변경 사항은 현재의 쉘만 영향을 받는다. 모든 로그인 된 유저들이 영향을 받으려면 /etc/security/limits.conf 파일을 수정하고 reboot 해야한다. 

 

Process State 

* Running : CPU를 사용중이거나 run queue 에 앉아있으면서 새로운 time slice 를 열심히 기다리는 상태. 

* Sleeping : IO 요청에 대한 응답을 기다리는 중. 요청이 완료되면 커널은 프로세스를 깨워서 다시 run queue 에 집어넣는다. 

* Stopped : 프로세스는 suspend 되어있다. 프로그래머가 디버깅할때 프로그램의 메모리, CPU 레지스터, 플래그, 다른 속성 등을 확인할때 사용됨. Ctrl-Z 하거나 디버거로 돌아갈때 이상태가 됨. 

* Zombie : 부모 프로세스가 자식을 거둬들이지 않았을때의 상태. 이런 프로세스를 defunct process 라 부르기도 한다. 좀비 프로세스는 모든 자원을 해제한 상태이지만 자신의 exit state 과 process table 에서의 자신에 대한 정보만은 해제를 못한 상태이다. 이런 프로세스는 init (PID = 1) 혹은 kthreadd (PID = 2)가 거둬들인다. 

 

Execution Mode 

특정 시점에 프로세스는 user mode 혹은 system mode (kernel mode) 하에서 돌아가고 있다. 어떤 명령어들이 수행될 수 있는지는 모드에 따라서, 하드웨어에 따라서 다르다. (소프트웨어 레벨이 아님)

모드는 시스템(소프트웨어)의 상태가 아니라 프로세서의 상태이다. 인텔에서 유저모드는 Ring 3 라 불리우고, 시스템모드는 Ring 0 라 불리운다. 

 

유저모드 

시스템콜을 실행할때 이외에 프로세스는 유저 모드에서 돌아간다.

프로세스가 시작되면 특정 유저의 공간에 고립되는 것은 process resource isolation 이라 한다. 유저 모드에서 돌아가는 프로세스는 자신만의 메모리 공간이 있고, 부분적으로 다른 프로세스와 공유될 수 있다. 

루트 유저가 실행하는 프로세스 혹은 유저 모드에서 돌아가는 setuid 프로그램도 시스템콜을 호출하면 반드시 모드 변경을 해야한다. 

 

시스템모드 (커널모드)

커널모드에서는 CPU는 주변장치, 메모리, 디스크 등 하드웨어에 대한 완전한 접근이 가능하다. 만약 어플리케이션이 이 자원들에 접근하려면 system call 을 호출해서 커널모드로의 context switch 를 일으켜야 한다. file 에 read, write 를 하거나 새로운 프로세스를 만드는 경우가 대표적이다. 

어플리케이션 코드는 절대 커널모드에서 돌아가지 않고, 오직 시스템콜 자체만 커널모드에서 돌아간다. 시스템콜이 완료되면 리턴 값이 생성되고, 프로세스는 context switch 를 다시 하여 유저 모드로 돌아간다. 

프로세스와 전혀 상관 없이 시스템이 커널 모드에 들어가는 경우가 있다. 예를 들어 하드웨어 인터럽트를 다룬다거나, 루틴을 스케줄링 한다거나, 시스템을 위해 여러 태스크들을 매니징 하는 경우들이 있다. 

 

Daemons : 백그라운드 프로세스 

* 필요할때만 불리울 수 있는 프로세스로 꽤나 유용함 

* 많은 daemon 들은 boot time 에 시작된다. 

* 보통 daemon 들의 이름은 d로 끝난다. 

* 예를들어, httpd, systemd-udevd 

* daemon 들은 외부 이벤트에 반응할 수 도 있고(systemd-udevd), 지나간 시간에 따라 반응할 수 있다.(crond)

* 보통은 표준 입출력 혹은 터미널을 제어할 수 없다. 

* 더 나은 보안 컨트롤을 제공한다. 

 

SysVinit 을 사용할 때 /etc/init.d 디렉토리는 여러개의 시스템 데이몬으로 시작된다. 이 스크립트는 daemon 이라는 쉘 함수의 매개변수로써 커맨드를 호출하고, 이는 /etc/init.d/functions 파일에 정의되어 있다. 

 

Creating Processes 

새로운 프로세스를 만드는 것을 forking 이라 부른다. 원래의 부모는 계속해서 돌아가고, 자식 프로세스가 시작된다. fork 다음에는 보통 exec 이 오는데, 이를 실행하면 부모가 종료되고 자식이 부모의 PID를 물려받으며 실행된다. 

 

(예시) 

sshd daemon 은 init 프로세스가 sshd init script 를 실행하고, 해당 스크립트로 sshd daemon 을 런칭하면서 시작된다. 이 daemon 은 원격 유저들로부터 ssh 요청을 듣는 프로세스이다. 

요청이 받아들여지면, sshd 는 요청을 처리할 자기 자신의 복사본을 만든다. 각 원격 유저당 이 복사본을 하나씩 갖게 되고, remote login 을 할 수 있게 된다. sshd 프로세스는 로그인 프로그램을 실행시켜 원격 유저를 확인하고, 만약 인증이 되면 로그인 프로세스는 shell 을 fork 하여서 유저의 명령어들을 해석한다. 

 

내부 커널 프로세스들은 여러가지 유지 작업을 하는데, 예를 들어 디스크로 버퍼가 flush 되는지 확인하고, CPU의 로드가 잘 분산되어 있는지 확인하며, 디바이스 드라이버가 큐된 작업들을 잘 처리하는지 등의 작업들이 있다. 이 프로세스들은 시스템이 running, sleeping 인 동안 계속 돌아간다. 

이 프로세스들은 커널이 시작했으나 일반 어플리케이션과 같이 유저 공간에서 돌아간다. 

확인하려면 다음의 명령어를 쳐보자. 

$ ps -elf 

 

이 프로세스들의 부모는 kthreadd (PPID = 2) 이고, 이 프로세스를 생성해내는 내부 커널 스레드들은 [ksoftirqd/0] 와 같이 브래킷 안에 이름이 써져있다.