hared memory와 semapore를 사용하여 데이터를 공동관리 하는 두개이상의 프로세스로 구성된 프로그램을 작성하시오
(예 : 계산담당 프로세스와 디스플레이 담당 프로세스)


이과제를 해결하기 위해서 shared memory와 semapore에 대해서 확실히 알고 넘어가야한다.

먼제 세마포어(semapores)에 대해서 알고 넘어가도록 하자.


Semaphores
세마포어
A counter used to provide access to a shared data object for multiple process.
세마포어란 여러 프로세스가 공유된 데이터를 Access 할 수 있게 하는 Counter이다.

To obtain a shared resource a process
프로세스가 공유된 자원을 얻기 위해서는
1.Test the semaphore that controls the resource
세마포어를 테스트 해봐야하는데 그것은 리소스를 컨트롤 한다.
2. If the value of the semaphore is positive, the process can use the resource
세마포어의 값이 양수(positive)이면 프로세스는 자원을 이용할 수 있다.
-The process decrement the semaphore value by 1, indicating that it has used one unit of the resource.
프로세스는 세마포어의 값을 1을 감소시킨다, 그것은 리소스의 하나의 unit을 이용했다는것을 나타낸다.
3.If the value of the semaphore is 0, the process goes to sleep until the semaphore value is greater than 0
만약 세마포어 값이 0이라면 프로세스는 세마포어의 값이 0보다 커질때까지 슬립을 한다.
-When the process wakes up, it returns to sleep
프로세스가 깨어나면 그것은 슬립을 리턴시킨다.

To implement semaphores correctly,
세마포어를 올바르게 구현하려면
The test of a semaphore’s value and the decrementing of this value must be an atomic operation.
세마포어의 값의 테스트와 이값의 감소가 atomic operation이어야 한다.
For this reason, semaphores are normally implemented inside the kernel.
이런 연유로 세마포어는 커널 안에서 일반적으로 구현이 된다.


이론적 형태의 세마포어

-네덜란드의 이론가 E.W.Dijkstra가 프로세스의 동기화 해결책으로 제안
-세마포어 (sem)은 다음과 같이 연산이 허용되는 정수형 변수

p(sem) or wait(sem)
if (sem != 0)
    decrement sem by one
else
    wait until sem becomes non-zero

v(sem) or signal(sem)

if (queue of waiting processes not empty)
restart first process in wait queue
else
increment sem by one

