1. Thread-based Server : process-based와 매우 유사하지만, process 대신 thread 사용

프로세스는 code, data, stack, heap 4가지를 가지며 각 프로세스마다 address space를 가짐
CPU에서 메인 메모리로 데이터를 보내주는 버스가 여러 가지 있는데
Addr bus, Control bus, Data bus가 있음
Instruction Execution Cycle : IF->ID->EX->WB
코드 영역에서 inst를 fetch해서 reg set에 저장하고 이 inst를 decoding, execution해서 다시 write back
그러고 다음 명령어를 다시 이 과정을 반복...
이런 걸 execution flow라고 하는데
Execution Flow(EF) : 실행하려는 명령어들의 sequence, 즉, Instruction Execute Sequence
프로세스 A에서 fork를 띄워서 자식 프로세스인 프로세스 B를 생성하면 process context 뿐만 아니라 EF도 복제됨
thread 관점에선 code, data, heap이 sharing되지만 stack은 sharing 되지 않음
스택은 thread마다 다름
thread는 하나의 프로세스에 여러 EF를 생성하여 concurrent하게 프로그램을 수행함
따라서 1 thread process는 EF가 하나, N thread는 여러 개의 EF를 가짐
address space는 프로세스 A와 B가 다르며 서로 코드를 공유하면서 EF를 여러 개 생성할 수 있음
(1) Traditional View of a Process
Process = process context + code, data, stack

process 관점에서 fork를 띄우면 자식 프로세스는 process context, code, data, stack 복사
(2) Alternate View of a Process
process = thread(stack + thread context) + code, data, kernel context(shared)

(3) A Process with multiple threads
- 여러 개의 thread는 프로세스와 연관
: 각 thread는 각자의 logical control flow를 가짐
: 각 thread는 같은 codem data, kernel context를 공유
: 각 thread는 logical 변수에 대한 각자의 스택이 있음
: 각 thread는 각자의 thread id가 있음 (TID)

1 thread process일 경우, 초록색으로 표시한 부분만 가짐
복제를 하면서 여러 thread를 갖게 되면 보라색 표시 부분을 sharing
(4) Logical view of threads
- 프로세스와 연관된 thread는 peer pool 생성

process는 thread와 다르게 계층구조의 tree를 가짐
(5) Concurrent Threads
- 2개의 thread는 동시간 대에 flow가 overlap되면 concurrent

노란색 부분 overlap -> A와 B Concurrent
초록색 overlap -> A와 C Concurrent
B와 C는 overlap되는 부분 없으므로 sequential

true parallelism을 가짐 -> 2 core에서 3개의 thread가 실행 (최대 cpu 개수만큼 ?????)
(6) Threads vs Processes
- similar
: 각자 자기의 logical control flow를 가짐
: 다른 core의 프로세스/thread와 concurrent하게 수행
: context switch됨
-different
: threads는 모든 코드와 데이터를 공유 (단, logical 스택 제외)
: 프로세스가 thread보다 프로세스를 생성하고 reaping할 때 2배의 overhead
(7) Threads (Pthreads) Interface
- Creating and reaping threads
: pthread_create() == fork()
: pthread_join() == wait
- Determining TID
: pthread_self() == getpid()
- Terminating threads
: pthread_cancel()
: pthread_exit()
: exit() -> 모든 thread를 종료
- Synchronizing access to shared variables
: pthread_mutex_init
: pthread_mutex_[un]lock
ex) The Pthreads "hello, world" Program
/*
* hello.c - Pthreads "hello, world" program
*/
#include "csapp.h"
void *thread(void *vargp);
int main()
{
pthread_t tid;
Pthread_create(&tid, NULL, thread, NULL); //EF의 또다른 EF 생성
Pthread_join(tid, NULL);
exit(0);
}
void *thread(void *vargp) /* thread routine */ ///생성된 thread가 함수 thread 실행
{
printf("Hello, world!\n");
return NULL;
}

