

스레드 단위로 관리되는 구조체로, 각 스레드의 문맥과 제어 정보를 저장.
- 하나의 PCB(Process Control Block)에 여러 TCB가 있을 수 있다.
(즉, 프로세스 내의 스레드 단위로 관리.)

* 위 그림은 Linux에서의 TCB와 PCB의 구조이다.
/* threads/thread.c */
struct thread {
/* Owned by thread.c. */
tid_t tid; /* Thread identifier. */
enum thread_status status; /* Thread state. */
char name[16]; /* Name (for debugging purposes). */
int priority; /* Priority. */
int init_priority; /* 해당 스레드의 '원래' Priority. */
int nice; /* nice값: 클수록 CPU 양보 ↑ */
int recent_cpu; /* 해당 스레드가 최근 얼마나 많은 CPU time을 사용 했는지 */
struct list donations; /* 해당 스레드가 가지고 있는 lock을 필요로 하는 스레드들 */
struct list_elem d_elem; /* donations 리스트를 쓰기 위한 리스트 요소 */
struct lock * wait_on_lock; /* 해당 스레드가 기다리고 있는 lock을 가리키는 포인터 */
int64_t wake_tick; /* 스레드가 깨어날 시각*/
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
struct list_elem allelem; /* List element. */
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint64_t *pml4; /* Page map level 4 */
/* Project 2: System Call 구현 */
int exit_status; // exit 상태를 나타내는 정수형 변수
struct file **fdt; // fd 테이블
int fd_idx; // fd 인덱스
struct file *runn_file; // 실행중인 file
struct intr_frame parent_if; // 해당 스레드의 interrupt frame
struct list child_list; // 자식 프로세스 리스트
struct list_elem child_elem; // 자식 프로세스 리스트 요소
struct semaphore fork_sema; // fork가 완료될 때 sgnal
struct semaphore exit_sema; // 자식 프로세스 종료 signal
struct semaphore wait_sema; //exit_sema를 기다릴 때 사용
#endif
/* Owned by thread.c. */
struct intr_frame tf; /* Information for switching */
unsigned magic; /* Detects stack overflow. */
};
위에서 얘기했던 스레드 ID, 상태 정보, 스택 포인터, 레지스터 상태, 우선 순위 등은 모두 struct thread 자료구조에 포함된다.
사실상 struct thread가 TCB에 해당하는 정보들을 가지고 있고, 스레드가 스케줄링 되고 실행 상태가 변화하면서 스레드 간 Context Switching 동작을 수행한다.
프로세스 단위로 관리되는 구조체로, 각 프로세스의 상태와 자원 정보를 저장.
- PCB는 스레드 단위의 정보를 포함하는 TCB를 관리하며, 프로세스 자원을 책임짐.

