슬라이드 1

Download Report

Transcript 슬라이드 1

Task 통신 및 동기화 :
Message Queue, Semaphore
Shared Memory
Chapter #12
강의 목차
▶
▶
▶
▶
IPC(Inter-Process Communication)
메시지 큐(Message Queue)
세마포어(Semaphore)
공유 메모리(Shared Memory)
Unix System Programming
2
IPC(Inter-Process
Communication)
IPC(Inter-Process Communication) (1)

프로세스 통신 도구(IPC Facilites) :


비동기적으로 동작하는 프로세스간의 통신을 위하여 운영체
제가 제공하는 도구
종류:






Signal(신호)
Pipe(파이프)
Message Queue(메시지 큐)
Semaphore(세마포어)
Shared Memory(공유 메모리)
Socket(소켓)
Linux System Programming
동일한 플랫폼 상에
서 유사하게 구현되
어 ICP 도구로 별도
분류하기도 함
4
IPC(Inter-Process Communication) (2)

프로세스 통신 도구 (IPC Facilites) :

세마포어(Semaphore)
프로세스 동기화와 자원 관리에 사용
 프로세스간의 상호배제 문제 해결에 사용


공유 메모리(Shared Memory)
가장 빠른 프로세스 통신 도구
 하나 이상의 프로세스에 부착하여 서로 공유하여 접근 가능
 프로세스간의 공유 메모리 접근을 위해 동기화가 필요


메시지 큐(Message Queue)
프로세스들간에 이산적인 양의 데이터 송수신을 위해 사용
 전송할 데이터를 메시지 형태로 생성하여 전달하고 메시지 수
신을 통해 데이터 수신이 가능
 인터넷 메일 서비스와 개념이 유사

Linux System Programming
5
IPC(Inter-Process Communication) (3)

IPC와 관련한 시스템 호출 함수:

IPC 시스템 호출 함수:
get
ctl
op
세마포어
semget
semctl
semop
공유 메모리
shmget
shmctl
shmop(shmat,
shmdt)
메시지큐
msgget
msgctl
msgop(msgsnd,
msgrcv)
기능
IPC 도구의 생성 및
참조 기능 제공
소유자 및 접근 허
가 변경 등의 기능
제공
IPC 도구의 연산(송
수신 제어 등) 기능
제공
Linux System Programming
6
IPC(Inter-Process Communication) (4)

IPC와 관련한 시스템 호출 함수:

get 시스템 호출
파일 열기 시스템 호출 open과 유사
 새롭게 통신 도구를 생성하거나 이미 생성된 통신 도구에 대한
id를 반환하여 접근 가능하게 함
 반환된 id는 파일 디스크립터와 비슷하게 동작
 접근 허가 또한 파일 접근 허가와 비슷하게 규정


ctl 시스템 호출
통신 도구에 관한 상태 정보 읽기
 몇몇 상태 정보(소유자, 그룹, 접근 허가 등)에 대한 변경
 통신 도구의 제거 기능


op 시스템 호출
프로세스간의 데이터 전송을 실행
 파일 시스템 호출 read/write와 유사
 Shmat는 파일의 open과 유사하고, shmdt는 파일의 close와 유사

Linux System Programming
7
IPC(Inter-Process Communication) (5)

IPC 도구 식별자:

IPC 도구 식별자 – IPC key
파일을 식별하기 위해 파일 이름을 사용하듯이 IPC 객체를 식
별하기 위해 사용하는 정수형 숫자
 프로세스들은 IPC key를 이용하여 IPC 자원을 공유
 자료형: key_t (<sys/types.h>에 정의)

key_t
char

device
Inode # of file
IPC key 변환 시스템 호출 – ftok(file_to_key)

IPC 도구의 공유를 편리하게 지원하기 위해 파일 경로명과 식
별자를 IPC 도구에 부여하고, 이를 IPC key 값으로 변환하는 시
스템 호출
Linux System Programming
8
IPC(Inter-Process Communication) (6)

IPC 도구 식별자:

IPC key 변환 시스템 호출 – ftok(file_to_key)
Linux System Programming
9
IPC(Inter-Process Communication) (7)

IPC 도구의 상태 자료 구조:



생성된 IPC 객체에 대한 상태 정보를 저장하는 구조체
IPC 도구 유형별로 별도의 상태 구조체를 지원하나 다음의
접근 허가 구조를 공통으로 포함한다
IPC 도구의 접근 허가 구조: struct ipc_perm
파일의 접근 허가와 유사한 기능을 지원
 접근 모드에서 실행 모드는 무의미함

Linux System Programming
10
IPC(Inter-Process Communication) (8)

IPC 도구 테이블



IPC 도구 유형별로 생성된 IPC 객체를 관리하기 위해 운영체
제가 유지하는 테이블
IPC 도구 유형별로 각각의 테이블을 유지
파일 테이블과 유사한 개념
Linux System Programming
11
IPC(Inter-Process Communication) (9)

IPC 도구 관리 명령:

ipcs 명령
현재 사용중인 IPC 도구들의 상태(status)를 보여주는 명령
 IPC 타입, 사용자 ID, key 값 그리고 접근 허가 등을 볼 수 있음
 옵션을 사용하여 지정된 유형의 IPC 도구 상태 정보만을 접근
가능



-q : 메시지 큐, -m : 공유 메모리, -s : 세마포어
ipcrm 명령
IPC 객체를 제거하는 명령
 IPC 도구 유형 및 key 값을 지정하는 원하는 IPC 객체를 제거

Linux System Programming
12
Message Queue
메시지 큐(Message Queue) (1)

메시지 큐(Message Queue)



