728x90
반응형

또다른 동기화 mechanism인 Semaphore에 대해서 다루어보겠다.

 

Critical sections

POSIX semaphore을 이용하려면 semaphore 객체를 초기화하거나 새로 생성하는 작업이 필요하다. Semaphore가 2가지 종류가 있다. 하나는 이름이 없는 unnamed semaphore, 다른 하나는 named semaphore.

Example)

1. process chain을 만든다. (process chain은 부모 프로세스가 자식 프로세스를 만들고 부모 프로세스는 빠져나가고 자식 프로세스가 계속 loop안에 남아서 자식 프로세스를 만드는 과정으로 진행) argument로 생성할 프로세스 개수를 추가적인 command라인으로 제공한다. 각각의 프로세스는 화면에 string을 출력하는데 process의 id와 child,parent id를 출력한다. 바로 출력을 하는 것이 아니라 process는 문자열을 buffer에다가 넣고 character buffer에 있는 내용을 loop을 돌면서 character 하나씩 출력한다. 한 character를 출력하고 다음 character 출력할때까지 delay를 줄 수 있다. delay를 주는 이유는 쉬는 사이에 context switch가 일어날 가능성이 많아진다. 그럼 다른 process가 선택이 되어서 다음 process가 진행이 된다. delay가 크면 buffer에 있는 것을 다 출력하기 전에 context switch가 일어나면 화면에서 섞이게 된다. 섞여서 출력이 된다. delay가 작으면 정상적으로 나온다. 하나의 process가 출력할 때 critical section이 아니기 때문에 다른 process가 침범해서 벌어지는 문제이다. 

 

chaincritical.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include "restart.h"
#define BUFSIZE 1024

int main(int argc, char *argv[]) {
   char buffer[BUFSIZE];
   char *c;
   pid_t childpid = 0;
   int delay;
   volatile int dummy = 0;
   int i, n;

   if (argc != 3){   /* check for valid number of command-line arguments */
      fprintf (stderr, "Usage: %s processes delay\n", argv[0]);
      return 1;
   }
   n = atoi(argv[1]);
   delay = atoi(argv[2]);
   for (i = 1; i < n; i++)
      if (childpid = fork())
         break;
   snprintf(buffer, BUFSIZE,
       "i:%d  process ID:%ld  parent ID:%ld  child ID:%ld\n",
       i, (long)getpid(), (long)getppid(), (long)childpid);
    c = buffer;
   /********************** start of critical section **********************/
   while (*c != '\0') {
      fputc(*c, stderr);
      c++;
      for (i = 0; i < delay; i++)
         dummy++;
   }
   /********************** end of critical section ************************/
   if (r_wait(NULL) == -1)
      return 1;
   return 0;
}

n이 2라면 loop는 1번만 돌린다.(이미 돌아가고 있는 process있기 때문에) argv[2]는 delay값. (atoi로 integer로 변경해준다.) snprintf로 buffer에 char값을 적는다. snprintf는 다쓴 다음에 자동으로 끝에 null값을 추가해준다. 모든 프로세스들은 buffer에 자기가 출력할 것을 저장해 놓는다. c에 buffer의 시작값을 저장한다. c의 값을 하나씩 증가하면서 c에 있는 buffer의 값을 출력시킬 것이다. for loop을 빠져나온 것은 화면에 출력할것을 먼저 쓴다. 그리고 while문에 들어가서 buffer에 character를 하나씩 출력한다. c가 가리키는 buffer값이 null값이 아닐동안 while문안에서 fputc를 호출해서 특정 file pointer에서 출력한다. 현재 c가 가리키는 buffer안에 있는 값을 출력을 하겠다. stderr장치에다가 출력을 하겠다. 계속 다음 것을 출력하는게 아니고 delay값만큼 dummy integer변수를 iteration을 돈다. 자식 프로세스도 이 부분에 진입할 수 있다. r_wait는 중요하게 안봐도 된다.

 

