semaphore
- 세마포는 양의 정수로 선언할 수 있다.
세마포의 2가지 기능
- semWait(); → 세마포-1을 하고 세마포 ≥ 0 이라면 지나가고 아니라면 세마포 ≥ 0 이 될 때까지 Block한다.
- 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);
// 플레그를 지정을 안햇는데 원래는 플레그값까지 지정을 잘 해줘야함!!
// 안 그러면 원하지 않은 결과 나와버릴지두..
같이 공부해볼 것
- 크리티컬 섹션
- 리더스 라이터스 문제
'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 |