[CS] IPC 세마포어

윤동환·2023년 4월 4일
0

Computer Science

목록 보기
7/10
post-thumbnail

세마포어

세마포어는 Message queue, Share memory의 프로세스간 메시지 전송의 목적과는 다르게 프로세스간 데이터를 동기화하고 보호하는데 목적이 있습니다.

semid_ds 구조체

커널에서 세마포어 정보를 유지하기위해 관리하는 구조체입니다.
이 구조는 semid_ds 유형 이며 다음과 같이 <sys/sem.h> 에 정의되어 있습니다.

/* 시스템의 각 세마포 세트에 대한 하나의 semid 데이터 구조. */
struct semid_ds {
	struct ipc_perm sem_perm;  /* Ownership and permissions */
	time_t          sem_otime; /* Last semop time */
    time_t          sem_ctime; /* Last change time */
    unsigned long   sem_nsems; /* No. of semaphores in set */
}

내부 변수들

  • sem_perm
    이것은 linux/ipc.h 에 정의된 ipc_perm 구조 의 인스턴스입니다.
    여기에는 액세스 권한 및 세트 생성자에 대한 정보(uid 등)를 포함하여 세마포어 세트에 대한 권한 정보가 포함됩니다.
    -> (세마포어 구조체에 접근할 수 있는 사용자 권한 설정)

  • sem_otime
    마지막 semop() 작업 시간(자세한 내용은 잠시 후)

  • sem_ctime
    이 구조에 대한 마지막 변경 시간(모드 변경 등)

  • sem_nsems
    세마포어 세트(배열)의 세마포어 수


sem 구조체

semid_ds 구조 에는 세마포어 배열 자체의 베이스에 대한 포인터가 있습니다.
각 배열 구성원은 sem 구조 유형 입니다. linux/sem.h 에도 정의되어 있습니다.

/* One semaphore structure for each semaphore in the system. */
struct sem {
	short   sempid;         /* pid of last operation */
	ushort  semval;         /* current value */
	ushort  semncnt;        /* num procs awaiting increase in semval */
	ushort  semzcnt;        /* num procs awaiting semval = 0 */
};

내부 변수들

  • sem_pid
    마지막 작업을 수행한 PID(프로세스 ID)

  • sem_semval
    세마포어의 현재 값

  • sem_semncnt
    리소스를 사용할 수 있기를 기다리는 프로세스 수

  • sem_semzcnt
    리소스 사용률 100%를 기다리는 프로세스 수


semget(2)

이 시스템 호출은 System V 세마포어 세트를 생성하거나 할당합니다.
커널은 semget 를 통해 넘어온 정보를 이용해서 semid_ds 구조체를 세팅합니다.

int semget(key_t key, int nsems, int semflg);

매개변수

  • key : 세매포어 식별자를 생성하기위한 고유한 값(메시지 큐를 인식합니다)
  • nsems : 생성할 세마포어 개수(새 세트에서 작성해야 하는 세마포어 수를 지정 -> 바이너리 세마포어는 1, 카운팅 세마포어는 1 이상)
  • semflg : 세마포어 접근 속성, IPC_CREAT(새로운 키면 식별자 새로 생성), IPC_EXCL(이미 존재하는 키면 오류 발생) 두가지 설정 가능

    IPC_EXCL은 그 자체로는 쓸모가 없지만 IPC_CREAT 와 결합하면 기존 세마포 세트가 액세스를 위해 열리지 않도록 보장하는 기능으로 사용할 수 있습니다.

세트의 세마포어의 최대 수는 ``linux/sem.h''에 다음과 같이 정의되어 있습니다:

#define SEMMSL 32 /* <=512 ID당 최대 세마포 수 */

기존 세트를 명시적 으로 여는 경우 nsems 인수는 무시됩니다 .

return 값

반환값: 성공 시 세마포어 세트 IPC 식별자
오류 시 -1 : errno

errno

  • EACCESS(허가 거부됨)
  • EEXIST(세트 존재, 생성할 수 없음(IPC_EXCL))
  • EIDRM(집합이 삭제 표시됨)
  • ENOENT(세트(큐)가 존재하지 않음, IPC_CREAT가 사용되지 않음)
  • ENOMEM(새 집합(큐)을 만들기에 메모리가 충분하지 않음)
  • ENOSPC(최대 설정 한도 초과)

