CS(Computer Science)/UNIX

UNIX - [Semaphore]

seongmik 2023. 1. 1. 21:00
반응형

semaphore

  • 세마포는 양의 정수로 선언할 수 있다.

 

세마포의 2가지 기능

  1. semWait(); → 세마포-1을 하고 세마포 ≥ 0 이라면 지나가고 아니라면 세마포 ≥ 0 이 될 때까지 Block한다.
  2. semSig(); → 세마포+1을 한다.
p(sem); // semWait
something interesting; // 크리티컬섹션
v(sem); // semSig

 

semget 시스템 호출

사용법

#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>

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

/*
> key : semaphore 집합 이름
> nsems : semaphore 집합 내의 semaphore 수
> permflags : 0600, IPC_CREAT, IPC_EXCL, ...
> return 값 : semaphore 집합 identifier
*/

몇 번 세마포 시그널에 대해서 wait와 sig를 할 지에 대해 설정해줄수있고, 꼭 설정해줘야한다!

 

집합 내 각 semaphore와 연관된 값

  • semval : semaphore 값 (semapahore 값의 초기화 필요)
  • sempid : 최근 semaphore를 access 한 process id
  • semncnt : semaphore 값이 증가하기를 기다리는 process 수
  • semzcnt : semaphore 값이 0이 되기를 기다리는 process 수

→ 원래 세마포 변수는 Queue인데 유닉스의 세마포는 큐를 2개를 가지고있다.

 

semctl 시스템 호출

#include <sys/sem.h>

int semctl (int semid, int sem_num, int command, union semun arg);

/*
> semid : semaphore identifier
> sem_num : 집합 내 특정 semaphore 지정 // 집합 전체한테 할 때는 그냥 0
> command
	> IPC_STAT : 상태 정보를 arg.stat에 저장
	> IPC_RMID : semaphore 집합 삭제
*/

메세지 큐를 사용할 때 메세지 컨트롤 명령을 사용하면 메세지 큐에 메세지가 몇 개가 들어있는지 궁금하면 찾아볼 수 있다. 하지만 필수는 아니다.

그에 비해 세마포는 semctl 명령이 필수이다. 왜냐하면 세마포 컨트롤 명령으로 세마포를 초기화하기 때문이다.

먼저 semget()으로 입력받은 세마포 Id를 쓰고, 그 뒤에 몇 번 세마포에 대한건지 지정할 수 있다. 그 뒤에 어떤 작업을 수행할 지 지정할 수 있다.

그 후, 많은 작업을 하는데 그 중 어떤 작업은 세마포 집합에서 특정 세마포에대해만 특정 작업을 할 수 있다. 그럼 세마포 번호를 적어야하고 그냥 전부한테 하면 0을 적으면된다.

4번째인자 command를 수행하면 OS가 명령을 수행한 결과를 준다. 4번째 인자를 통해서 값을 볼 수 있다.

return값은 그냥 성공했냐 실패했냐만 리턴된다.

IPC_RMID를 쓸 때는 전달하고 전달받을 데이터가 없으므로 그냥 NULL을 4번째 인자에 넣어주면된다.

  • command = 단일 semaphore에 영향을 미치는 기능
    • GETVAL : semval 값 return → 세마포 초기화할 때 사용
    • SETVAL : semval 값을 arg.val 값으로 지정
    • GETPID : sempid 값을 return
    • GETNCNT : semncnt 값을 return
    • GETZCNT : semzcnt 값을 return
  • command = semaphore 집합 전체에 영향을 미치는 기능
    • GETALL : 모든 semval 값을 arg.array에 저장 → 마지막인자인 arg에 모든 세마포값을 집어 넣는다.
    • SETALL : arg.array 값으로 모든 semval 값을 지정

 

struct semun arg :

union semun{
		int val; 
		struct semid_ds *buf;
		unsigned short *array;
};
// union : 이 안에 3개의 값중에 하나를 골라서 가지고있는 변수로 만든다.
// 세개중 여러개를 동시에 사용할 수는 없다. -> 그것이 union타입이니깐..

/*
buf와 array는 포인트이다!!!!!!!!!!!
따라서 쓸 때
union semun arg;
struct semid_ds temp;
arg.buf = &temp;
와 같이 써야한다.
*/

 

semaphore 만들고 초기값 설정 하기

