ㅍㅍㅋㄷ

Linux namespace - PID 본문

IT/Linux

Linux namespace - PID

클쏭 2015. 7. 14. 14:53

PID namespace






 최근 lightweight 한 가상화 플랫폼인 Docker나 Linux container LXC 가 주목을 받고 있는데, 여기에 사용된 기반 기술 중 하나로 namespace 라는 것이 있다. namespace 에 대한 설명은 이전 포스팅 참고 ( Linux namespace ) 


 namespace 는 크게 6가지로 분류되는데, 이번 포스팅에는 PID namespace에 대해 알아볼 예정이다. 


1. UTS namespace : hostname 을 변경하고 분할

2. IPC namespace : Inter-process communication. 프로세스간 통신 격리

3. PID namespace : PID (Process ID)를 분할 관리

4. NS namepsace : file system 의 mount 지점을 분할하여 격리

5. NET namespace : Network interface, iptables 등 network 리소스와 관련된 정보를 분할

6. USER namespace : user와 group ID를 분할 격리




Linux Process ID (PID) 와 PID 1


 Linux 에서 실행되는 모든 process들은 각각에 고유한 process ID(PID) 가 부여된다. kernel 에서는 이 Process들을 Tree 형태로 관리한다.

 Tree 형태라는 의미는 모든 process 가 부모-자식 관계 (parent-child hierarchy) 를 가지고 있다는 의미이다. 

 

 root@~~# pstree

init─┬─acpid

     ├─atd

     ├─cron

     ├─dbus-daemon

     ├─dhclient

     ├─7*[getty]

     ├─rsyslogd───3*[{rsyslogd}]

     ├─sshd─┬─sshd───sshd───bash───sudo───su───bash───vim

     │      ├─sshd───sshd───bash───sudo───su───bash

     │      └─sshd───sshd───bash───sudo───su───bash───pstree

     ├─systemd-logind

     ├─systemd-udevd

     ├─upstart-file-br

     ├─upstart-socket-

     └─upstart-udev-br

[ pstree 로 확인해 본 process 관계 ]

 

 위에서 보이는 것 같이, 모든 Process의 최상단에 위치한 process가 존재하게 되는데 이것을 init process 라고 한다. 

 시스템에서 생성되는 모든 process들은 init process에 의해 실행되어 지며, init process에게는 유일하게 PID가 "1" 이라는 숫자를 할당 받게 된다.  


 즉,  PID 1 = init 이다. 



 PID namespace를 설명하기 전에 먼저 PID가 1인 init process에 대해 설명한 이유는,

 PID namespace가 갖게 되는 가장 큰 특징이 바로 init process 만이 가질수 있는 PID 1 을 독립적으로 추가 할당해 준다는 것이다. 


[ PID namespace ]



 위 그림을 보면 이해가 빠를 것이다. 

 4번 Process를 parent로 하는 6번 Process가 PID namespace로 구성 되게 된다고 가정하면, 

 6번 Process는 origin 역할을 하는 1번 root Process ID를 부여 받게 될 것이다.

 그리고 해당 namespace 내부에서 실행되는 process들은 기존 PID 할당순서와 별개로 PID를 부여 받게 된다.  


  child namespace에서 PID 1을 할당 받았다고 해서 이 process 가 init process는 아니다. 그러나 그 역할은 init process와 비슷하게 수행되는 부분이 많다. 

 예를 들면, 위 그림에서 만약 8(3)번 process 가 죽게 되면, 9(4)번 process 는 parent가 없는 고아(Orphaned process) 가 되는데, 이때 자동으로 6(1)번 process가 parent가 되는 child reaping 이 일어난다. 본래는 init process가 담당하지만, PID namespace 내부에서는 PID 1 process가 이렇게 init 역할을 대신한다.    




Clone을 이용한 PID namespace 구성 방법


PID namespace를 생성하는 방법은 여타 namespace를 구성하는 방법과 다르지 않다. 

clone() 생성시 flag로 CLONE_NEWPID 만 추가해 주면 된다. 


int child_pid = clone(child_main, child_stack+STACK_SIZE, CLONE_NEWPID | SIGCHLDNULL);


아래 소스코드를 보자.

#define _GNU_SOURCE

#include <sys/types.h>

#include <sys/wait.h>