프로세스간에 이산적인 데이터를 메시지 형태로 전송하는
통신 도구
파이프와 같이 FIFO 타입의 데이터 전송을 지원
메시지는 메시지와 바이트(byte)들의 모임으로 구성
메시지 유형은 정수값으로 여러가지 메시지 형태에 id를 부여
하여 구별하기 위해 사용
 바이트 모임은 전송하는 데이터를 의미하는 것으로 문자, 그림
또는 일련의 구조체 데이터 등을 전송하기 위햇 사용


관련 시스템 호출 함수
msgget() – 메시지 큐를 생성
 msgsnd()/msgrcv() – 메시지 전송 및 수신
 msgctl() – 메시지 큐 제어

Linux System Programming
14
메시지 큐(Message Queue) (2)

메시지 큐의 구조:

메시지 큐의 상태 구조
체 : msgid_ds
메시지 큐 객체의 상
태 정보를 저장하는
구조체
 하나의 메시지 큐가
생성되면 하나의
msgid_ds 구조체가
생성

Linux System Programming
15
메시지 큐(Message Queue) (3)

메시지 큐의 동작:



전송 프로세스가 전송할 데이터를 메시지 형태로 만들어 메
시지를 전송하면 전송된 메시지는 메시지 큐의 마지막에 연
결된다
수신 프로세스는 메시지 큐에 존재하는 메시지를 선택적으
로 수신할 수 있다.
수신 프로세스에서는 메시지를 수신할 때에 다음의 수신 정
책 중에 하나를 지정하여야 한다.
메시지 큐에서 첫번째 메시지
 메시지 큐에서 지정된 타입의 첫번째 메시지
 메시지 큐에 있는 타입들의 범위들로 부터 첫번째 메시지

Linux System Programming
16
메시지 큐(Message Queue) (4)

메시지 큐의 생성: msgget()


메시지 큐 생성 시스템 호출
주어진 key를 갖는 메시지 큐가 존재하면 메시지 큐의 id를
반환하고, 그렇지 않으면 새롭게 메시지 큐를 생성하고 id를
반환
Linux System Programming
17
메시지 큐(Message Queue) (5)
Linux System Programming
18
메시지 큐(Message Queue) (6)

메시지 큐의 생성: msgget()

msgget() 시스템 호출의 flag 옵션
Linux System Programming
19
메시지 큐(Message Queue) (7)

메시지 큐 구조체: msqid_ds
Linux System Programming
20
메시지 큐(Message Queue) (8)

메시지 큐 구조체 초기값
Linux System Programming
21
메시지 큐(Message Queue) (9)

메시지 큐의 제어: msgctl()


메시지 큐 제어 시스템 호출
메시지 큐의 상태를 질의하거나 상태 정보를 변경, 또는 메
시지 큐를 제거하는 기능을 지원
Linux System Programming
22
메시지 큐(Message Queue) (10)

메시지 큐의 제어: msgctl()

msgctl() 시스템 호출의 명령어
Linux System Programming
23
메시지 큐(Message Queue) (11)

msgctl() 시스템 호출의 사용 예

메시지 큐 관련 자료 구조 선언
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msqid;
struct msqid_ds ds;

메시지 큐의 제거
msgctl(msqid, IPC_RMID, (struct
msqid_ds *)0);

메시지 큐의 소유자 변경
msgctl(msqid, IPC_STAT, &ds);
ds.msg_perm.uid = 51;
msgctl(msqid, IPC_SET, &ds);
Linux System Programming
24
메시지 큐(Message Queue) (12)

msgctl() 시스템 호출의 사용 예

메시지 큐의 접근 허가 변경
msgctl(msqid, IPC_STAT, &ds);
ds.msg_perm.mode = 0660;
msgctl(msqid, IPC_SET, &ds);

메시지 큐의 메시지 크기 변경
msgctl(msqid, IPC_STAT, &ds);
ds.msg_qbytes = 5000;
msgctl(msqid, IPC_SET, &ds);
Linux System Programming
25
메시지 큐(Message Queue) (13)

메시지 큐의 연산: msgsnd(), msgrcv()

메시지 구조체 :
#include <sys/msg.h>
struct msgbuf {
long mtype;
유형 */
char mtext[1];
}
/* 메시지
/* 메지시 텍스트 */
메시지 유형은 전송되는 여러가지 유형의 메시지 형태를 분류
하여 지정
 메시지 텍스트는 전송하는 데이터를 저장
 메시지 텍스트의 크기는 최소 0에서 최대 64 KB 이내이며, 묵
시적으로 2048 바이트로 사용

Linux System Programming
26
메시지 큐(Message Queue) (14)

메시지 송신 시스템 호출 : msgsnd()
Linux System Programming
27
메시지 큐(Message Queue) (15)

메시지 송신 시스템 호출 : msgsnd()

msgsnd 시스템 호출에 의한 메시지 큐 구조체 변경
Linux System Programming
28
메시지 큐(Message Queue) (16)

메시지 수신 시스템 호출 : msgrcv()
Linux System Programming
29
메시지 큐(Message Queue) (17)

메시지 수신 시스템 호출 : msgrcv()

msgrcv() 시스템 호출에서의 메시지 타입 지정
Linux System Programming
30
예제 프로그램 #1 (1)

하나의 메시지를 전송하고 수신하는 프로그램
// file name : ex9-1a.c
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct {
long t;
char a[60];
} x = {11L,"hello"};
main()
{
int
mid;
mid = msgget(11L,IPC_CREAT | 0666);
msgsnd(mid,&x,strlen(x.a)+1,0);
sleep(60);
msgctl(mid,IPC_RMID,0);
}
Linux System Programming
31
예제 프로그램 #1 (2)
// file name : ex9-1b.c
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct {
long t;
char a[60];
} x ;
main()
{
int
mid,rtn;
mid = msgget(11L,0);
rtn=msgrcv(mid,&x,60,0L,0);
printf("rtn=%d type=%d text=%s\n",rtn,x.t,x.a);
}
Linux System Programming
32
예제 프로그램 #2 (1)