main thread에서 pthread를 생성하고 리턴하면 context switch가 일어나면서 생성된 peer thread에서 printf로 hello world를 출력하고 리턴
Pthread_join 함수로 peer thread가 종료될 때까지 기다리고 종료돼서 리턴하면 exit하여 모든 thread를 종료
=> EF가 2개면 concurrent하게 실행됨
(8) Thread-Based Concurrent Echo Server
int main(int argc, char **argv)
{
int listenfd, *connfdp;
socklen_t clientlen;
struct sockaddr_storage clientaddr;
pthread_t tid;
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen=sizeof(struct sockaddr_storage);
connfdp = Malloc(sizeof(int));
// 메인 thread에서 생성되므로 메인 thread의 스택에 들어있음
*connfdp = Accept(listenfd, (SA *) &clientaddr, &clientlen);
Pthread_create(&tid, NULL, thread, connfdp);
// ex) echo일 경우, 새로운 fd에 해당하는 echo thread를 생성
// 밑의 thread함수에서도 connfd가 있음 -> main thread와 pthread가 main의 스택 변수인 connfd에 동시에 접근하게 되므로
// unintended sharing 발생
}
}
/* Thread routine */
void *thread(void *vargp)
{
int connfd = *((int *)vargp);
Pthread_detach(pthread_self());
// 다른 thread와 독립적으로 실행, thread가 종료했을 때 reaping을 kernel이 자동으로 함
Free(vargp);
echo(connfd);
Close(connfd);
return NULL;
}

main에서 accept함수를 통해 connfdp를 생성하고 peer thread를 생성하여 이 thread의 주소를 connfd가 갖고 있음
따라서 connfp로 heap에 peer thread이 접근할 수 있음
- detached mode
: 다른 thread와 독립적으로 수행
: thread가 종료되면 자동으로 커널에서 reaping
- connfd를 유지하기 위해 할당된 free storage가 있음
- connfd를 close해줘야 함 안그러면 메모리 누수 발생!!
(9) Thread-based Server Execution Model

- 각 클라이언트는 각 peer thread에 의해 처리
- thread들은 TID를 제외하고 모든 프로세스의 상태를 공유
- 각 thread들은 local 변수에 대한 분리된 스택을 가짐
(10) Thread-based Server 문제점
- 메모리 누수를 피하기 위해 detached 모드에서 run
: thread는 joinable / detached
: joinable thread는 다른 thread에 의해 reaping되거나 kill 될 수 있임
(메모리 리소스를 free 시키기 위해서 pthread_join 함수로 reaping)
: detached thread는 다른 thread에 의해 reaping되거나 kill 될 수 없음
(리소스는 종료시 자동적으로 커널에 의해 reaping됨)
: default 상태는 joinable
(detached 상태로 만들기 위해 pthread_self()함수나 pthread_detach 함수 이용)
- 의도치 않은 sharing을 피하기 위해 조심해야함
: ex) 메인 thread의 스택에서 pointer를 넘겨줄 때 (위의 예제에서 설명)
- thread에 의해 호출된 모든 함수들은 thread-safe 해야함
(11) Thread-based Server 장단점
- 장점
: thread 사이의 데이터를 공유하기 쉬훔
: thread는 프로세스보다 더 효율적
- 단점
: 의도치않은 sharing은 어려운 에러
-> 무슨 데이터가 공유되었고, 어떤 private data가 있는지 알기 어려움
-> 테스트로 발견하기 어려움
(12) 요약
- Process-based
: 리소스를 공유하기 어려움
: 의도치 않은 sharing을 피할 수 있음
- Event-based
: low level
: 스케쥴링에 대해 전체 제어
: 매우 낮은 overhead
: fine grained한 수준의 concurrency를 만들 수 없음
: 멀티 코어를 사용할 수 없음
- Thread-based
: 리소스를 공유하기 쉬움
: medium overhead
: 스케쥴링 규칙에 대해 제어가 별로 없음
: 디버깅이 어려움 (shared data -> 의도치 않은 bug 생성)
-> event의 순서를 반복할 수 없음
'SP' 카테고리의 다른 글
| [sp] Synchronization: Advanced (0) | 2022.05.05 |
|---|---|
| [sp] Synchronization: Basics (0) | 2022.05.03 |
| [sp] Concurrent Programming(1) (0) | 2022.04.11 |
| [sp] Network Programming (3) (0) | 2022.04.06 |
| [sp] Network Programming (2) (0) | 2022.04.06 |