#include <stdio.h>

#include <sched.h>

#include <signal.h>

#include <unistd.h>


#define STACK_SIZE (1024 * 1024)


static char child_stack[STACK_SIZE];

char* const child_args[] = {

  "/bin/bash",

  NULL

};


int child_main(void* arg)

{

  printf(" - [%4d] Current namspace, Parent PID : %d\n", getpid(), getppid() );

  sethostname("namespace", 12);

  execv(child_args[0], child_args);

  return 1;

}


int main()

{

  printf(" - [%4d] New namespace, Parent PID : %d\n", getpid(), getppid() );

  int child_pid = clone(child_main, child_stack+STACK_SIZE, CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | SIGCHLD, NULL);

  waitpid(child_pid, NULL, 0);

  return 0;

}


clone()시 PID 외에 UTS와 IPC도 flag로 추가하였다. 

child process를 생성시 PID를 출력하여 보고, 그 PID의 parent PID도 함께 출력해 보는 예제이다. 


결과는 아래와 같다. 


root@~~# ./newpid

 - [5631] Current namespace, Parent PID : 5578

 - [      1] New namespace, Parent PID : 0

root@namespace:~# 



  clone()을 실행하기 전 PID는 5631 였는데, CLONE_NEWPID flag로 생성한 namespace 의 PID는 1로 설정되었다. 또한 이 namespace의 parent는 0으로 마치 init process와 동일한 성격을 지녔다는 것을 알 수 있다. 

  

  이때, 새로운 namespace에서 본래 parent였던 PID인 5631을 인식할 수 있을까?


root@namespace:~# kill -9 5631

bash: kill: (5631) - No such process


  위와 같이 새로운 namespace는 PID 관리 영역이 분리 되었으므로 PID 5631 을 못찾는다고 응답한다. 

  그렇다면, PID 관리 영역이 완전히 분리 되었다면, 새로운 namespace에서는 자신이 관리 하는 영역의 Process만 보여야 할것이다. 그래야만 진정한 isolation 이 구현되었다고 할 수 있다.  하지만 결과는 그렇지 않다. 


새로운 namespace에서 process tree를 살펴보면?


root@namespace:~# pstree -p

init(1)─┬─acpid(936)

        ├─atd(939)

        ├─cron(938)

        ├─dbus-daemon(751)

        ├─dhclient(600)

        ├─getty(879)

        ├─getty(882)

        ├─getty(886)

        ├─getty(887)

        ├─getty(889)

        ├─getty(1058)

        ├─getty(1059)

        ├─rsyslogd(788)─┬─{rsyslogd}(789)

        │               ├─{rsyslogd}(790)

        │               └─{rsyslogd}(791)

        ├─sshd(933)───bash(5578)───newpid(5631)───bash(5632)───pstree(5650)

        ├─systemd-logind(779)

        ├─systemd-udevd(451)

        ├─upstart-file-br(813)

        ├─upstart-socket-(585)

        └─upstart-udev-br(446)

 

격리 생성한 PID namespace에서 확인하였는데도 불구하고, 모든 process tree 가 보인다. 

게다가 새롭게 할당받은 PID 1 번은 확인조차 되지 않는다. (PID 5632가 새로 할당받은 PID 1 이다)


하지만 이것은 bug가 아니다. 아직 우리가 완전히 namespace를 구현하지 않았을 뿐이다. 

실제 kernel 레벨에서는 PID가 분리되어 관리되고 있지만, 이 Process를 기록하고 관리하는 filesystem 영역인 /proc 에 대한 격리화를 하지 않았기 때문이다. 이 부분은 PID namespace 가 아닌 NS namespace 에서 다루도록 한다. 


NS (File system) namespace  바로가기



[참고]

  • http://www.toptal.com/linux/separation-anxiety-isolating-your-system-with-linux-namespaces
  • https://blog.jtlebi.fr/2014/01/05/introduction-to-linux-namespaces-part-3-pid/


'IT > Linux' 카테고리의 다른 글

Linux namespace - NET  (4) 2016.05.10
Linux namespace - NS (File System)  (0) 2016.05.04
Linux namespace - IPC  (0) 2015.07.13
Linux namespace - UTS  (2) 2015.07.03
Linux namespace  (0) 2015.07.02
Comments