critical section은 코드의 특정부분을 의미한다. 특정 부분을 critical section으로 만들겠다. 한번에 하나의 process만 진행을 해야 한다. critical section과 관련된 코드 파트를 4가지 파트로 구분해서 이야기 한다. (운영체제에서 얘기하는것과 동일)

critical section을 빠져나온다음에 exit section으로 진입한다. exit section에서 lock을 release해야 한다. lock을 release하는 routine이 진행이 되어야 한다. unlock을 호출하는 부분이 exit section이 될 것이다. 

 

Solution to Critical-Section Problem

3가지 조건을 만족해야 critical section 문제를 해결할 수 있는 mechanism이 될 수 있다. 조건은 1. mutual exclusion(코드에는 한번에 하나의 process만 진입할 수 있다), 2. Progress (진행이 된다, 만약에 어떤 process도 critical sectino에 있지 않으면 process가 critical section에 진입하기 원하는 process가 있다면 그러한 process들 중에서 다음 process를 선택해서 다음 process가 critical section으로 진입할 수 있어야한다, 빠져나간 process가 아닌 대기중에 process가 계속 critical section에 진입할 수 있어야한다.)

1번을 만족하는데 2번을 만족 안하는 경우. lock을 요청한 첫번째 process만 들어가는데 이 process가 unlock을 하고 나가야되는데 unlock을 안하고 나갔으면 다음 process는 계속 기다려야 한다. 그래서 2번 조건을 만족하지 않는다(unlock을 해서 다음 process가 들어가도록 해야한다) 3번 조건은 Bounded Waiting(기다리는 process에 대한 조건인데, 기다리는 시간이 한정적이어야 한다. 마냥 계속 기다려서는 안되고 제한된 시간 안에는 process안에 진입할 수 있어야한다. bound가 존재해야 한다. 특정 bound안에는 critical section안에는 진입할 수 있어야 한다. 모든 process에게 fair한 조건을 줘야한다. 특정 process는 대기하고 있는데 계속 다른 process에게 밀려서 진입하지 못하는 case가 생길 수도 있기 때문이다.)

 

Semaphores

semaphore객체는 OS에서 관리하는 resource중 하나이고 process들이 다양한 방식으로 동기화를 할때 사용할 수 있는 동기화 mechanism이다. integer variable이다. 이것이 수행할 수 있는 operation이 2가지가 있다. atomic operation이라 하는것은 더이상 쪼갤 수 없는 operation. 중간에 다른 operation이 끼어들어올 수 없는 operation이다. 수행하는 부분이 그 자체로 critical section이 되어야 한다. 개발자 입장에서 할 수 있는것이 아니고, 커널(OS)영역에서 지원을 해주어야 한다. Semaphore의 함수는 kernel에서 지원을 해준다는 말이고 하나는 wait operation, 하나는 signal operation이다. 2개의 operation을 진행할 수 있는 정수형 변수다. wait는 정수값을 줄일려고 하는 oepration, signal은 정수값을 증가시키는 operation이구나 라고 생각하면 된다.

 wait operation은 semaphore operation의 정수값을 줄이는데 항상 줄이는 것이 아니고 semaphore값이 0보다 크면 semaphore값을 감소시키고, 만약에 0이면 더이상 못줄이니까 wait 함수를 호출한 caller의 실행을 block시킨다. 즉, 대기를 하게 된다. wait함수를 호출해서 semaphore값이 0이면 대기. waiting queue로 들어가서 block된다. 그게 아니라 10이였다 그러면 wait를 호출한 것은 9로 줄이고 지나가는 것이다. 0인데 wait를 호출하면 그 process는 waiting queue로 들어가서 대기한다. 0보다 큰 경우에만 줄인다. signal함수는 반대로 semaphore를 증가시키는 함수이다. semaphore를 증가시키기 전에 마냥 증가시키는 것이 아니고 block된 process가 있으면 그 process를 깨워주는 역할도 한다. 먼저 thread나 process가 blcok되어있으면 signal함수는 semaphore값을 0인상태로 두고 하나를 unblock시킨다. signal 함수를 호출하면 하나를 깨운다. 만약에 아무 thread도 block되어있지 않으면 그때 semaphore를 증가시킨다. 