예제 프로그램 #1을 개선하여 여러 개의 메시지를 송
수신하는 프로그램
// file name : ex9-2a.c
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_SEND_SZ 30
#define DONE 99L
main()
{
void perror();
key_t key;
int
mid;
static struct {
long
mtype;
char
mtext[MAX_SEND_SZ];
} buf;
Linux System Programming
33
예제 프로그램 #2 (2)
static char *string[3] = {
"hello",
"how are you",
"good-bye"};
int i,rtn;
if((key= ftok(".",'a'))==-1) {
perror("Can\'t form key");
exit(1);
}
mid = msgget(key,IPC_CREAT| 0660);
if(mid == -1) {
perror("Sender can not make msg queue!");
exit(2);
}
Linux System Programming
34
예제 프로그램 #2 (3)
buf.mtype = 1L;
for(i=0;i<3;i++)
{
strcpy(buf.mtext,string[i]);
if(msgsnd(mid,&buf,strlen(buf.mtext)+1,0)==-1)
{ perror("Sender can not msgsnd!");
exit(3);
}
}
rtn = msgrcv(mid,&buf,MAX_SEND_SZ,DONE,0);
if(rtn == -1) {
perror("Sender can not msgrcv");
exit(4);
}
msgctl(mid,IPC_RMID,0);
}
Linux System Programming
35
예제 프로그램 #2 (4)
// file name : ex9-2b.c
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_RECV_SZ 60
#define FIFO 0L
#define ZERO_LEN 0
#define DONE 99L
static struct {
long
mtype;
char
mtext[MAX_RECV_SZ];
} buf;
key_t key;
int
mid;
int
rtn;
Linux System Programming
36
예제 프로그램 #2 (5)
int main(int argv,char *argc[])
{
void perror();
if((key= ftok(".",'a'))==-1) {
perror("Can\'t form key");
}
exit(1);
mid = msgget(key,0);
if(mid == -1) {
perror("Receiver can not access msg queue!");
exit(2);
}
while(1) {
rtn = msgrcv(mid,&buf,MAX_RECV_SZ,FIFO,0);
printf("rtn=%d buf.mtype=%ld buf.mtext=%s\n",
rtn,buf.mtype,buf.mtext);
if(!strcmp(buf.mtext,"good-bye")) break;
}
buf.mtype = DONE;
msgsnd(mid,&buf,ZERO_LEN,0); exit(0);
37
예제 프로그램 #3 (1)

메시지 송신 프로그램가 키보드 입력을 메시지로 만들어 여러
개의 수신 프로그램에 전달하는 프로그램

송신 프로그램은 fork()/exec() 시스템 호출을 통하여 수신 프로세스
를 생성하고, 수신 프로세스는 수신된 메시지를 표준 출력으로 출
력하도록 한다.
// file name : ex9-3a.c
//
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_SZ 80
#define NCHILD 3
Linux System Programming
38
예제 프로그램 #3 (2)
#define
WAIT_SOLE_USE(MSGQID) { \
int still_active = NCHILD; \
int i; \
long s_pid; \
s_pid = getpid(); \
while(still_active) { for(i=0;i<NCHILD;i++) \
if(msgrcv(MSGQID,&buf,MAX_SZ,s_pid,IPC_NOWAIT) != -1) \
if(!strncmp(buf.mtext,"DONE",4))
still_active--; \
sleep(2); \
} \
}
int main(void)
{
void perror();
key_t ftok();
int msgqid;
struct {
long mtype;
Linux System Programming
char mtext[MAX_SZ];
39
예제 프로그램 #3 (3)
int msg_length;
static char qid_evar[40];
static char *envp[2] = { qid_evar };
int this_fork, child;
long r_pid[NCHILD];
char child_name[20];
int eof = 0;
if((msgqid = msgget(IPC_PRIVATE,IPC_CREAT | 0660))
== -1) {
perror("Sender can't make message queue!");
exit(1);
}
sprintf(qid_evar,"MQID=%d",msgqid);
for(child=0;child<NCHILD;child++)
{
this_fork = fork();
if(this_fork == -1) {
perror("fork failed"); exit(2);
}
else if(this_fork == 0) {
sprintf(child_name,"mwall_r%d",child);
execle("ex9-3b",child_name,0,envp);
Linux System Programming
40
perror("exec
failed"); exit(3);
}
예제 프로그램 #3 (4)
else
r_pid[child] = (long) this_fork;
}
setbuf(stdout,(char *) 0);
while(1) {
fprintf(stdout,
"Enter message to be sent to all receivers:");
if(gets(buf.mtext) == (char *) 0) {
fprintf(stdout,"\n");
eof++; strcpy(buf.mtext,"EOF");
}
msg_length = strlen(buf.mtext) +1;
for(child=0;child<NCHILD;child++) {
buf.mtype = r_pid[child];
if(msgsnd(msgqid,&buf,msg_length,0) == -1) {
perror("Producer msgsnd error");
exit(4);
}
}
if(eof) break;
sleep(1);
Linux System Programming
41
}
예제 프로그램 #3 (5)
WAIT_SOLE_USE(msgqid);
msgctl(msgqid,IPC_RMID,0);
exit(0);
}
Linux System Programming
42
예제 프로그램 #3 (6)
// file name : ex9-3b.c
//
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_SZ 80
int main(int argc, char *argv[])
{
void perror();
char *getenv();
long pid;
char *valuep;
int msgqid;
struct {
long mtype;
char mtext[MAX_SZ];
} buf;
Linux System Programming
43
예제 프로그램 #3 (7)
pid = (long) getpid();
if((valuep = getenv("MQID")) == NULL) {
fprintf(stderr,"%s:can't get env
MQID\n",argv[0]);
exit(1);
}
else sscanf(valuep,"%d",&msgqid);
while(1)
{
msgrcv(msgqid,&buf,MAX_SZ,pid,MSG_NOERROR);
printf("%s received
'%s'\n",argv[0],buf.mtext);
if(!strcmp(buf.mtext,"EOF")) break;
}
buf.mtype = (long) getppid();
strcpy(buf.mtext,"DONE");
if(msgsnd(msgqid,&buf,strlen(buf.mtext),0) == -1) {
fprintf(stderr,"%s:msgsnd error\n",argv[0]);
exit(2);
}
Linux System Programming
44
exit(0);
Semaphore
세마포어(Semaphore) (1)

세마포어(Semaphore)


실행단위(프로세스 또는 쓰레드) 간의 동기화 도구
2개의 원자적 연산 P와 V가 정의되어 있는,정수값을 가지는
객체
s : 세마포어
 P(s) : if ( s > 0 ) then
s-else 현재 프로세스는 대기한다;
 V(s) : if ( 1개 이상의 프로세스가 대기중 ) then
1개 프로세스만 진행한다
else s++;


일상 생활에서의 ‘신호등’과 같은 동작을 수행

철도 교통을 통제하기 위한 깃발신호로부터 유래
Linux System Programming
46
세마포어(Semaphore) (2)

세마포어(Semaphore)

(계속)
세마포어 연산 P & V 는 원자적 연산(atomic operation)
하나의 프로세스가 P 또는 V 연산을 실행하는 도중에는 중지하
지 않는다
 어떤 프로세스가 P 또는 V 연산을 이용하여 세마포어에 접근하
는 동안에는 다른 프로세스가 세마포어 값을 변경할 수 없다


세마포어의 활용
상호 배제(mutual exclusion) 문제 – 두 개 이상의 프로세스가
하나의 공유 자원을 접근할 때에 한 순간에 하나의 프로세스만
공유 자원을 접근할 수 있도록 보장함
 실행 동기화 – 두 개 이상의 프로세스간에 실행 순서에 맞추어
실행됨을 보장함

Linux System Programming
47
세마포어(Semaphore) (3)

세마포어 종류 :

이진 세마포어(Binary Semaphore)
0 또는 1의 정수 값만 가지는 세마포어
 P(s) 연산은 세마포어 s가 1일 때에 0으로 변경
 V(s) 연산은 세마포어 s가 0일 때에 1로 변경
 하나의 자원에 대한 공유 및 동기화를 지원


계수형 세마포어(Counting Semaphore)
범위에 제한이 없는 정수 값을 가지는 세마포어
 일반적으로 언급하는 세마포어
 다수의 공유 자원에 대해 여러 프로세스가 접근할 때에 상호 배
제 및 동기화를 지원

Linux System Programming
48
세마포어(Semaphore) (4)

세마포어 이용 - 공유 자원에 대한 상호 배제(Mutual Exclusion)

여러 개의 프로세스가 하나의 자원을 공유하는 경우, 동시에 여러
프로세스가 자원을 접근하면 예상하지 못하는 문제가 발생함


예: 동시에 여러 프로그램이 프린터에 출력을 시도하는 경우
해결책 – 상호 배제
한 순간에 하나의 프로세스만 공유 자원을 접근함을 보장함
 임계 영역(Critical Section) : 전체 프로그램 중에서 공유자원을 접근하
는 프로그램 영역
 한 순간에 임계 영역을 실행하는 프로세스는 하나만 존재하도록 보장함


세마포어를 이용하여 상호 배제를 구현
공유자원의 갯수에 따라 이진 세마포어 또는 계수형 세마포어를 사용
 공유 자원에 대한 잠금과 풀기(lock & unlock) 기능을 지원

Linux System Programming
49
세마포어(Semaphore) (5)

세마포어 이용 - 공유 자원에 대한 상호 배제(Mutual Exclusion)
프로세스 P1
세마포어 s
프로세스 P2
(초기값: 1)
P(s)
Critical
Section
P(s)
Critical
Section
공유 자원
V(s)
V(s)
Linux System Programming
50
세마포어(Semaphore) (6)

세마포어 이용 - 프로세스간의 실행 동기화(Synchronization)

하나의 프로그램이 여러 개의 프로세스로 이루어지는 경우, 프로세
스간의 종속성에 의해 실행 순서가 정해지며, 반드시 실행 순서에
의해 동작하여함


예: 하나의 프로세스가 다른 프로세스가 제공하는 데이터를 받아 동작
하는 경우
해결책 – 프로세스 실행 동기화
프로세서 P1가 T1 문장을 실행한 후에 프로세스 P2가 T2 문장을 실행
하여야 하는 경우
 프로세스 P2는 T2 문자을 실행하기 전에 프로세스 P1이 T1 문장을 실
행하여는지를 검사




프로세스 P1이 T1 문장을 실행하였으면 바로 T2 문장을 실행
프로세스 P1이 T2 문장을 실행하지 않았으면 실행할 때까지 대기
세마포어를 이용하여 동기화를 지원함

대개의 경우 이진 세마포어를 사용
Linux System Programming
51
세마포어(Semaphore) (7)

세마포어 이용 - 프로세스간의 실행 동기화(Synchronization)
프로세스 P1
세마포어 s
프로세스 P2
(초기값: 0)
V(s)
P(s)
T1: -----
T2: -----
Linux System Programming
52
세마포어(Semaphore) (8)

세마포어의 생성: semget()
Linux System Programming
53
세마포어(Semaphore) (9)

세마포어의 생성: semget()
Linux System Programming
54
세마포어(Semaphore) (10)

세마포어의 생성: semget()

semget() 시스템 호출의 동작
세마포어 구조체 초기화:
Linux System Programming
55
세마포어(Semaphore) (11)

세마포어의 제어: semctl()

세마포어 제어 연산
1.
2.
3.
4.
5.

세마포어의 집합 안에 개별적인 세마포어나 모든 세마포어에
대해 세마포어 값을 읽어오거나 새로운 값을 설정
세마포어 집합의 상태 정보을 읽어오거나 변경
세마포어에서 대기중인 프로세스의 수를 결정
마지막으로 세마포어를 연산한 프로세스를 결정
세마포어를 제거
semctl ()시스템 호출을 통해 상기의 세마포어 제어 연산을
실행
Linux System Programming
56
세마포어(Semaphore) (12)

세마포어의 제어: semctl()
Linux System Programming
57
세마포어(Semaphore) (13)

세마포어의 제어: semctl()

semctl() 시스템 호출의 명령어 및 반환값
Linux System Programming
58
세마포어(Semaphore) (14)

세마포어의 제어: semctl()

세마포어 생성
#include
#include
#include
#define
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
ANY
0
int semid, rtn;
struct semid_ds semds;
ushort us[5], init_us[5] = {0, 6, 7, 1, 4};
…
semid = semget(key, 5, IPC_CREAT|0666);

세마포어 집합에서 하나의 세마포어를 초기화
semctl(semid, 2, SETVAL, 7);

세마포어 집합에서 하나의 세마포어 값 읽기
// GETCNT, GETZCNT, GETPID도 같은 형태로 사용
semctl(semid, 2, GETVAL,
ANY);
Linux System
Programming
59
세마포어(Semaphore) (15)

세마포어의 제어: semctl()

세마포어 집합 내의 모든 세마포어의 값을 초기화
semctl(semid, ANY, SETALL, init_us);

세마포어 집합 내의 모든 세마포어의 값 읽기
semctl(semid, ANY, GETALL, us);

세마포어의 소유자 변경
// 접근 허가 모드 변경에도 같은 형태로 사용
semctl(semid, ANY, IPC_STAT, &semds);
semds.sem_perm.uid = 51;
semctl(semid, ANY, IPC_SET, &ds);

세마포어 집합 제거
semctl(semid, ANY, IPC_RMID, ANY);
Linux System Programming
60
세마포어(Semaphore) (16)

세마포어의 연산: semop()

세마포어의 값을 증가시키거나 감소시키는 연산을 수행
Linux System Programming
61
세마포어(Semaphore) (17)
Linux System Programming
62
세마포어(Semaphore) (18)
Linux System Programming
63
예제 프로그램 #4 (1)

세마포어를 이용하여 공유 자원에 대한 접근을 제어
하는 프로그램

공유 자원은 표준 출력
// file name : ex10-1.c
//
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#define DUMMY 0
#define COUNT 4
int main(int argc, char *argv[])
{
key_t ipckey; /* modified */
int
semid, semget(),semctl(),semop();
int
pid,getpid();
int
creator,i;
static struct sembuf lock = {0,-1,SEM_UNDO};
static struct sembuf unlock = {0, 1,SEM_UNDO};
setbuf(stdout,(char *) NULL);
64
예제 프로그램 #4 (2)
ipckey = ftok(argv[0],'s');
if((semid = semget(ipckey,1,IPC_CREAT | IPC_EXCL | 0666)) != -1)
{
creator = 1;
}
else {
if((semid = semget(ipckey,1,0)) == -1) {
perror(argv[0]); exit(1);
}
creator = 0;
}
if(creator) {
if(semctl(semid,0,SETVAL,1) == -1) {
perror(argv[0]); exit(2);
}
}
pid = getpid();
for(i=0;i<COUNT;i++){
if(semop(semid,&lock,1) == -1) {
perror(argv[0]); exit(3);
}
printf("\t[%d]locking\t",pid);
sleep(1);
Linux System Programming
printf("[%d] unlocking\n",pid);
65
예제 프로그램 #4 (3)
if (semop(semid,&unlock,1) == -1) {
perror(argv[0]); exit(4);
}
}
if (creator) {
sleep(5);
if(semctl(semid,DUMMY,IPC_RMID,DUMMY) == -1) {
perror(argv[0]);
exit(5);
}
}
}
Linux System Programming
66
예제 프로그램 #5 (1)




다음 그림과 세 개의 세마포어
를 갖는 세마포어 집합을 이용
하여 프린터 자원을 관리하는
프로그램
첫번째 세마포어는 두 개의 프
린터 모두를 관리하는 계수형
세마포어
두번째 및 세번째 세마포어는
각각 프린터1과 프린터2를 관
리하는 세마포어
프린터는 단말기 장치를
PRINTER1으로, 정규 파일을
PRINTER2로 환경 변수를 정의
하여 시뮬레이션한다
67
예제 프로그램 #5 (2)
#include
#include
#include
#include
#include
#include
#define
#define
#define
#define
<fcntl.h>
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
<stdio.h>
"ex10-2.h"
DUMMY 0
NUMPR 2
ACQUIRE
RELEASE
-1
1
int main(int argc,char *argv[])
{
char *getenv(), *printer[NUMPR], buf[BUFSIZ];
key_t ipckey; /* modified */
int semid, semget(), semctl(), semop();
ushort initial[NUMPR +1];
int i, prntnum, creator, getpid();
int n, fdin, fdout;
struct sembuf operation[2];
char errmsg[30];
if((printer[1] = getenv("PRINTER1")) == (char *) NULL ||
(printer[2] = getenv("PRINTER2")) == (char *) NULL) {
printf("missing printer assignment\n");
exit(1);
}
68
예제 프로그램 #5 (3)
if(strncmp(argv[0],"line",4) ==0) prntnum = 1;
else if(strncmp(argv[0],"lase",4) ==0) prntnum =2;
else prntnum = getpid() % NUMPR +1;
ipckey = ftok(argv[0], 's');
if((semid = semget(ipckey,NUMPR +1,IPC_CREAT| IPC_EXCL|
0666)) != -1) {
creator = 1;
} else { if((semid = semget(ipckey, NUMPR +1, 0666)) == -1)
{
sprintf(errmsg, "%s - semget", argv[0]);
perror(errmsg);
exit(2);
}
}
if(creator) { /* initialize semaphore set */
initial[0] = NUMPR;
for(i=1; i<= NUMPR; i++) initial[i] = 1;
if(semctl(semid, DUMMY, SETALL, initial) == -1) {
sprintf(errmsg,"%s -SETALL", argv[0]);
perror(errmsg); exit(3);
}
}
operation[1].sem_num = prntnum;
operation[1].sem_op = ACQUIRE;
operation[1].sem_flg = SEM_UNDO;
operation[0].sem_num = 0;
operation[0].sem_op = ACQUIRE;
operation[0].sem_flg = SEM_UNDO;
69
예제 프로그램 #5 (4)
if(semop(semid, operation, 2) == -1) {
sprintf(errmsg,"%s - ACQUIRE", argv[0]);
perror(errmsg); exit(4);
}
if((fdin = open(argv[1], O_RDONLY)) == -1) {
sprintf(errmsg,"%s - %s", argv[0], argv[1]);
perror(errmsg); exit(5);
}
if((fdout = open(printer[prntnum], O_CREAT| O_WRONLY)) == -1)
{
sprintf(errmsg,"%s - %s", argv[0], printer[prntnum]);
perror(errmsg); exit(6);
}
while((n= read(fdin, buf, BUFSIZ)) > 0) write(fdout, buf, n);
operation[1].sem_op = RELEASE;
operation[0].sem_op = RELEASE;
if(semop(semid, operation, 2) == -1) {
sprintf(errmsg,"%s - RELEASE", argv[0]);
perror(errmsg); exit(7);
}
exit(0);
}
70
예제 프로그램 #6 (1)


다음 그림과 같은 생산
라인을 시뮬레이션하는
프로그램
각 공정은 다중 프로세스
를 생성하고 이들의 생산
순서와 소비 시간을 세마
포어와 프로세스 수면
(sleep)을 이용하여 시뮬
레이션한다
71
예제 프로그램 #6 (2)
//
// header file name : ex10-3.h
//
#define
#define
#define
#define
#define
#define
NWIDGETS
PARTA
PARTB
PARTC
SUB1
HOLDER
5
0
1
2
3
0
72
예제 프로그램 #6 (3)
//
// source file name : ex10-3a.c
//
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include "ex10-3.h"
int semprodn;
void endsim();
int main(int argc, char *argv[])
{
int widget;
int pgrp;
char asc_prod_key[20];
static struct sembuf partc_sub1[2] = {
{ PARTC, -1, SEM_UNDO}, { SUB1, -1, SEM_UNDO}
};
if((semprodn=semget(IPC_PRIVATE,4,IPC_CREAT | 0640)) == -1)
{
printf("Can't get production line semaphore set\n");
exit(1);
73
}
예제 프로그램 #6 (4)
signal(SIGINT, endsim);
sprintf(asc_prod_key,"%d", semprodn);
if( fork() == 0)
execl("ex10-3b","a", asc_prod_key, (char
if( fork() == 0)
execl("ex10-3b","b", asc_prod_key, (char
if( fork() == 0)
execl("ex10-3b","c", asc_prod_key, (char
if( fork() == 0)
(char *)0);
execl("ex10-3c","ex10-3c", asc_prod_key,
*)0);
*)0);
*)0);
for(widget=1; widget < NWIDGETS; widget++) {
semop(semprodn, partc_sub1, 2);
printf("%s: ready to make widget #%d\n",
argv[0], widget);
}
endsim();
}
void endsim()
{
semctl(semprodn, 0,IPC_RMID, 0);
signal(SIGTERM, SIG_IGN);
kill(0, SIGTERM);
74
예제 프로그램 #6 (5)
//
// source file name : ex10-3b.c
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include "ex10-3.h"
int main(int argc, char *argv[])
{
int semprodn, index;
int unit = 0;
static struct sembuf parti = { HOLDER, 1, SEM_UNDO };
static int prodtimeabc[3] = {2,3,4};
semprodn = atoi( argv[1] );
index = argv[0][0]- 'a'; /* argv[0] == [abc] */
parti.sem_num = index;
while(1) {
semop(semprodn, &parti, 1);
printf("%s: producing unit #%d\n", argv[0], ++unit);
sleep(prodtimeabc[index]);
}
}
75
예제 프로그램 #6 (6)
//
// source file name : ex10-3c.c
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include "ex10-3.h"
int main(int argc, char *argv[])
{
int semprodn;
int unit = 0;
static struct sembuf part_ab[2] = {
{PARTA, -1, SEM_UNDO }, {PARTB, -1, SEM_UNDO } };
static struct sembuf sub1 = { SUB1, 1, SEM_UNDO };
semprodn = atoi( argv[1] );
while(1) {
semop(semprodn, part_ab, 2);
semop(semprodn, &sub1, 1);
printf("%s: producing sub-assembly #%d\n", argv[0],
++unit);
}
}
76
Shared Memory
공유메모리(Shared Memory) (1)

공유메모리(Shared memory)

두 개이상의 프로세스가 실제 메모리의 일부를 공유하여 통신
할 수 있는 IPC 도구
통신하려는 프로세스는 우선 공유 메모리를 할당받는다
 프로세스는 할당된 공유 메모리를 자신의 가상주소공간 일부에
붙여(attach) 메모리에 데이터를 읽고 쓰는 것가 동일한 방법으로
필요한 데이터를 읽거나 전송하려는 데이터를 기록하여 상호 통
신한다
 프로세스간의 통신이 종료되면 공유 메모리를 가상주소공간에서
떼어내고(detach) 공유 메모리를 해제한다



IPC 도구 중에서 가장 효율적
시스템 간의 이식성이 낮음
프로세스간의 메모리 공유를 위해 별도의 하드웨어 지원이 필요
 공유 메모리를 사용한 프로그램을 하드웨어 지원이 없는 시스템
에서는 사용불가

Linux System Programming
78
공유메모리(Shared Memory) (2)

공유 메모리 시스템 호출 :




shmget – 공유 메모리를 새롭게 할당하거나 할당된 공유 메
모리의 핸들을 반환한다
shmctl – 공유 메모리와 관련된 상태 변수값을 변경하거나
공유 메모리를 제거한다
shmat – 공유 메모리를 프로세스의 가상주소공간에 논리적
으로 부착(attach)한다
shmdt – 프로세스의 가상주소공간으로부터 공유 메모리를
분리한다
Linux System Programming
79
공유메모리(Shared Memory) (3)

공유 메모리 시스템 호출 :
shmget()
프로세스 P1
프로세스 P2
공유메모리
shmat()
가상주소:
10000
shmat()
shmdt()
shmdt()
가상주소:
20000
shmctl()
Linux System Programming
80
공유메모리(Shared Memory) (4)

공유 메모리의 생성: shmget()
Linux System Programming
81
공유메모리(Shared Memory) (5)

공유 메모리의 생성: shmget()

공유 메모리 구조체
Linux System Programming
82
공유메모리(Shared Memory) (6)

공유 메모리의 생성: shmget()

공유 메모리 구조체의 초기화
Linux System Programming
83
공유메모리(Shared Memory) (7)

공유 메모리의 제어: shmctl()
Linux System Programming
84
공유메모리(Shared Memory) (8)

공유 메모리의 제어: shmctl()

shmctl() 시스템 호출의 명령어
Linux System Programming
85
공유메모리(Shared Memory) (9)

공유 메모리의 연산: shmat() / shmdt()
Linux System Programming
86
예제 프로그램 #7 (1)




학원 강좌 예약 시스템 프로그램
부모 프로세스는 강좌 예약 정보를 갖는 공유 메모리을 할당하
고, 공유 메모리에 대한 상호배제를 위한 세마포어를 생성한다
자식 프로세스을 생성하여 강좌 예약을 수행하는데, 자식 프로세
스는 주기적으로 강좌 예약을 수행하도록 시뮬레이션하며, 더 이
상 예약할 좌석(seat)가 없으면 종료한다
부모 프로세스는 모든 자식 프로세스가 종료할 때까지 기다렸다
가 세마포어와 공유 메모리를 제거하고 종료한다
//
// file name: ex11-1.h
//
struct CLASS {
char
class_number[6];
char
date[6];
char
title[50];
int
seats_left;
}
typedef struct CLASS class_t;
87
예제 프로그램 #7 (2)
// file name: ex11-1a.c
//
#include <stdio.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include "ex11-1.h"
class_t class = {
"1001", "120186",
"C Language for Programmers",
15 };
#define NCHILD 3
int child[NCHILD];
char
int
char
char
void
*shm_ptr, *shmat();
semid, shmid;
ascsemid[10], ascshmid[10];
pname[14];
rpterror();
int main(int argc, char *argv[])
{
Linux System Programming
int i;
88
예제 프로그램 #7 (3)
strcpy(pname, argv[0]);
shm_init(); sem_init();
for(i=0; i < NCHILD; i++) {
child[i] = fork();
switch(child[i]) {
case -1: rpterror("fork-failure");
exit(1);
case 0: sprintf(pname,"ex11-1b%d",i+1);
execl("ex11-1b", pname, ascshmid, ascsemid, (char *)
0);
perror("execl faild"); exit(2);
}
}
wait_and_wrap_up();
}
shm_init()
{
shmid=shmget(IPC_PRIVATE, sizeof(class), 0600|IPC_CREAT);
if(shmid == -1) {
perror("shmget failed"); exit(3);
}
shm_ptr = shmat(shmid, (char *) 0, 0);
if(shm_ptr == (char *) -1) {
perror("shmat failed"); exit(4);
}
memcpy(shm_ptr,(char *) &class, sizeof(class));
89
sprintf(ascshmid,"%d", shmid);
}
예제 프로그램 #7 (4)
sem_init() {
if((semid=semget(IPC_PRIVATE, 1, 0600| IPC_CREAT)) == -1) {
perror("semget failed"); exit(5);
}
if((semctl(semid, 0, SETVAL,1)) == -1) {
printf("parent: semctl, SETVAL failed\n"); exit(6);
}
sprintf(ascsemid,"%d",semid);
}
wait_and_wrap_up() {
int wait_rtn, w, ch_active = NCHILD;
while(ch_active > 0) {
wait_rtn = wait((int *) 0);
for(w=0; w < NCHILD; w++)
if(child[w] == wait_rtn) {
ch_active--;
break;
}
}
printf("Parent removing shm and sem\n");
shmdt(shm_ptr);
shmctl(shmid, IPC_RMID);
semctl(semid, 0, IPC_RMID, 0);
exit(0);
}
90
예제 프로그램 #7 (5)
void rpterror(char *string)
{
char errline[50];
sprintf(errline,"%s %s",string, pname);
perror(errline);
}
91
예제 프로그램 #7 (6)
// file name: ex11-1b.c
//
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include "ex11-1.h"
class_t
char
int
struct
struct
void
*class_ptr;
*memptr, *shmat(), *pname;
semid, shmid, ret;
sembuf lock = { 0, -1, 0};
sembuf unlock = { 0, 1, 0};
rpterror();
int main(int argc, char *argv[])
{
if(argc < 3) {
fprintf(stderr, "Usage: %s shmid semid\n", argv[0]);
exit(1);
}
pname = argv[0];
sscanf(argv[1],"%d", &shmid);
92
memptr = shmat(shmid, (char *) 0, 0);
예제 프로그램 #7 (7)
if(memptr == (char *) -1) {
rpterror("shmat failed");
exit(2);
}
class_ptr = (class_t *) memptr;
sscanf(argv[2], "%d", &semid);
sell_seats();
ret = shmdt(memptr);
exit(0);
}
sell_seats() {
int all_out = 0;
srand((unsigned) getpid());
while(! all_out) { /* loop to sell all seats */
if(semop(semid, &lock, 1) == -1) {
rpterror("semop lock failed");
exit(4);
}
if(class_ptr -> seats_left > 0) {
class_ptr -> seats_left--;
printf("%s SOLD SEAT -- %2d left\n", pname,
93
class_ptr->seats_left);
}
예제 프로그램 #7 (8)
else {
all_out++;
printf("%s sees no seats left\n", pname);
}
ret = semop(semid, &unlock, 1);
if(ret ==-1) {
rpterror("semop unlock failed");
exit(4);
}
sleep((unsigned) rand() %10 +1);
}
}
void rpterror(string)
char *string;
{
char errline[50];
sprintf(errline,"%s %s",string, pname);
perror(errline);
}
94
예제 프로그램 #8 (1)



세마포어와 공유 메모리를 이용하여 버퍼의 자료를 생산하고 소
비하는 생산자/소비자(producer/consumer) 프로그램
생산자는 공유 메모리를 할당하여 버퍼를 설정한다
생산자와 소비자 사이에 버퍼 접근을 위한 동기화를 위해 2 개
의 세마포어를 생성하여 사용한다
버퍼 쓰기 동기화(consumed 동기화) - 생산자는 소비자가 버퍼의
자료를 읽고 간 후에 새로운 자료를 버퍼에 쓴다
 버퍼 읽기 동기화(produced 동기화) - 소비자는 생산자가 버퍼에
새로운 자료를 쓴 후에 자료를 읽어간다

95
예제 프로그램 #8 (2)
// file name: ex11-2a.c
//
/* producer program:
producer program sets up a buffer to be read by a consumer.
semaphore are used to ensure taht producer dosen't
overwrite an unread buffer and consumer dosen't read
the same data more than once.
*/
#include
#include
#include
#include
#include
<stdio.h>
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
<sys/shm.h>
#define MSG_SIZE 30
#define MAX 5
main()
{
char *getenv();
key_t ftok();
char *shmat();
key_t key;
void perror(), exit();
96
예제 프로그램 #8 (3)
/* two semaphores in a set
index 0 - incremented when producer
- tested and decremented by
if buffer has been reset
index 1 - incremented when consumer
- tested and decremented by
if consumer is done.
*/
has reset buffer
consumer to check
has read buffer
producer to check
static struct sembuf wait_consumed = { 1, -1, 0};
static struct sembuf signal_produced = { 0, 1, 0};
int semid, shmid;
char *message;
int i;
if((key = ftok(getenv("HOME"), 'u')) == (key_t) -1) {
fprintf(stderr,"ftok key formation error\n");
exit(1);
}
semid = semget(key, 2, IPC_CREAT | 0660);
if(semid == -1) {
perror("producer semget():");
exit(2);
}
97
예제 프로그램 #8 (4)
shmid = shmget(key, MSG_SIZE, IPC_CREAT | 0660);
if(semid == -1) {
perror("producer shmget():"); exit(3);
}
message = shmat(shmid, (char *) 0, 0);
for(i=1; i < MAX; i++) {
/* producer has to go first */
if(i>1) semop(semid, &wait_consumed, 1);
sprintf(message, "message %d", i);
semop(semid, &signal_produced, 1);
}
shmdt(message);
sleep(5);
/*
/* allow consumer to digest last message */
alternatively a DELIMITER string could be placed in
shared memory when seen by the consumer, it would exit.
the producer would do shmctl (..., IPC_STAT, ....)
and when shm_attach == 1, it would remove the two IPC
facilities
*/
shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0);
shmctl(shmid, 0, IPC_RMID);
98
예제 프로그램 #8 (5)
// file name: ex11-2b.c
//
/* consumer program:
producer program sets up a buffer to be read by a consumer.
semaphore are used to ensure taht producer dosen't
overwrite an unread buffer and consumer dosen't read
the same data more than once.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#define MSG_SIZE 30
int main(int argc, char *argv[])
{
/* two semaphores in a set
index 0 - incremented when producer
- tested and decremented by
if buffer has been reset
index 1 - incremented when consumer
- tested and decremented by
if consumer is done.
*/
has reset buffer
consumer to check
has read buffer
producer to check
99
예제 프로그램 #8 (6)
key_t key;
static struct sembuf wait_produced = { 0, -1, 0};
static struct sembuf signal_consumed = { 1, 1, 0};
int semid, shmid;
char *message;
int rtn;
if((key = ftok(getenv("HOME"), 'u')) == (key_t) -1) {
fprintf(stderr,"ftok key formation error\n");
exit(1);
}
/* either producer or consumer might be the creator
but producer will be the remover of IPC resources.
producer and consumer's effective uid must be the same.
*/
semid = semget(key, 2, IPC_CREAT | 0660);
if(semid == -1) {
perror("producer semget():");
exit(2);
}
100
예제 프로그램 #8 (7)
shmid = shmget(key, MSG_SIZE, IPC_CREAT | 0660);
if(semid == -1) {
perror("producer shmget():");
exit(3);
}
message = shmat(shmid, (char *) 0, SHM_RDONLY);
while(1) {
rtn = semop(semid, &wait_produced, 1);
/* when producer is done semid will be IPC_RMID
forcing break
*/
if(rtn == -1) {
perror("consumer - semop on wait_consumed");
break;
}
printf("%s recevied: %s\n", argv[0], message);
semop(semid, &signal_consumed, 1);
}
shmdt(message);
}
101