새로운 세마포어 리턴시 초기화 값

  • sem_perm.cuid, sem_perm.uid: 함수를 호출한 프로세스의 유효 사용자 ID로 설정된다.
  • sem_perm.cgid, sem_perm.gid: 함수를 호출한 프로세스의 유효 그룹 ID로 설정된다.
  • sem_perm.mode: semflg 값으로 설정된다.
  • sem_nsems: nsems 값으로 설정된다.
  • sem_otime: 0으로 설정된다.
  • sem_ctime: 현재 시각으로 설정된다.

semop(2)

이 시스템 호출은 System V 세마포어 세트에서 리소스 할당, 리소스 대기 또는 리소스 해제 작업을 수행합니다.

int semop(int semid, struct sembuf *sops, size_t nsops);
  • sedmid: semget 함수로 생성한 세마포어 식별자
  • sops: 세마포어 집합에서 수행할 작업 배열에 대한 포인터
  • nsops: 해당 배열의 작업 수

반환값

RETURNS: 성공 시 0(모든 작업 수행)
오류 시 -1 : errno

errno

  • E2BIG(원자적으로 허용되는 최대 작업 수보다 큰 nsops)
  • EACCES(권한 거부됨, 사용자 namespace에 CAP_IPC_OWNER 기능이 없음)
  • EAGAIN(IPC_NOWAIT지정 혹은 timeout, 작업을 진행할 수 없음)
  • EFAULT(sops 인수가 가리키는 유효하지 않은 주소)
  • EFBIG(일부 작업의 경우 sem_num 값이 0보다 작거나 세트의 세마포어 수보다 크거나 같음)
  • EIDRM(세마포어 세트가 제거됨)
  • EINTR(수면 중 signal 수신)
  • EINVAL(세트가 존재하지 않거나 semid가 유효하지 않거나, nsops에 양수가 아닌값 있음)
  • ENOMEM(SEM_UNDO flag 지정, 메모리가 부족)
  • ERANGE(세마포어 값이 범위를 벗어남)

에러 값

sops가 가리키는 배열의 각 nsops 요소는 단일 세마포어에서 수행할 작업을 지정하는 구조입니다.

세마포어 buf 구조체

unsigned short sem_num;  /* semaphore number */
short          sem_op;   /* semaphore operation */
short          sem_flg;  /* operation flags */

flag 옵션에 IPC_NOWAIT을 주어 이미 세마포어 lock이 걸려있다면 에러 발생 가능. 0을 주면 unlock까지 대기

내부 변수들

  • sem_num
    처리하려는 세마포어의 번호

  • sem_op
    수행할 작업(양수, 음수 또는 0)

  • sem_flg
    운영 플래그
    sem_flg에서 인식되는 플래그는 IPC_NOWAIT 및 SEM_UNDO입니다.
    작업이 SEM_UNDO를 지정하면 프로세스가 종료될 때 자동으로 취소됩니다.

sops에 포함된 일련의 작업은 배열 순서로 수행되며 원자적으로 수행됩니다. 즉, 작업이 완전한 단위로 수행되거나 전혀 수행되지 않습니다. 모든 작업을 즉시 수행할 수 없는 경우 시스템 호출의 동작은 아래에 설명된 대로 개별 sem_flg 필드에 IPC_NOWAIT 플래그가 있는지에 따라 달라집니다.

각 작업은 세마포어 집합의 sem_num 번째 세마포어에서 수행되며, 집합의 첫 번째 세마포어는 0으로 번호가 매겨집니다. sem_op 값으로 구분되는 세 가지 유형의 작업이 있습니다.

  1. sem_op 이 음수 이면 해당 값을 세마포어에서 뺍니다. 이는 세마포어가 액세스를 제어하거나 모니터링하는 리소스를 얻는 것과 관련이 있습니다. IPC_NOWAIT가 지정되지 않은 경우 호출 프로세스는 세마포어에서 요청된 리소스 양을 사용할 수 있을 때까지 휴면합니다(다른 프로세스가 일부를 해제함).

  2. sem_op이 양수 이면 해당 값이 세마포어에 추가됩니다. 이는 애플리케이션의 세마포어 세트로 리소스를 다시 반환하는 것과 관련이 있습니다. 리소스는 더 이상 필요하지 않을 때 항상 세마포 세트로 반환되어야 합니다!

  3. sem_op 가 0이면 호출 프로세스는 세마포어의 값이 0이 될 때까지 sleep()합니다. 이는 세마포어가 100% 활용도에 도달할 때까지 기다리는 것과 관련이 있습니다. 이에 대한 좋은 예는 전체 사용률에 도달할 경우 세마포 세트의 크기를 동적으로 조정할 수 있는 수퍼유저 권한으로 실행되는 데몬입니다.


semctl(2)

세마포어세트에서 제어하는 작업을 수행합니다.