pseudo code. wait의 과정은 automic하게 진행해야 한다. 

void wait(semaphore_t *sp){
  if(sp->value > 0) sp -> value--;
  else{
    <add this thread to sp-> list>
    <block>
  }

void signal(semaphore_t *sp){
  if(sp->list!=NULL)
     <remove a thread from sp->list>
  else{
    sp->value++;
  }
}

Semaphore examples

다양한 방식의 동기화 방식을 해야 하는데 다양한 방식은 초기값이 중요하다. 거기서 동기화 방식이 결정된다. s라는 semaphore가 있다고 가정하고 보호하고 싶은 critical section이 있다고 하면 그 전에 entry section에서 허가를 먼저 받아야 한다. 허가를 받기 위해 wait operation을 사용한다.

 wait를 진행하면 첫번째 process는 semaphore값을 0으로 줄이고 critical section으로 진입한다. 그 사이에 다음 process가 와서 진입할려고 한다 하면 wait를 호출했더니 semaphore의 값이 이미 0이다. 그래서 나는 semaphore를 줄이지 못하고 이 process는 waiting queue에 들어가서 대기해야 한다.(block) critical section에 들어간 process가 다 진행하면 exit section에 들어가서 signal 함수를 사용해서 반환해 준다. 

semaphore값을 1로 초기화하는것이 중요하다. 만약에 semaphore 초기값을 0으로 초기화했으면 어떻게 동작할까. 모든 wait가 block하고 deadlock 결과를 도출할 것이다. 마냥 기다리면 deadlock이 된다. 만약에 8로 선언하면, critical section에 여러개 process가 들어가서 critical section이 깨진다.(총 8개의 process가 진입하게 된다) 9번째가 진행하면 들어가지 못하고 대기하게 된다. 이 case는 critical section은 깨졌는데 다른 방식으로 동기화가 된다. 한번에 최대 8개가 진입할 수 있다는 동기화가 진행하게 된 것이다. 

semaphore의 2개의 operation을 잘 숙지하자. entry section에 쓸 수 있는 허가를 얻기 위한 것이 wait, signal은 허가를 반환하는 허가를 반환한다는 것은 semaphore값을 증가시키는 것. 또다른 방식으로 동기화하는 방식을 얘기하고 있다. 두 process가 수행하는 operation의 수행 순서를 어느정도 제한을 두고자 한다. 어떤 식이냐면 process 1번의 statement, process2는 b를 진행시킬려고 하는 것이다. a와 b 2 statement는 어떤 순서로 진행이 될지 모른다. semaphore을 사용하면 순서를 제어할 수 있다. 1번이 a를 실행을 하는데 2번이 하는 b보다 a가 먼저 항상 실행이 되도록 항상 a라는 statement가 b보다 먼저 실행이 되게끔 해야겠다. 그렇게 하기 위해서 sync라는 semaphore를 사용하게 된다. 초기값을 0으로 초기화하고 정상적으로 process 1이 먼저 진행했다라고 하면 a를 실행하고 signal을 호출해서 1로 증가시키고 끝났다. process 2는 wait를 해서 semaphore를 0으로 줄이고 b를 실행한다. a다음에 b실행된다.

만약 process2가 먼저 실행이되면 semaphore는 이미 0이라 process2가 대기하게 된다. 그다음에 process1이 나중에 실행하더라도 대기하는 process(process2)가 있으니까 깨우면 a가 실행되고 b가 실행된다. a가 실행되는 도중에 b가 실행되는경우? wait함수가 먼저 호출되면 2가 대기상태에 들어가게 된다. 어떤 순서로 실행되건 상관없이 a가 먼저 실행되고 b가 실행되게 제어를 한 것이다. semaphore는 0으로 초기화 했기때문에 이게 가능해진 것이다.

 

Semaphore examples

Process 1 executes;
for( ; ; ){
  wait(&S);
  a;
  signal(&Q);
}
Process 2 executes;
for( ; ; ){
  wait(&Q);
  b;
  signal(&S);
}

둘다 무한 loop를 돌고 있다. Process1은 a statement를 실행하려고 하고 Process2는 b를 반복적으로 실행하려고 한다. 두 프로세스는 concurrent하게 실행이 되고, 어떤 순서로 번갈아가면서 실행이 될지 알 수 없다. concurrent한 실행에서는 그 실행 순서를 제어할 수 없다. 두 프로세스 모두 실행하려는 statement전에 wait를 호출하여 semaphore를 감소시킨다. 원하는 statement를 실행한 다음에는 signal 함수를 써서 semaphore값을 증가시키던지, 대기하던 process를 깨우는 operation을 반복하고 있다. 여기서 2개의 semaphore를 사용하고 있다. Semaphore를 여러개 사용할 수 있는 예시이다. 초기값을 어떻게 주느냐에 따라서 a와 b statement를 진행하는 방식이 달라진다. 

- S and Q = 1 : a와 b의 실행횟수 차이가 1보다 클 수 없다.

S와 Q semaphore의 초기값을 둘 다 1로 초기화하게 되면, a와 b 두 프로세스의 반복실행 방식이 어떻게 되는가. 어떤 프로세스가 먼저 진행될지는 모른다. for loop을 한바퀴 돌때마다 iteration이 추가가 된다. 이게 마냥 반복해서 실행할 수 있는게 아니라 자신의 iteration 횟수가 다른 iteration process의 횟수보다 하나 이상 더 앞서갈 수는 없는 방식으로 동기화가 된다. 만약 P1이 다 끝나고 P2가 실행된다고 가정하면, P1이 실행되면 S는 1에서 0, Q는 1에서 2로 바뀐다. 그리고 for loop을 다시 실행하면 S는 0이라서 멈춘다. 이 때 iteration차이가 1번이다. 결국 P1은 P2가 실행되지 않은경우 한번밖에 실행이 안되므로, iteration 횟수의 차이가 1보다 크게 날 수는 없다고 할 수 있는 것이다. 

- one semaphore is 1 and the otehr 0

예를 들어 S = 1, Q = 0 으로 초기화 했다. 이런 경우는 다른 식으로 동기화가 된다. process들이 strict alternation으로 진행을 한다. a와 b가 번갈아가면서 concurrent 실행이 되도록 제약을 걸 수 있다. Semaphore를 1로 초기화한 process가 먼저 실행을 하고 서로 번갈아가면서 실행된다. 예시에서는 a,b,a,b, ... 이렇게 반복이 된다. 

- S and Q = 0

deadlock이 발생하게 된다. 

 

Semaphore examples

Process 1 executes:
for( ; ; ){
  wait(&Q);
  wait(&S);
  a;
  signal(&S);
  signal(&Q);
}
Process 2 executes:
for( ; ; ){
  wait(&S);
  wait(&Q);
  b;
  signal(&Q);
  signal(&S);
}

- S and Q = 1

: 상황에 따라 다르다. 어떤 process가 먼저 cpu가지냐에 따라 deadlock에 빠지거나 실행이 잘될 수도 있다. 이런 경우가 debugging 하기가 힘들다. CPU가 어떤 process를 진행하냐에 따라 동기화가 잘 진행될 수도 있고, deadlock에 빠질 수도 있다. 만약 process 1이 wait(&Q)를 하고 cpu를 잃어버려 process 2가 진행하게 되면 deadlock에 빠질 수 있다. 두 process모두 첫번쨰 wait만 실행하고 deadlock이 벌어지는 것이다. 

 

POSIX:SEM Unnamed Semaphores

: POSIX에서 제공하는 semaphore는 2가지가 있다. 먼저 unnamed semaphore. 둘 중에 하나 편의에 따라서 쓰면 되는 것이고 두 semaphore가 사용하는 system call 함수가 다르다. semaphore type에 변수를 지정해서 사용한다. semaphore type이 정의되어 있다. semaphore type은 sem_t. named 와 unnamed semaphore 모두 sem_t를 사용한다. 정수값을 갖는다. 

#include <semaphore.h> 헤더파일에 존재한다. 객체를 생성하고 초기화작업이 필요하다. 

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned value);
int sem_destroy(sem_t *sem);

