30. 컴퓨터 시스템의 노트를 깊이 이해하고 프로그래밍(concurrent)(2)

5657 단어 Concurrent
1. 공유 변수
1) 스레드 스토리지 모델
스레드는 커널에서 자동으로 조정되며, 모든 스레드는 자신의 스레드 상하문 (thread context) 을 가지고 있으며, 유일한 정수 스레드 ID (Thread ID, TID), 창고, 창고 지침, 프로그램 카운터, 일반적인 목적 레지스터와 조건 코드를 포함한다.모든 스레드는 다른 스레드와 함께 프로세스 상하문의 나머지 부분을 공유한다. 전체 사용자의 가상 주소 공간을 포함한다. 이것은 텍스트(코드)만 읽고 데이터를 읽거나 쓰기, 더미와 모든 공유 라이브러리 코드와 데이터 구역으로 구성되며, 스레드도 같은 열린 파일의 집합을 공유한다.[1]
레지스터는 공유되지 않고 가상 메모리는 항상 공유됩니다.
The memory model for the separate thread stacks is not as clean.These stacks are contained in the stack area of the virtual address space, and are usually accessed independently by their respective threads. We say usually rather than always, because different thread stacks are not protected from other threads. So if a thread somehow manages to acquire a pointer to another thread’s stack, then it can read and write any part of that stack. 예제 29행에서where the peer threads reference the contents of the main thread's stack indirectly through the global ptr variable.
2) 스토리지에 변수 매핑
전역 변수와 마찬가지로 가상 메모리의 읽기/쓰기 영역은 프로그램에서 설명한 모든 로컬 정적 변수의 실례만 포함합니다.모든 스레드의 창고는 로컬 자동 변수의 실례를 포함합니다.
3) 변수 v는 공유된 것으로 그 실례가 하나 이상의 라인에 인용될 경우에만 적용된다.
예제 코드
/* $begin sharing */ #include "csapp.h" #define N 2 void *thread(void *vargp); char **ptr; /* global variable */ int main() { int i; pthread_t tid; char *msgs[N] = { "Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < N; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL); } void *thread(void *vargp) { int myid = (int)vargp; //cnt    , myid      static int cnt = 0; printf("[%d]: %s (cnt=%d)
", myid, ptr[myid], ++cnt); } /* $end sharing */

2. 신호량으로 동기화
같은 공유 변수, 여러 라인을 업데이트할 때 매번 업데이트가 있기 때문에 이 변수에 대해'레지스터에 불러오고 업데이트하면 메모리에 저장하고 쓰기'라는 과정이 있다. 여러 라인을 조작할 때 오차가 생기고 혼란스러운 상황이 발생하기 때문에 공유 변수를 보호하여 이 업데이트 작업이 원자성을 가지도록 할 필요가 있다.
신호량 s는 페이지 정수 값이 아닌 전역 변수로 P, V 조작이라는 두 가지 특수한 조작으로만 처리할 수 있다.
P(s):
  while (s <= 0); s--;
     V (s): s++;
    The P operation waits for the semaphore s to become nonzero, and then decrements it.The V operation increments s.
1) 기본 사상은 모든 공유 변수(또는 관련 공유 변수 집합)를 하나의 신호량 s(초기값 1)와 연결시킨 다음에 P(s), V(s) 조작으로 상응하는 임계구(코드 한 단락)를 포위하는 것이다.이 방법으로 공유 변수의 신호량을 보호하는 것을 2진 신호량(binary semaphore)이라고 하는데, 값이 항상 1, 0이기 때문이다.
The definitions of P and V ensure that a running program can never enter a state where a properly initialized semaphore has a negative value.
11.4.4posix 신호량에 대한 소개가 있습니다.
2) 2진법 신호량은 일반적으로 상호 배척 자물쇠라고 하는데 상호 배척 자물쇠에 P조작을 실행하는 것을 가쇄라고 하고 V조작을 잠금 해제라고 한다.상호 배척 자물쇠에 자물쇠를 채우고 잠금을 풀지 않은 라인을 점용 상호 배척 자물쇠라고 부른다.
3. 신호량으로 공유 자원을 조정한다.
이런 상황에서 한 라인은 신호량으로 다른 라인을 알리는데 프로그램 상태의 어떤 조건은 이미 진실이 되었다.예를 들어 생산자-소비자 문제.
예제 코드
#ifndef __SBUF_H__

#define __SBUF_H__



#include "csapp.h"



/* $begin sbuft */

typedef struct {

    int *buf;          /* Buffer array */         

    int n;             /* Maximum number of slots */

    int front;         /* buf[(front+1)%n] is first item */

    int rear;          /* buf[rear%n] is last item */

    sem_t mutex;       /* Protects accesses to buf */

    sem_t slots;       /* Counts available slots */

    sem_t items;       /* Counts available items */

} sbuf_t;

/* $end sbuft */



void sbuf_init(sbuf_t *sp, int n);

void sbuf_deinit(sbuf_t *sp);

void sbuf_insert(sbuf_t *sp, int item);

int sbuf_remove(sbuf_t *sp);



#endif /* __SBUF_H__ */

//source code

/* $begin sbufc */

#include "csapp.h"

#include "sbuf.h"



/* Create an empty, bounded, shared FIFO buffer with n slots */

/* $begin sbuf_init */

void sbuf_init(sbuf_t *sp, int n)

{

    sp->buf = Calloc(n, sizeof(int)); 

    sp->n = n;                       /* Buffer holds max of n items */

    sp->front = sp->rear = 0;        /* Empty buffer iff front == rear */

    Sem_init(&sp->mutex, 0, 1);      /* Binary semaphore for locking */

    Sem_init(&sp->slots, 0, n);      /* Initially, buf has n empty slots */

    Sem_init(&sp->items, 0, 0);      /* Initially, buf has zero data items */

}

/* $end sbuf_init */



/* Clean up buffer sp */

/* $begin sbuf_deinit */

void sbuf_deinit(sbuf_t *sp)

{

    Free(sp->buf);

}

/* $end sbuf_deinit */



/* Insert item onto the rear of shared buffer sp */

/* $begin sbuf_insert */

void sbuf_insert(sbuf_t *sp, int item)

{

    P(&sp->slots);                          /* Wait for available slot */

    P(&sp->mutex);                          /* Lock the buffer */

    sp->buf[(++sp->rear)%(sp->n)] = item;   /* Insert the item */

    V(&sp->mutex);                          /* Unlock the buffer */

    V(&sp->items);                          /* Announce available item */

}

/* $end sbuf_insert */



/* Remove and return the first item from buffer sp */

/* $begin sbuf_remove */

int sbuf_remove(sbuf_t *sp)

{

    int item;

    P(&sp->items);                          /* Wait for available item */

    P(&sp->mutex);                          /* Lock the buffer */

    item = sp->buf[(++sp->front)%(sp->n)];  /* Remove the item */

    V(&sp->mutex);                          /* Unlock the buffer */

    V(&sp->slots);                          /* Announce available slot */

    return item;

}

/* $end sbuf_remove */

/* $end sbufc */


참고 자료
[1] http://www.cnblogs.com/mydomain/archive/2011/07/10/2102147.html

좋은 웹페이지 즐겨찾기