[UNIX] 세마포어

Taegang Yun·2023년 12월 9일
1

Unix 프로그래밍

목록 보기
17/19

세마포어의 기본 동작 구조

  • 세마포어는 중요한 처리 부분에 들어가기 전에 **p()** 함수를 실행해 잠금 기능을 수행
  • 처리를 마치면 v() 함수를 실행해 잠금을 해체
  • 잠금 기능을 수행 중인 동안에는 다른 프로세스가 처리 부분의 코드를 실행할 수 없다.
  • sem은 세마포어 값을 의미
p(sem); /*lock*/

/*Critical Section*/

v(sem); /*unlock*/

p()

p(sem){
	while sem == 0 do wait;
	sem--;
}
  • sem이 0이면 다른 프로세스가 처리 부분을 수행하고 있다는 의미.

v()

v(sem){
	sem++;
	if(대기 중 프로세스가 있으면)
		대기 중인 첫 번째 프로세스 동작
}

세마포어 생성 : semget()

  • key : IPC_PRIVATE 또는 ftok() 함수로 생성한 키를 지정
  • nsems 에는 생성할 세마포어 개수를 지정
  • 세마포어는 집합 단위로 처리되므로 한 식별자에 여러 세마포어가 새애성.
  • 플래그는 IPC_CREAT, IPC_EXCL을 사용

세마포어 정보를 담고 있는 구조체는 shmid_ds

  • sem_perm : IPC 공통 구조체
  • sem_otime : 세마포어 연산을 수행한 마지막 시간
  • sem_ctime : 세마포어 접근권한 변경 마지막 시간
  • sem_nsems : 세마포어 집합의 세마포어 개수
  • 새로운 세마포어 식별자를 리턴할 때 구조체의 설정
    • sem_perm.cuid, sem_perm.uid : 함수를 호출한 프로세스의 유효 사용자 ID로 설정
    • sem_perm.cgid, sem_perm.gid : 함수를 호출한 프로세스의 유효 그룹 ID로 설정
    • sem_perm.mode : semflg 값으로 설정
    • sem_nsems : nsems 값으로 설정, 세마포어 집합이 생성되지 않으면 0으로 설정될 수도 있음
    • sem_otime : 0으로 설정
    • sem_ctime : 현재 시각으로 설정
  • 세마포어에 연관된 값
    • semval : 세마포어의 값
    • sempid : 세마포어에 최근 접근했던 프로세스의 프로세스 식별번호
    • semncnt : 세마포어의 값이 현재보다 더 큰 값을 갖기를 기다리는 프로세스의 수
    • semzcnt : 세마포어의 값이 0이 되기를 기다리는 프로세스의 수

세마포어 제어 : semctl()

  • 세 번째 인자인 cmd
    • IPC_STAT : 현재 세마포어 정보를 arg.buf로 지정한 메모리에 저장
    • iPC_SET : 지정한 값으로 변경
    • IPC_RMID : semid로 지정한 세마포어 관련 구조체를 제거
    • IPC_INFO
    • 구조체 항
      • GETALL : 세마포어 집합에 있는 모든 세마포어의 semval 값을 arg.array가 가리키는 배열에 저장
      • GETNCNT : 세마포어의 semncnt 값을 읽어옴
      • GETVAL : 세마포어의 semval 값을 읽어옴
      • SETVAL : 세마포어의 semval 값을 arg.val로 설정
      • GETPID : 세마포어의 sempid 값을 읽어옴
      • GETZCNT : 세마포어의 semzcnt 값을 읽어옴
      • SETALL : 세마포어 집합에 있는 모든 세마포어의 semval 값을 arg.array가 가리키는 배열값으로 설정

세마포어 연산 : semop()

semid가 가리키는 세마포어에 크기가 nsops인 sembuf 구조체로 지정한 연산을 실행.

  • sem_flg ; IPC_NOWAIT나 SEM_UNDO를 지정
    • SEM_UNDO : 프로세스가 비정상적으로 갑자기 종료될 때 세마포어 동작 취소.

sem_op < 0 일 때

세마포어 잠금 기능을 수행. 공유 자원 사용 목적

  • semval ≥ |sem_op| → semval -= |sem_op|
  • semval < sem_op && semflg == IPC_NOWAIT → return
  • semval < && semflg ≠ IPC_NOWAIT → semnncnt++
  • 시스템에서 semid가 제거, 이 경우 errno가 EIDRM

sem_op > 0 일 때

세마포어 잠금 해제, 사용 중이던 공유 자원 돌려줌

sem_op 값이 semval 값에 더해짐.

sem_op == 0 일 때

  • semval 값이 0이면 semop() 함수는 즉시 리턴
  • semval 값이 0이 아니고 sem_flg에 IPC_NOWAIT이 설정되어 있으면 semop() 함수는 즉시 리턴
  • semval 값이 0이 아니고 sem_flg에 IPC_NOWAIT이 설정되어 있지 않으면 semop() 함수는 semzcnt 값을 증가시키고 semval 값이 0이 되기를 기다림

semval = 세마포어 현재 값

sem_op = 세마포어 연산 1, 0, -1 ?

세마포어 생성과 초기화

void initsem(key_t semkey)

union semun{
	int val;
	struct shmid_ds *buf;
	unsigned short *array;
}

int initsem(key_t semkey){
	union semun semunarg;
	int status = 0, semid;

	semid = semget(semkey, 1, IPC_CREAT|IPC_EXCL|0600);
	if(semid == -1){
		if(errno == EEXIST) // -1이고 EEXIST면 이미 존재하는 식별자, 기존 식별자 읽어옴
			semid = semget(semkey, 1, 0); 
	}
	else{
		semunarg.val = 1;
		status = semctl(semid, 0, SETVAL, semunargs);
	}
	return semid;
}

세마포어 연산

void semlock(int semid)

int semlock(int semid){
	struct sembuf buf;

	buf.sem_num = 0;
	buf.sem_op = -1;
	buf.sem_flg = SEM_UNDO;
	
	return 0;
}

void semunlock(int semid)

int semunlock(int semid){
	struct sembuf buf;

	buf.sem_num = 0;
	buf.sem_op = 1;
	buf.sem_op = SEM_UNDO; //문제 발생 시 동작 취소

	return 0;
}

void semhandle()

void semhandle(){
	int semid;
	pid_t pid = getpid();

	if((semid = initsem(1)) < 0) exit(1);

	semlock(semid);
	
	/* Critical Section */
	
	semunlock(semid);

	exit(0);
}

int main(){
	int a;
	for(a = 0 ; a < 3; a++)
		if(fork() == 0)) semhandle();
}
profile
언젠간 전문가가 되겠지

0개의 댓글