초기화하고 destroy까지 해주어야 한다. 초기화하는 함수가 sem_init 함수고, 반환하는 함수가 sem_destroy함수이다. 

sem_init()함수를 보면 파라미터 3개가 필요하다. 초기화할때 초기값이 필요하다. 초기값을 마지막 파라미터로 지정한다. semaphore은 양수값만 되기때문에 unsigned. pshared는 indicator인데, semaphore를 공유할지 안할지 결정한다. 0또는 1로 지정한다. 0으로 지정했다면 false니까 share하지 않겠다는 뜻이다. 이렇게 되면 이 semaphore를 초기화한 process의 thread들 끼리만 사용할 수 있다. 그럼 다른 process의 thread와 공유하지 않겠다는 뜻. true면 공유하겠다(nonzero) semaphore에 access할수만 있다면 다 사용할 수 있다. 초기화를 성공하면 0이 반환, 아니면 -1이 반환된다. 

다 쓴 semaphore 시스템에 반환하려면 sem_destroy 사용하면 된다. 

 

POSIX:SEM Semaphore Operations

#include <semaphore.h>
int sem_post(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_getvalue(sem_t *restrict sem, int *restrict sval);

- sem_post() : semaphore의 signal operation을 수행하는 함수이다. 이 함수는 signal-safe해서 signal handler내부에서 사용할 수 있다.

- sem_wait()

- sem_trywait() : systemcall함수에 try가 들어가면 함수의 동작을 실행할 수 있는지 try해보는것. wait를 할 수 있는지 test해보는 것. (semaphore가 0이면 못줄이니까 -> block이 되는게 아니라 바로 -1을 리턴함) 줄일 수 있는지 확인만 하고 다른 task 실행하고 싶을 때 사용. 

- sem_getvalue() : 현재 semaphore의 값을 확인하고자 할 때 사용하는 함수. 어디로 반환하냐 하면 2번째 파라미터로 반환한다. 성공적으로 알아왔으면 0, 아니면 -1 리턴이 되는 것이다. sval이 output parameter가 되는 것. 

*주의해서 사용해야함 : 현재 semaphore값이 어느 시점에 semaphore값인지 확인할 방법은 없다. 막 변경되고 있는 상황이였다면 호출한 순간에 semaphore값이 반환되지vcdd는 않는다. unspecified time에 반환된다. 

semshared.c

#include <errno.h>
#include <semaphore.h>

static int shared = 0;
static sem_t sharedsem;

int initshared(int val) {
   if (sem_init(&sharedsem, 0, 1) == -1)
      return -1;
   shared = val;
   return 0;
}

int getshared(int *sval) {
   while (sem_wait(&sharedsem) == -1)
      if (errno != EINTR)
         return -1;
   *sval = shared;
   return sem_post(&sharedsem);
}

int incshared() {
   while (sem_wait(&sharedsem) == -1)
      if (errno != EINTR)
         return -1;
   shared++;
   return sem_post(&sharedsem);
}
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#define NUMTHREADS 10

int initshared(int val);
int incshared();
int getshared(int *sval);

/* ARGSUSED */
static void *increment(void *args) {
   int i;
   for (i=0;i<100;i++)
      incshared();
   return NULL;
}

int main (void) {
   int error;
   int i;
   pthread_t tid[NUMTHREADS];
   int val;

   if (initshared(0)) {
      perror("Could not initialize shared variable");
      return 1;
   }
   getshared(&val);
   printf("Shared variable initialized to %d\n", val);

   for (i = 0; i < NUMTHREADS; i++)
      if (error = pthread_create(tid+i, NULL, increment, NULL))
         fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
   printf("Number of threads created: %d\n", NUMTHREADS);
   for (i = 0; i < NUMTHREADS; i++)
      if (error = pthread_join(tid[i], NULL))
         fprintf(stderr, "Failed to join thread: %s\n", strerror(error));
   printf("All threads done\n");
   getshared(&val);
   printf("Shared variable now has value %d\n", val);
   return 0;
}

 

POSIX:SEM Named Semaphores

- 이름과 semaphore객체에 access할 권한만 가지고있으면 동기화를 할 수 있다. file처럼 다룰 수가 있는 것이다. 이름을 지정을 할 수 있는데, 이름은 slash로 시작을해야 한다. /로 시작하지 않는 경우 어떻게 실행될지 모른다. 

#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, ...);

named semaphore를 생성하거나 이미 만들어진 semaphore를 access하고자 할 때 사용하는 system call 함수가 sem_open()함수이다. 마치 file을 open할때 썼던것처럼 file을 다루는 것과 비슷하게 사용. parameter는 기본 parameter가 2개이고 oflag에 따라서 부가적인 parameter가 따라 올수도 있다. named semaphore를 생성할꺼냐, open할꺼냐 -> oflag는 0을 주면 있는것을 open하고 named semaphore를 새로 만들려고 하면 oflag에 O_CREATE flag을 주면 된다. 파일을 새로 만들어서 open 하겠다고 하는 것처럼, 이름이 미리 존재하면 이때는 그 파일이 그냥 open이 된다. (O_CREATE가 무시가 된다) semaphore도 동일하다. O_EXCL와 같이 사용할 수 있다. 이 경우에 이미 이름이 존재하면 error를 리턴한다. SEM_FAILED값이 리턴이 된다. semaphore 권한에서 실행권한은 따로 줄 수 없다. 읽기 쓰기 권한만 줄 수있다. access permission, 초기값 같이 지정해주는 것이다.

예제>

getnamed() function : named semaphore를 open하든지 생성하는 함수를 따로 정의함. 

getnamed.c

#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/stat.h>
#define PERMS (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) //644권한
#define FLAGS (O_CREAT | O_EXCL)

int getnamed(char *name, sem_t **sem, int val) {
   while (((*sem = sem_open(name, FLAGS , PERMS, val)) == SEM_FAILED) &&
           (errno == EINTR)) ; 
   if (*sem != SEM_FAILED) //에러가 아니면
       return 0;
   if (errno != EEXIST) //exist(같은 이름이 존재한다)에러가 아니면
      return -1;
   while (((*sem = sem_open(name, 0)) == SEM_FAILED) && (errno == EINTR)) ;
   if (*sem != SEM_FAILED) //failed가 아니면(성공했다면)
       return 0;
   return -1;
}
//getnamed 2번째 파라미터가 output parameter임

14장의 젤 처음 critical section을 named로 해결한것.

#include <errno.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include "restart.h"
#define BUFSIZE 1024
int getnamed(char *name, sem_t **sem, int val);

int main  (int argc, char *argv[]) {
   char buffer[BUFSIZE];
   char *c;
   pid_t childpid = 0;
   int delay;
   volatile int dummy = 0;
   int i, n;
   sem_t *semlockp;

   if (argc != 4){       /* check for valid number of command-line arguments */
      fprintf (stderr, "Usage: %s processes delay semaphorename\n", argv[0]);
      return 1;
   }
   n = atoi(argv[1]);
   delay = atoi(argv[2]);
   for (i = 1; i < n; i++)
      if (childpid = fork())
         break;
   snprintf(buffer, BUFSIZE,
      "i:%d  process ID:%ld  parent ID:%ld  child ID:%ld\n",
       i, (long)getpid(), (long)getppid(), (long)childpid);
   c = buffer;
   if (getnamed(argv[3], &semlockp, 1) == -1) {
      perror("Failed to create named semaphore");
      return 1;
   }
   while (sem_wait(semlockp) == -1)                         /* entry section */
       if (errno != EINTR) {
          perror("Failed to lock semlock");
          return 1;
       }
   //이부분이 critical section이어야 함. critical section으로 만들어줌.
   //critical section으로 만들기 위에서 sem_wait로 초기화되서 생성되었을거다.
   //맨처음 만드는것은 그 위에 getnamed(semaphore이름, ~~, ~~)
   while (*c != '\0') {                                  /* critical section */
      fputc(*c, stderr);
      c++;
      for (i = 0; i < delay; i++)
         dummy++;
   }
   if (sem_post(semlockp) == -1) {                           /* exit section */
      perror("Failed to unlock semlock");
      return 1;
   }
   if (r_wait(NULL) == -1)                              /* remainder section */
      return 1;
   return 0;
}

-> argv[0] : 파일명 argv[1]: 프로세스 개수 argv[2]: 얼마나 쉴건지 argv[3]: named semaphore이름

성공하면 semaphore 포인터값으로 반환을 해주겠다. semaphore로 critical section을 만들기 위해서는 초기값을 1로 설정을 해야 한다. 새로 만들고 getnamed로 open해서 반환해주겠다. 처음 호출한 process가 semaphore 만들것이고 그 이후는 semaphore사용할 것이다. semaphore의 waiting queue로 대기하게 된다. process가 빌때까지. delay가 얼마나 됐건 process에서 빠져나가고 exit section에서 반환할 것이다. sem_post함수를 사용함. 대기중인 process가 있는지 보고 있으면 깨워준다. named semaphore는 ls밑에 디렉토리 구조에서 보이지는 않는다.

 

Closing and unlinking

#include <semaphore.h>
int sem_close(sem_t *sem);
int sem_unlink(const char* name);

named semaphore는 프로그램이 종료되더라도 남아있다. 그래서 named semaphore인 경우 system call함수를 사용해서 close함수, unlink함수를 사용해야 한다. close는 사용을 더 이상하지 않겠다는 뜻이지 semaphore가 삭제되는 것은 아니다. sem_unlink함수를 사용해서 삭제를 해야 한다. sem_unlink함수는 비동기식으로 작동한다. 요청이 성공적으로 전달되면 바로 전달하고 삭제는 나중에 할수있다. 

destroynamed()

#include <errno.h>
#include <semaphore.h>

int destroynamed(char *name, sem_t *sem) {
    int error = 0;

    if (sem_close(sem) == -1)
       error = errno;
    if ((sem_unlink(name) != -1) && !error)
       return 0;
    if (error)        /* set errno to first error that occurred */
       errno = error;
    return -1;
}

sem_close로 close하고 unlink로 삭제한다. 에러는 sem_close 또는 sem_unlink에서 발생할 수 있다. error 코드는 첫번째 발생한 error를 errcode에 넣는걸로 되어있다. 둘 다 에러가 발생했다면 첫번째 발생한 것을 심었다. 

#include <semaphore.h>
#include <stdio.h>

int destroynamed(char *name, sem_t *sem);
int getnamed(char *name, sem_t ** sem, int val);

int main(int argc, char *argv[]) {
   sem_t *mysem;

   if (argc != 2) {
      fprintf(stderr, "Usage %s semname\n", argv[0]);
      return 1;
   }
   if (getnamed(argv[1], &mysem, 0) == -1) {
       perror("getnamed");
       return 1;
   }
   if (destroynamed(argv[1], mysem) == -1) {
      perror("first destroy failed");
      return 1;
   }
   fprintf(stderr,"Semaphore %s destroyed\n", argv[1]);
   if (destroynamed(argv[1], mysem) == -1) {
      perror("second destroy should have failed and did");
      return 0;
   }
   fprintf(stderr, "Destroy successful\n");
   return 1;
}
728x90
반응형

'CS > 시스템 프로그래밍' 카테고리의 다른 글

Signals  (0) 2021.12.07
Thread Synchronization  (0) 2021.12.01
POSIX Threads  (0) 2021.11.23
Times and Timers  (0) 2021.11.16
UNIX Special Files  (0) 2021.11.03

+ Recent posts