안녕하세요,
커널주중 E조 첫 주 스터디 진행 시 tgid 및 pid개념 정리에 2주간 질문이 남아 안목이 있으신 분들의 의견을 들어보고 싶어 질문드립니다.
저희 쪽에서는 당연하게도 2가지 접근이 있었는데, 아래 두가지 방향 중 어느 방향이 맞는 해석일까요?
-------------
- - task_struct의 pid 필드는 우리가 알고 있는 PID(Process ID) 개념
PID를 얻기 위해 사용하는 getpid()는 pid를 가져옴. tgid는 실제 PID와는 다르게 개념상 존재하는 필드로 보임.
내지는 pid는 우리가 아는 PID 개념대로 프로세스는 유니크하게, 쓰레드는 부모의 pid와 같이 정해지며, 쓰레드 id구분을 위해 tid라는게 따로 있음. (-> 이 부분은 저도 task_struct구조를 못 봐서 아직은 잘 모르겠습니다.)
뭐 어쨌거나 tgid=PID가 아님! 다른 개념임!
- - task_struct의 pid 필드는 우리가 알고 있는 PID와 다르며, tgid 필드가 PID 개념
PID를 얻기 위해 사용하는 getpid()는 tgid를 가져오는 함수임. pid필드는 우리가 아는 PID 개념에서 특별히 중요한 개념은 아닌 듯.
-------------
2주차 당시 제가 이해한 바는 아래와 같았습니다.
- - 먼저, 기존에 가지고 있던 PID에 대한 지식은 완전 배제 (아직 PID라는 개념은 Process ID가 아니다)
- - task_struct 구조체가 생성될때마다 pid(unique한 key)가 생성됨
- - POSIX Standard에서 정한 바, 생성된 task가 프로세스일 경우 {tgid}={pid}, 쓰레드일 경우 {tgid}={부모의 pid}로 정의
따라서 pid는 1부터 생성된다는 가정 하에, Process A->Process B->Thread A' (Process A에서 생성) 순서로 task_struct 생성 시
- - Process A (pid=1, 프로세스로 생성되었으므로 tgid=pid=1)
- - Process B (pid=2, 프로세스로 생성되었으므로 tgid=pid=2)
- - Thread A' (pid=3, 쓰레드로 생성되었으므로 tgid=부모 pid=1)
getpid()는 tgid를 리턴하므로, Process A, Thread A'는 1, Process B는 2를 리턴하게 된다고 이해했습니다.
이 과정에 따라 tgid는 POSIX Standard에서 정의하는 PID 개념과 equivalent하여 리눅스 시스템의 실제 PID로 차용하게 된 것 같습니다.
기존 pid는 그냥 task_struct 생성 시 나온 unique한 값이라는 의미밖에 없어 보이는 것 같습니다. 굳이 알려고 한다면 gettid라는 함수나 별도의 시스템콜을 이용하여 get하는 것 같습니다. (정확한 인용을 못한건 양해 부탁드립니다.)
------------
추가 질문으로
- - 한 프로세스를 한 개의 쓰레드로 봐야하는가, 아니면 한 프로세스는 별도로 한 개의 쓰레드를 가지는가?
스터디 중에는 해석하기 나름이라고 생각했으나 끝나고 개인적으로 다시 생각해 봤을 땐 그냥 프로세스나 쓰레드는 별개의 개념인 것 같았습니다. 제 생각에 프로세스나 쓰레드나 리눅스에서는 task_struct로 각각 독립적으로 표현되고 작업단위로서 스케줄링당하므로 프로세스가 별도로 메인 쓰레드 하나를 가지는 건 아닌 것 같습니다.
덧붙여 2주밖에 안 됐지만 엉뚱한 질문에도 잘 참고 넘어가 주시는 팀원분들께 감사드립니다.
++덧, 게시글에는 서식이 안 먹네요ㅠ
댓글 2
.
안녕하세요. 커널14기 B팀 이한길이라 합니다.
커널내에서는 프로세스든 쓰레드든 task_struct 구조체를 통해 구현되므로, 커널단에서는 한 쓰레드가 한 Task 라고 생각하면 되지 않을까 싶습니다. 그것을 tgid가 같은 쓰레드의 모임을 유저단에서 pid라고 칭하는 거라고 생각합니다.
즉 프로세스나 쓰레드는 태스크의 공유측면에서 개념적으로만 존재한다고 보면 되지 않을까 싶습니다. 글쓴이님꼐서 생각하신 개념이랑 비슷하게 이해했습니다.
최초에 task가 하나 생성된다면, 1프로세스 == 1쓰레드 == 1task 가 되겠지만, 그 task 내에서 또 한개의 task를 쓰레드로 생성한다면, 1프로세스 == 2쓰레드 == 2tasks 가 되겠습니다.
물론 공유가 없이 fork 등으로 생성한다면 2프로세스 == 각각 2개쓰레드 == 각각 2개 tasks 가 되겠습니다.
윈도우는 확실히 이와는 다른게, EPROCESS, ETHREAD 와 같이 커널 구조체가 프로세스와 쓰레드를 가리키는 구조체들이 존재합니다. 이는 커널단에서 그 존재를 확실히 분류했으므로, 한 프로세스 안에 한 쓰레드가 존재한다고 봐도 좋겠죠.
끝으로 일반적으로 리눅스 코딩에서 getpid() 를 하면 프로세스의 id 라고 하여 pid가 리턴이 됩니다.
이 pid는 만약 프로세스가 1개의 task만을 가진다면, 그 task의 task_struct 가 가진 pid(task_struct->pid)와 같은 값을 가지게 됩니다. 왜냐면 getpid()를 통해 리턴되는 pid 는 공유되는 task_struct->tgid 와 일치하는데, 1개 task이기 때문에 공유하는 task_struct->tgid도 task_struct->pid와 일치하기 때문입니다.
제가 몰랐던 것은, 그렇다면, 쓰레드를 생성했을 경우, 이 쓰레드의 pid() 는 무엇을 호출해 줄 것인가인데, 65쪽에 SYSCALL_DEFINE0(getpid) 를 보면, 이 함수는 단순히 current 의 task_tgid_vnr , 즉 task_struct 의 tgid 값을 리턴해 주는 함수임을 알 수 있습니다. 이 tgid는 61쪽을 보면, 프로세스를 생성할 경우에는 새로운 생성된 task의 pid값으로 세팅해주며, 스레드를 생성할 경우에는, 부모쓰레드의 tgid값, 즉 fork로 생성이 되서 tgid 와 pid가 유일한 task(process)에서 쓰레드를 생성했다면, 이 유일한 tgid 가 생성된 쓰레드로 복사되고, pid는 유일하게 생성됩니다.
따라서 tgid ==> 즉 getpid() 는 생성된 쓰레드집합 프로세스의 아이디인 것입니다. 그것을 각 예제에서는 TGID(xx) 라고 표기하였습니다. 엄밀히 말하면 ps -ef 에 출력되는 pid입니다.
또한 예제에 출력된 PID들은 단순히 task로써 유일한 값을 가지는 task_struct 의 pid입니다.
예제에서는 출력할 때, struct_task 의 양식을 따랐을 뿐이고,
프로세스로서 실제 가지는 pid는 ps -ef 에 나오는 값과 일치하며, 이 값은 한 프로세스-태스크의 pid, tgid 값과 일치하고, 그 태스크가 생성한 모든 공유 쓰레드의 tgid 값과 일치하게 됩니다.
쓰레드그룹아이디==thread group id==tgid==1 process(sharing multiple threads) id == getpid() == ps -ef 의 id
ps -ef 를 하면 각 쓰레드는 숨겨지고 하나의 아이디만 나오므로 논리적으로 생각하면 이렇게 될 수밖에 없습니다.
그러므로 원래 우리가 알고 있는 pid와 투영해 보면, 2번째 개념이 맞다는 것을 알 수 있으며, 그렇다고 task_struct->pid 가 중요하지 않은건 아니라고 봅니다. 커널단에서 task를 제어하기 위해서 유일한 번호가 필요하고, 또 유저단에서도 쓰레드를 번호로 제어하기 위해 필요할 수도 있겠죠, 물론 리눅스 코딩을 많이 안해봐서 쓰레드를 번호로 뭔가를 제어해야 할 일이 있는지는 모르겠지만요..