* 위 그림은 Linux에서의 TCB와 PCB의 구조이다.
우리가 작성하고 있는 PintOS는 기본적으로 단일 스레드 프로세스를 전제한다.
즉, 멀티 스레딩을 지원하지 않는다.
→ 스레드의 관리 (TCB)와 프로세스의 관리(PCB)를 구분할 필요가 크지 않다!
따라서, 단일 자료구조인
struct thread만으로 TCB와 PCB를 분리하지 않고 구현이 가능하다.
보통 멀티스레드를 지원하는 시스템에서 각 스레드가 TCB를 통해 관리되고, 이러한 스레드가 속한 프로세스는 PCB를 통해 관리된다.
PintOS와 같은 프로젝트에서는 struct thread로 이러한 구조를 단순화 시키는 것이 효율적이다!
/* include/threads/interrupt.h */
struct intr_frame {
/* intr-stubs.S의 intr_entry에 의해 푸시됨.
중단된 작업의 저장된 레지스터입니다. */
struct gp_registers R; // 정수 레지스터 구간
uint16_t es; // Extra Segment - Extra Data 영역
uint16_t __pad1;
uint32_t __pad2;
uint16_t ds; // Data Segment - 데이터 영역
uint16_t __pad3;
uint32_t __pad4;
/* intr-stubs.S의 intrNN_stub에 의해 푸시됨. */
uint64_t vec_no; /* Interrupt vector number. */
/* 때로는 CPU에 의해 푸시되고, 그렇지 않으면 일관성을 위해 intrNN_stub에 의해 0으로 푸시됩니다.
CPU는 이를 'EIP' (Extended) Instruction Pointer 바로 아래에 두지만 우리는 여기로 옮깁니다. */
uint64_t error_code;
/* CPU에 의해 푸시됨.
중단된 작업의 저장된 레지스터입니다.. */
uintptr_t rip; // Instruction Pointer = Program Counter 다음에 실행될 명령의 주소
uint16_t cs; // Code Segment - 명령어 영역
uint16_t __pad5;
uint32_t __pad6;
uint64_t eflags; // Extended Flags - 상태, 제어, 시스템 플래그 -> 레지스터가 어떤 일을 수행하는 지
uintptr_t rsp; // Stack Pointer - 스택 포인터
uint16_t ss; // Stack Segment - 임시 Stack 영역
uint16_t __pad7;
uint32_t __pad8;
} __attribute__((packed));
Interrupt 또는 System Call 발생 시 CPU의 실행 상태(레지스터, 스택 포인터, 플래그 등)를 임시 저장.
- Interrupt, System Call이 발생했을 때, 기존의 실행 상태를 보존하여 작업이 끝난 후 복구할 수 있게 한다.
rax, rbx, rsp, rip 등.rflags).cs, ss 등.→ Interrupt, System Call 발생 시 레지스터 상태 임시 저장 및 복원 (의미, 개념론적인 interrupt frame으로 사용)
→
struct thread구조체 안에 위치하면서, 스레드 간 문맥 전환이 발생할 때에도 레지스터 상태 정보를 저장. (레지스터 상태를 저장하는 자료구조로서 사용)" 스레드의 레지스터 정보도 저장해야하는데, 이를 어떻게 저장할까? "
" → 이미 레지스터를 저장할 수 있는 구조체가 있으니, 이를 재사용하자! "
/* threads/thread.h */
struct thread {
/* Owned by thread.c. */
tid_t tid; /* Thread identifier. */
enum thread_status status; /* Thread state. */
char name[16]; /* Name (for debugging purposes). */
int priority; /* Priority. */
int origin_priority; //* 본래 priority
...
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint64_t *pml4; /* Page map level 4 */
/* Project 2: System Call 구현 */
int exit_status; // exit 상태를 나타내는 정수형 변수
struct file **fdt; // fd 테이블
int fd_idx; // fd 인덱스
struct file *runn_file; // 실행중인 file
struct intr_frame parent_if; // interrupt frame
struct list child_list; // 자식 프로세스 리스트
struct list_elem child_elem; // 자식 프로세스 리스트 요소
struct semaphore fork_sema; // fork가 완료될 때 sgnal
struct semaphore exit_sema; // 자식 프로세스 종료 signal
struct semaphore wait_sema; //exit_sema를 기다릴 때 사용
#endif
...
/* Owned by thread.c. */
struct intr_frame tf; /* Information for switching */
unsigned magic; /* Detects stack overflow. */
};
parent_if, tf가 말하자면 각각 interrupt frame, thread frame.if는 system call의 처리나 interrupt의 처리에 사용tf는 thread 간 context switch에 사용
- PintOS에서 TCB, PCB라는 이름은 사용되지 않지만, 스레드 관리를 위해
struct thread라는 자료구조를 사용하며, 이는 TCB의 역할을 수행한다.
- 이 때, 우리가 구현한 PintOS는 기본적으로 단일 스레드 프로세스를 전제하기 때문에,
struct thread만으로 사실상 PCB의 역할까지도 구현할 수 있다.
- Interrupt Frame은 스레드 간 Context Switch가 아닌 Interrupt, System Call 등 예외 상황이 발생했을 때 커널과 사용자 모드 간 전환을 지원하기 위해 사용된다.
but! PintOS 안의 thread 정보를 저장할 때 어차피 레지스터 정보도 저장해야 하므로,interrupt frame을struct thread안에서도 재사용 한다.
struct thread 자료구조에 사용자 프로그램을 실행할 스레드 정보를 포함. → 프로세스 생성 시 해당 스레드에 프로그램의 실행 파일 적재struct thread에 저장하고, 다음 실행할 스레드의 상태를 복원해 실행. (즉, Context Switch) → Context Switch 이후 interrupt handler는 intr_frame을 복구하고 프로그램의 실행을 재개.
- [WIL] PintOS 프로젝트와 함께 알아보는 병행성과 동기화 도구: Semaphore, Lock, Mutex, Condition Variable, Monitor
https://velog.io/@takealittletime/WIL-PintOS-프로젝트와-함께-알아보는-병행성과-동기화-도구-Semaphore-Lock-Mutex-Condition-Variable-Monitor