int semctl ( int semid, int semnum, int cmd, union semun arg );
  • semid: semget 함수로 생성한 세마포어 식별자
  • semnum: 기능을 제어할 세마포어 번호
  • cmd: 수행할 제어 명령
  • arg: 제어 명령에 따라 필요시 사용할 세마포어 공용체의 주소(선택 사항)

    세마포어는 실제로 단일 엔터티가 아니라 집합으로 구현됩니다.
    세마포어 작업에서 IPC 키 뿐만 아니라 세트 내의 대산 세마포어도 전달해야합니다.

반환 값

반환값: 성공 시 양의 정수
오류 시 -1: errno

성공 시

IPC_INFO, SEM_INFO, SEM_STAT : 세마포어별로 가장 많이 사용된 항목의 인덱스 또는 식별자를 반환
GETNCNT : semncnt 값 반환
GETPID : sempid 값 반환
GETVAL : semval 값 반환

errno

  • EACCESS(허가 거부됨)
  • EFAULT(인수 인수가 가리키는 유효하지 않은 주소)
  • EIDRM(세마포어 세트가 제거됨)
  • EINVAL(세트가 존재하지 않거나 semid가 유효하지 않음)
  • EPERM(EUID는 arg의 cmd에 대한 권한이 없음)
  • ERANGE(세마포어 값이 범위를 벗어남)

cmd 지정 가능 값

IPC_RMID: semid로 지정한 세마포어와 관련된 데이터 구조체를 제거한다.
IPC_SET: 세마포어 정보 내용 중 sem_perm.uid, sem_perm.gid, sem_perm.mode 값을 네 번째 인자로 지정한 값으로 변경한다. 이 명령은 root 권한이 있거나 유효 사용자 ID일 경우만 사용할 수 있다.
IPC_STAT: 현재 세마포어의 정보를 arg.buf로 지정한 메모리에 저장한다.
GETVAL: 세마포어의 semval 값을 읽어온다.
SETVAL: 세마포어의 semval 값을 arg.val로 설정한다.
GETPID: 세마포어의 sempid 값을 읽어온다.
GETNCNT: 세마포어의 semncnt 값을 읽어온다.
GETZCNT: 세마포어의 semzcnt 값을 읽어온다.
GETALL: 세마포어 집합에 있는 모든 세마포어의 semval 값을 arg.array가 가리키는 배열에 저장한다.
SETALL: 세마포어 집합에 있는 모든 세마포어의 semval 값을 arg.array가 가리키는 배열값으로 설정한다.

cmd 설정 값에 따라서 리턴하는 값이 달라집니다.
GETVAL이면 semval 값을 리턴하고, GETPID면 sempid 값을 리턴합니다.
semctl 함수는 수행을 성공하면 0을, 오류가 발생하면 모든 경우에 -1을 리턴합니다.

Union semun

arg 인수 는 semun 유형의 인스턴스를 나타냅니다 . 이 특정 공용체는 다음과 같이 linux/sem.h 에서 선언됩니다 .

/* semctl 시스템 호출을 위한 인수. */ 
union { 
	int val; /* SETVAL의 값 */ 
	struct semid_ds *buf; /* IPC_STAT & IPC_SET용 버퍼 */ 
	ushort *array; /* GETALL & SETALL용 배열 */ 
	struct seminfo *__buf; /* IPC_INFO용 버퍼 */ 
	void *__pad; 
};

내부 변수

  • value
    SETVAL 명령을 수행할 때 사용됩니다. 세마포어를 설정할 값을 지정합니다.

  • buf
    IPC_STAT/IPC_SET 명령에 사용됩니다. 커널에서 사용되는 내부 세마포어 데이터 구조의 복사본을 나타냅니다.

  • array
    GETALL/SETALL 명령에 사용되는 포인터. 세트의 모든 세마포어 값을 설정하거나 검색하는 데 사용할 정수 값의 배열을 가리켜야 합니다.

    나머지 인수 __buf 및 __pad 는 커널 내의 세마포어 코드에서 내부적으로 사용되며 응용 프로그램 개발자에게는 거의 또는 전혀 쓸모가 없습니다. 사실, 이 두 인수는 Linux 운영 체제에만 해당되며 다른 UNIX 구현에서는 찾을 수 없습니다.

Reference

https://12bme.tistory.com/227
https://tldp.org/LDP/lpg/node21.html
https://www.geeksforgeeks.org/ipc-full-form/
https://www.tutorialspoint.com/inter_process_communication/inter_process_communication_semaphores.htm#

profile
모르면 공부하고 알게되면 공유하는 개발자

0개의 댓글