반응형

posix shm_open

저수준 open 함수와 같은 원형이다.

기존의 유닉스 파일 처리 개념을 확장하고, name은 항상 / 로 시작한다.

메모리의 일부를 파티션 공간처럼 만들고, 여기에 파일을 만들어 공유하는 방식이다.

 

int shm_open(const char *name, int oflag, mode_t mode);
int shm_unlink(const char *name)

삭제는 shm_unlink(const char *name)을 활용한다.

posix 공유메모리에는 디렉토리 개념이 존재하지 않기 때문에 name에 슬래시를 붙이면 안된다.

 

그리고 posix 공유 메모리는 리얼타임 라이브러리를 활용하기 때문에

컴파일 시에 rt 라이브러리를 링크해야 정상적으로 컴파일이 가능하다.

gcc -o source source.c -lrt

 

open과 shm_open 차이

open은 disk 파일 시스템의 파일을 이용하여 공유하고

shm_open은 메모리의 일부를 파일 시스템으로 표현한 /dev/shm에서 파일 개념을 표현한다.

 

#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main() {
    int fd, pagesize, length;
    caddr_t addr;
    pagesize = sysconf(_SC_PAGESIZE);
    length = 1 * pagesize;

    //m.dat 앞에 슬래시를 붙이면 안된다. posix 공유메모리에는 디렉토리가 존재하지 않기 때문이다.
    if ((fd = shm_open("m.dat", O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) {
        perror("shm_odsafpen");
        exit(1);
    }

    if (ftruncate(fd, (off_t) length) == -1) {
        perror("ftruncate");
        exit(1);
    }

    addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    close(fd);
    strcpy(addr, "Ftruncate Test\n");


    return 0;
}

위 코드에서 파일 디스크립터인 fd를 close 하여도

addr에 해당 공유 메모리의 주소가 남아있기 때문에 strcpy를 활용하여 매핑한 메모리에 데이터를 정상적으로 쓰는 게 가능하다.

 

 

POSIX 공유 메모리의 삭제

공유 메모리를 삭제할 때는 shm_unlink를 이용하면 된다.

파일을 삭제하는 unlink와 동일한 구조이다.

 

만약 위의 코드처럼 mmap으로 공유 메모리의 주소를 맵핑하고 있다면

munmap으로 맵핑을 해제 한뒤 unlink로 파일을 삭제해야 한다.

 

만약 posix 공유메모리를 사용 중에 삭제하는 경우에는

리눅스에서는 새로운 프로세스는 공유 메모리에 연결이 불가능하고,

기존 프로세스들은 공유 상태를 유지하게 된다. 이는 표준안에는 명시되지 않은 현상이다.

 

 

 


 

 

POSIX 세마포어

posix 세마포어는 외부에서 접근 가능한 인터페이스 경로 유무에 따라

명명된 세마포어(named semaphore), 익명 세마포어(nameless/anonymous semaphore)로 구분된다.

 

 

익명 세마포어 생성

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

sem에는 생성된 posix 세마포어를 저장한다.

pshared에는 현재 프로세스에서만 사용할 때는 0을 두고, 여러 프로세스에서 공유를 목적으로 생성할 때는 0이 아닌 값을 둔다.

value는 초기화할 세모포어의 값으로 리소스의 수를 의미한다.

 

 

명명된 세마포어 생성

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

저수준 open함수와 유사하고 결과로 세마포어를 얻는다. name은 항상 / 로 시작한다

value에는 익명 세마포어와 동일하게 초기화할 세마포어의 값으로 리소스의 수를 둔다.

 

생성된 세마포어는 /dev/shm/ 의 경로에 생성된다.

posix 공유 메모리 처럼 "/m.sem" 파일을 sem_open 하면,

/dev/shm/sem.m.set 파일이 생성된다.

 

oflag에는 O_CREATE와 O_EXCL이 사용 가능하다.

실패 시에는 SEM_FAILED를 반환한다.

 

 

posix 세마포어는 스레드 라이브러리를 사용하여 컴파일 할때 링크를 해주어야한다.

gcc -o source source.c -pthread

 

 

posix 세마포어 연산 : P

#include <semaphore.h>

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

//timespec
struct timepec{
	time_t tv_sec;
    long tv_nsec;
};

posix 세마포어는 한 번에 -1씩만 가능하다. 리소스가 없는 경우(value == 0)이면 대기한다.

 

sem_wait는 리소스가 없으면 대기하고,

sem_trywait는 리소스가 없으면 에러를 반환하고, errno에 EAGAIN이 설정된다.

sem_timedwait는 리소스가 없으면 지정한 시간만큼 대기하고, 대기 이후에도 없으면 에러를 반환하고,

errno에 ETIMEOUT이 설정된다.

 

sem_timedwait는 UNIX 시간 표현으로 절대시간을 이용한다. 10초 대기를 하려면 현재 시간에 +10초를 해야 한다.

struct timespec ts_timeout;
ts_timeout.tv_sec = time(NULL) + 10;
ts_timeout.tv_nsec = 0;

if(sem_timedwait(psem, &ts_timeout) == -1){
	if(errno = ETIMEOUT){ /* handle timeout */}
	else { /* Otherwise */ }
}

 

posix 세마포어 연산 : V

세마포어에서 V 연산은 리소스를 +1 한다. 대기하는 경우는 없다. 실패하는 경우는

최대값(SEM_VALUE_MAX)보다 커지는 경우로, errno에 EOVERFLOW가 설정된다.

int sem_post(sem_t *sem)

 

 

 

posix 세마포어의 제거

익명 세마포어를 제거할 때는 sem_destroy를 사용한다.

프로세스가 종료되면 자연스럽게 세마포어가 해제되고, 직접 제거하고자 하는 경우에 사용한다.

int sem_destroy(sem_t *sem);

 

명명된 세마포어를 제거할 때는 sem_close, sem_unlink를 사용한다.

int sem_close(sem_t *sem);
int sem_unlink(const char *name);

 

sem_open에 대응하는 sem_close를 통해 닫는다.

생성된 세마포어 파일을 삭제하기 위해 sem_unlink를 사용한다.

+ Recent posts