union semun{ 
		int val;
		struct semid_ds *buf;
		ushort *array;
};

union semun arg;
semid=semget((key_t) 0123, 1, 0600|IPC_CREAT|IPC_EXCL);
arg.val=3;

semctl(semid, 0, SETVAL, arg);
union semun{
		int val;
		struct semid_ds *buf;
		ushort *array;
};

union semun arg;
ushort buf[3];
semid=semget((key_t) 0246, 3, 0600|IPC_CREAT|IPC_EXCL);
// IPC_EXCL을 사용하는 이유? : 세마포를 제일 처음 만든 사람만 세마포에 접근할 수 있고 그 이후에 만든 사람은 -1을 받게되서 세마포를 초기화할 수 없다. 세마포를 사용하고 계속 초기화하는 대참사 방지.

for (i=0; i<3; i++)
		buf[i]=i+1; arg.array=buf;

semctl(semid, 0, SETALL, arg);

→ “semctl(semid, 0, SETALL, arg);” 는 모든 세마포를 사용하는 명령 뒤에 적도록하자.

주의

semget을 해서 세마포를 만들고 arg.val = 정수 를 했더라도, 결과적으로 semctl을 사용해서 세마포의 값을 초기화하지않으면 세마포의 값은 변하지 않는다.

 

semop 시스템 호출

사용법

#include <sys/sem.h>

int semop(int semid, struct sembuf *op_array, size_t num_ops);

/*
> semid : semaphore identifier
> oparray : 수행 할 연산 지정
> num_ops : op_array내의 sembuf의 수 (여러 개의 semaphore에 대한 연산을 동시에 지정 할 수 있음.)
*/
struct sembuf{
		unsigned short sem_num;
		short sem_op;
		short sem_flg;
};

/*
> sem_num : semaphore index
> sem_op : 수행 할 연산 (양의 정수 또는 음의 정수 또는 0) // 더하고 빼는 값을 조절할 수 있는 아주 강력한 연산이다.
> sem_flg : IPC_NOWAIT or SEM_UNDO
*/
// IPC_NOWAIT : 크리티컬 섹션에 누군가 있으면 안 기다리고 그냥 포기!
// SEM_UNDO : 지금까지 내가 세마포에 대해 했던 모든 작업을 UNDO하겠다는 것. 근데 웬만하면 쓰지마라 내 생각과 좀 다르게 행동할 것임

sem_op : 수행할 연산

  • 음수 : p() or wait() 연산
if (semval >= |sem_op|)
		set semval to semval - |sem_op|;
else{
		wait until semval reaches or exceeds |sem_op|;
		then set semval to semval - |sem_op|;
}

→ 유닉스의 세마포는 fifo가 아니다. 내가 세마포에서 몇을 빼냐에 따라서 먼저 깨워지냐 아니냐가 달라진다.

프로세스 마다 세마포에서 뺴는 값을 다르게 만들어서 다양한 방법으로 동기화 할 수 있다.

유닉스에서는 세마포에서 값을 +1하거나 -1만 하는게 아니고 원하는 정수만큼 할 수 있다.!!!!!!!!!!!!!!!!

→ 생각을 많이하면 원래 세마포 5개써서 해야할 일을 세마포1개 써서 할 수 있다.

 

semaphore 연산 실행의 예

struct sembuf p_buf;
p_buf.sem_num=0;
p_buf.sem_op=-1;
semop(semid, &p_buf, 1);
printf("process %d in critical section\\n", pid);
sleep(10);
printf("process %d leaving critical section\\n", pid);
p_buf.sem_num=0;
p_buf.sem_op=1;
semop(semid, &p_buf, 1);
// 플레그를 지정을 안햇는데 원래는 플레그값까지 지정을 잘 해줘야함!!
// 안 그러면 원하지 않은 결과 나와버릴지두..

 

 

같이 공부해볼 것

  1. 크리티컬 섹션
  2. 리더스 라이터스 문제
반응형

'CS(Computer Science) > UNIX' 카테고리의 다른 글

UNIX - [Record Locking]  (0) 2023.01.03
UNIX - [Shared memory]  (0) 2023.01.02
UNIX - [시스템 V의 프로세스간 통신 - Message queue]  (0) 2023.01.01
UNIX - [PIPE - 2]  (0) 2022.12.31
UNIX - [PIPE]  (3) 2022.12.30