-두 연산은 모두 atomic operations
`sem을 변경할 수 있는 프로세스는 한 순간에 오직 하나뿐이다.

세마포어의 좋은점: 세마포어의 불변특성 (unvariant)
-(semaphore’s initial value + number of v operations – number of completed p operations) >= 0
세마포어는 다방면으로 사용될 수 있다.
-가장 단순한 경우는 프로그램의 특정한 부분을 수행하는 프로세스가 한 순간에 오직 하나만 존재하도록 하는 mutual exclusion (상호배제)를 보장.

-예제:
(number of completed p operations – number of v operations) <= initial value of semaphore sem의 초기값이 1이면
(number of completed p operations – number of v operations) <= 1
즉 P와 V사이에 있는 문장들은 한 순간에 오직 하나의 프로세스에 의해서만 수행된다.

이런 이론으로 Unix에는 세마포어가 구현이 되어 있다. 유닉스에 세마포어 시스템 호출은 어떻게 이루어지는지 한번 살표보도록 하자. 커널에서는 세마포어를 위한 스트럭처를 이용하여 세마포어를 관리하게 된다. 이때 사용하는 스트럭처가 다음의 smid_ds이다.

struct semid_ds{
  struct ipc_perm sem_perm; //세마포어에 대한 접근권한
  struct sem *sem_base; /* ptr to first semaphore */
  ushort sem_nsems;     /* # of semaphores in set */
  time_t sem_otime;     /* last-semop() time *///마지막으로 세마포어와 관련된 작업을 수행하는 시간
  time_t sem_ctime;     /* last-change time *///마지막으로 스트럭처의 데이터들이 업데이트 된 시간
};

이제 Shared Memory에 대해서 알아보자.

Shared memory란 말그대로 프로세스들이 특정 메모리 영역을 공유하도록 만든 뒤, 이 공간을 이용하여 통신을 수행하는 기법이다. 메모리를 서로 공유하는 프로세스들은 공유 가상 메모리를 가리키는 테이블 엔트리를 가지게 된다.

다른 IPC 기법들과 마찬가지로 공유메모리는 킷값을 이용하여 잡근 및 관리가 된다. 공유 메모리 또한 프로세스 동기화가 필요하기 떄문에 세마포어 등을 이용하여 자원에 대한 관리를 해주어야 한다. 

유닉스 시스템은 shm_segs라는 벡터를 이용하여 공유 메모리를 관리하게 된다. 그리고 벡터속에는 shmid_ds라는 스트럭처가 저장이 되는데 shmid_ds를 이용하여 공유메모리 정보를 저장하게 된다. 

이것을 토대로 프로그램을 작성해보자.  프로그램은 두개의 프로그램으로 나뉘고 하나의 프로세스는 메시지를 받으려고 기다리는 프로세스이고 다른 하나는 메세지를 입력하는 프로세스가 될것이다. 이것을 shared memory와 세마포어를 이용해서 작성 하였다.

메시지를 보내는 프로그램은 우선 프로그램이 실행이 되면 세마포어의 값을 1감소시키고 메시지 입력을 대기한다. 메시지를 입력하면 세마포어의 값을 하나더 증가 시킨다.

메시지를 받는 프로그램은 세마포어를 감소하고 메시지를 출력한뒤 세마포어를 증가시키는데 세마포어가 보내는 프로그램쪽에서 차지하고 있다면 계속 대기상태가 되게 될것이다. 프로그램의 소스코드는 다음과 같다.


메시지를 보내는 프로그램
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
//공유 메모리 사이즈 설정
#define SIZE 64
 
//이프로세스는 상대편 프로세스가 메시지를 입력할때까지는 process가 돌면 안된다. 
//세마포어를 이용해서 막아보자
 
int main(int argc, char *argv[])
{
    //세마포어와 shared memory 변수선언
    void *s_memory = (void *)0;
    char *buffer;
    int semId, proId, smId;
    int isRun = 1;
 
    struct sembuf semB;
     
    //shmget을 이용하여 공유메모리를 확보한다.
    smId = shmget((key_t)9000, SIZE, 0666|IPC_CREAT);
    if(smId == -1)
    {
        printf("shmget 실행실패\n");
        return 0;
    }
 
    //shmat을 이용하여 공유 메모리 주소 얻기
    s_memory = shmat(smId, (void *)0, 0);
    if(s_memory == (void *)-1)
    {
        printf("shmat 실행실패\n");
        return 0;
    }
 
    //공유메모리 주소와 내부 변수 포인터연결
 
    buffer = (char *)s_memory;
 
 
    //sembuf의 초기값 설정
 
    semB.sem_flg = SEM_UNDO;
    semB.sem_num = 0;
 
    //semget을 이용해서 세마포어 ID를 구한다.
    semId = semget((key_t)1234, 1, 0666 | IPC_CREAT);
 
    //세마포어 초기값 설정
    if(semctl(semId, 0 , SETVAL, 1) == -1)
    {
        fprintf(stderr, "세마포어 초기화 실폐\n");
        exit(0);
    }
    //본프로세스의 PID출력
    printf("본프로세스의PID값은 : %d\n", getpid());
     
     
 
 
    //프로세스의 입무를 수행하기 전에 세마포어값을 감소 후 수행 그다음 세마포어 값을 다시 증가 시킨다.
    while(isRun)
    {
        //세마포어에 마지막으로 수정을 가한 프로세스 PID 출력
        proId = semctl(semId, 0, GETPID, 0);
        printf("세마포어를 변경한 마지막 PID: %d\n", proId);
 
             
        //세마포어의 값을 감소시킨다.
        semB.sem_op = -1;
        if(semop(semId, &semB, 1) == -1)
        {
            fprintf(stderr, "세마포어 값감소 실패\n");
            exit(0);
        }
         
        printf("메시지입력 : "); 
        scanf("%s",buffer);
               
        //quit을 보내면 종료
        if(!strcmp(buffer, "quit"))
        {
            break;
        }

 

        //세마포어 값을 증가시킨다.
        semB.sem_op = 1;
        if(semop(semId, &semB, 1) == -1)
        {
            fprintf(stderr, "세마포어 값증가 실패\n");
            exit(0);
        }
    }
 
    //프로세스와 공유메모리 분리
 
    if(shmdt(s_memory) == -1)
    {
        printf("shmdt 실행실패\n");
        return 0;
    }
 
    return 1;
}


메시지를 받는 프로그램

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
//공유 메모리 사이즈 설정
#define SIZE 64
 
//이프로세스는 상대편 프로세스가 메시지를 입력할때까지는 process가 돌면 안된다. 
//세마포어를 이용해서 막아보자
 
int main(int argc, char *argv[])
{
    //세마포어와 shared memory 변수선언
    void *s_memory = (void *)0;
    char *buffer;
    int semId, proId, smId;
    int isRun = 1;
 
    struct sembuf semB;
     
    //shmget을 이용하여 공유메모리를 확보한다.
    smId = shmget((key_t)9000, SIZE, 0666|IPC_CREAT);
    if(smId == -1)
    {
        printf("shmget 실행실패\n");
        return 0;
    }
 
    //shmat을 이용하여 공유 메모리 주소 얻기
    s_memory = shmat(smId, (void *)0, 0);
    if(s_memory == (void *)-1)
    {
        printf("shmat 실행실패\n");
        return 0;
    }
 
    //공유메모리 주소와 내부 변수 포인터연결
 
    buffer = (char *)s_memory;
 
 
    //sembuf의 초기값 설정
 
    semB.sem_flg = SEM_UNDO;
    semB.sem_num = 0;
 
    //semget을 이용해서 세마포어 ID를 구한다.
    semId = semget((key_t)1234, 1, 0666 | IPC_CREAT);
 
    //세마포어 초기값 설정
    if(semctl(semId, 0 , SETVAL, 1) == -1)
    {
        fprintf(stderr, "세마포어 초기화 실폐\n");
        exit(0);
    }
    //본프로세스의 PID출력
    printf("본프로세스의PID값은 : %d\n", getpid());
     
     
 
 
    //프로세스의 입무를 수행하기 전에 세마포어값을 감소 후 수행 그다음 세마포어 값을 다시 증가 시킨다.
    while(isRun)
    {
        //세마포어에 마지막으로 수정을 가한 프로세스 PID 출력
        /*proId = semctl(semId, 0, GETPID, 0);
        printf("세마포어를 변경한 마지막 PID: %d\n", proId);*/
 
            
        if(!strcmp(buffer,""))continue; //일단 버퍼가 없으면 그냥 계속 지나쳐간다.
        
        //세마포어의 값을 감소시킨다.
        semB.sem_op = -1;
        if(semop(semId, &semB, 1) == -1)
        {
            fprintf(stderr, "세마포어 값감소 실패\n");
            exit(0);
        }
 
        
        printf("받은 메시지 : %s\n", buffer); //메시지를 출력해준다.
        
        //quit을 받으면 종료
        if(!strcmp(buffer, "quit"))
        {
            break;
        }
        
 
        //세마포어 값을 증가시킨다.
        semB.sem_op = 1;
        if(semop(semId, &semB, 1) == -1)
        {
            fprintf(stderr, "세마포어 값증가 실패\n");
            exit(0);
        }
        
 
    }
 
    //프로세스와 공유메모리 분리
 
    if(shmdt(s_memory) == -1)
    {
        printf("shmdt 실행실패\n");
        return 0;
    }
 
    //공유메모리 제거
    if(shmctl(smId, IPC_RMID, 0) == -1)
    {
        printf("shmctl 실행실패\n");
        return 0;
    }
     
 
    //세마포어를 제거한다.
    if(semctl(semId, 0, IPC_RMID, 0) == -1)
    {
        fprintf(stderr, "세마포어 제거 실패\n");
        exit(0);
    }
 
 
    return 1;
}


실행 결과는 다음과 같다.



다음과 같이 메시지를 주고 받을 수 있게 프로그램이 수행된다.







'CSE(컴퓨터 공학) > 유닉스 시스템' 카테고리의 다른 글

유닉스 과제 #3  (0) 2009.05.06
유닉스 과제2  (0) 2009.04.08
Posted by 태씽