#.키와 식별자
시스템 V IPC를 사용하려면 IPC의 객체를 생성해야 하는데, 이를 위해 공통적으로 사용하는 기본 요소가 키와 식별자이다.
#.키생성 방법
-키로 IPC_PRIVATE를 지정한다 IPC_PRIVATE를 키로 지정해 생성된 식별자를 서버와 클라이언트 모두 알 수 있게 해야 한다. fork 함수로 생성된 부모-자식 프로세스 간 통신에서 유용하게 사용할 수 있다.
-ftok 함수로 키를 생성한다. ftok 함수는 경로명과 숫자값을 받아서 키를 생성한다. 따라서 서버와 클라이언트가 같은 경로명과 숫자값을 지정하면 공통 식별자를 생성할 수 있다.
*상수 : IPC_PRIVATE
*키 생성 : key_t ftok(const char *path, int id);
*IPC 정보 검색 : ipcs [-aAbciJmopqstZ] [-D mtype]
*IPC 삭제 : ipcrm [-m shmid] [-q msqid] [-s semid] [-M shmkey] [-Q ,sgleu] [-S semkey]
키 생성하기 : ftok(3)
#include <sys/ipc.h>
key_t ftok(const char *path, int id);
ftok 함수는 path에 지정한 경로명과 id에 지정한 정수값을 조합해 새로운 키를 생성한다. 경로명은 파일시스템에 존재해야 한다.
#.IPC 공통 구조체
IPC에서 공통으로 사용하는 IPC 공통 구조체는 <sys/ipc.h> 파일에 정의되어 있다.
struct ipc_perm {
uid_t uid; // 구조체의 소유자 ID를 의미한다.
gid_t gid; // 구조체의 소유 그룹 ID를 의미한다.
uid_t cuid; // 구조체를 생성한 사용자 ID를 의미한다.
gid_t cgid; // 구조체를 생성한 그룹 ID를 의미한다.
mode_t mode; // 구조체에 대한 접근 권한을 의미한다.
uint_t seq; // 슬롯의 일련번호를 의미한다.
key_t key; // 키값을 의미한다.
int pad[4]; // 향후 사용을 위해 예약되어 있는 영역이다.
}
#.시스템 V IPC 정보 검색
시스템 V IPC의 정보를 검색하고 현재 상태를 확인하는 명령은 ipcs이다. ipcs 명령을 실행하는 동안에도 IPC의 상태가 변경될 수 있음.
#.시스템 V IPC 정보 삭제
불필요한 IPC 객체를 삭제하려면 ipcrm 명령을 사용
#.메시지큐
메시지 큐는 파이프와 유사한데 단, 파이프는 스트림 기반으로 동작하고, 메시지 큐는 메시지(또는 패킷) 단위로 동작한다.
*메시지 큐 생성 : int msgget(key_t key, int msgflg);
*메시지 전송 : int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
*메시지 수신 : ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int msgflg);
*메시지 제어 : int msgctl(int msqid, int cmd, struct msqid_ds *buf);
#.메시지큐 수행결과
msgsnd 함수의 수행이 성공하면 msqid_ds 구조체의 항목에서 msg_qnum 값이 1 증가하고, msg_lspid는 msgsnd 함수를 호출한 프로세스의 ID로 설정됩니다. msg_stime은 현재 시각으로 설정됩니다. msgsnd 함수는 수행을 실패하면 -1을 리턴하고 메시지는 전송하지 않습니다.
#.공유메모리
메모리의 일부 공간을 두 독립적인 프로세스에서 공유하고, 해당 메모리를 통해 데이터를 주고받을 수 있다.
*공유 메모리 생성 : int shmget(key_t key, size_t size, int shmflg);
*공유 메모리 연결 : void *shmat(int shmid, const void *shmaddr, int shmflg);
*공유 메모리 해제 : int shmdt(char *shmaddr);
*공유 메모리 제어 : int shmctl(int shmid, int cmd, struct shmid_ds *buf);
#.세마포어
세마포어는 프로세스 사이의 동기를 맞추는 기능을 제공해준다. 공유 메모리에 여러 프로세스가 동시에 쓰기를 시도하면 데이터가 손상되는 현상이 발생하므로, 여러 프로세스 사이에서 동작의 순서를 지정해줘야 한다.
*세마포어 생성 : int semget(key_t key, int nsems, int semflg);
*세마포어 제어 : int semctl(int semid, int semnum, int cmd, ...);
*세마포어 연산 : int semop(int semid, struct sembuf *sops, size_t nsops);
#.메시지큐 생성+전송 예제
결과

#.메시지큐 받기 예제
결과

#.메시지큐 제거 예제
결과

#.공유메모리 생성 예제
결과

#.부모 프로세스와 자식 프로세스 사이에서 공유 메모리를 사용해 데이터를 주고 받는 예제
결과

#.독립적인 프로세스 사이에서 공유 메모리를 사용해 데이터를 주고 받는 예제
-서버
#include <sys/types.h> | |
#include <sys/mman.h> | |
#include <signal.h> | |
#include <sys/ipc.h> | |
#include <sys/shm.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <string.h> | |
/* | |
서버 역할을 하는 server 에서는 공유 메모리를 생성하고, | |
client가 공유 메모리에 데이터를 기록하고 시그널을 보낼 때까지 기다립니다. | |
시그널을 받으면 공유 메모리에서 데이터를 읽어오고, 응답 데이터를 공유 메모리에 기록합니다. | |
*/ | |
void handler(int dummy){ | |
printf("handler called \n"); | |
} | |
int main(void){ | |
key_t key; | |
int shmid; | |
void *shmaddr; | |
char buf[1024]; | |
sigset_t mask; | |
//공유메모리 생성 | |
key = ftok("shmfile" , 1); | |
shmid = shmget(key, 1024, IPC_CREAT|0666); | |
//SIGUSR1 시그널을 받을때까지 기다리도록 한다. | |
sigfillset(&mask); //집합 변수에 모든 시그널 정보를 넣었음 | |
sigdelset(&mask, SIGUSR1); //시그널 제거 | |
sigset(SIGUSR1 , handler); //시그널 처리 함수를 변경 | |
printf("Listener wait for Talker \n"); | |
sigsuspend(&mask); //시그널을 BLOCK시킴과 동시에 대기합니다 | |
//시그널을 받으면 공유메모리를 연결 | |
//client 가 공유 메모리에 저장한 데이터를 읽어서 출력한다. | |
printf("Listner Start =====\n"); | |
//공유 메모리 연결 | |
shmaddr = shmat(shmid, NULL , 0); | |
//공유 메모리 내용을 buf에 복사 | |
strcpy(buf , shmaddr); | |
printf("Listener recieved : %s\n" , buf); | |
//공유 메모리에 응답데이터 저장 | |
strcpy(shmaddr , "have nice day \n"); | |
//공유 메모리의 연결을 끊기 전에 sleep 함수를 실행하는 이유는 | |
//client가 ipcs를 실행할 시간을 주기 위해서다. | |
sleep(3); | |
//공유메모리 해제 | |
shmdt(shmaddr); | |
return 0; | |
} |
-클라이언트
#include <sys/types.h> | |
#include <signal.h> | |
#include <sys/mman.h> | |
#include <sys/ipc.h> | |
#include <sys/shm.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
/* | |
클라이언트 역할을 하는 client에서는 server가 생성한 공유 메모리의 식별자를 읽어서 | |
공유 메모리를 연결하고 데이터를 기록한 후 listener에 시그널을 보냅니다. | |
잠시 기다렸다가 server가 기록한 데이터를 읽어서 출력하고 공유 메모리를 해제한 후 삭제합니다. | |
*/ | |
int main(int argc, char *argv[]){ | |
key_t key; | |
int shmid; | |
void *shmaddr; | |
char buf[1024]; | |
//client는 server와 같은 파일과 정수값을 사용해 키를 생성한다. | |
key = ftok("shmfile", 1); | |
//키값으로 server가 만든 공유 메모리의 식별자를 읽어온다. | |
shmid = shmget(key, 1024, 0); | |
//공유 메모리와 연결하고 해당 메모리에 인삿말을 복사한다. | |
shmaddr = shmat(shmid, NULL,0); | |
strcpy(shmaddr , "hello, i am talker\n"); | |
//명령행 인자로 받은 server의 pid를 지정하고 SIGUSR1 시그널을 보낸다. | |
kill(atoi(argv[1]) , SIGUSR1); | |
//sleep 함수를 수행해 잠시 기다렸다가 공유 메모리에서 listener가 보낸 응답을 읽어 출력한다. | |
sleep(2); | |
strcpy(buf, shmaddr); | |
printf("Listener said : %s\n" , buf); | |
//현재 공유 메모리의 상태 정보를 검색한 후, 공유 메모리 연결을 해제하고 | |
system("ipcs -m"); | |
shmdt(shmaddr); | |
//공유 메모리를 제거한다. | |
shmctl(shmid, IPC_RMID, NULL); | |
return 0; | |
} |
결과

#.세마포어 예제
#include <unistd.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include <sys/types.h> | |
#include <sys/sem.h> | |
#include <sys/ipc.h> | |
#include <stdio.h> | |
#define MAX_THREAD 2 | |
int count = 0; | |
void *myThreadFunc(void *data); | |
/* | |
sembuf 구조체는 <sys/sem.h> 파일에 정의되어 있습니다. | |
struct sembuf { | |
ushort_t sem_num; // 세마포어 번호를 의미 | |
short sem_op; // 세마포어 연산을 의미 | |
// 연산을 위한 플래그로, IPC_NOWAIT 또는 SEM_UNDO를 지정한다. | |
// SEM_UNDO: 프로세스가 비정상적으로 갑자기 종료할 때 | |
// 세마포어 동작을 취소한다. | |
short sem_flg; | |
}; | |
*/ | |
struct sembuf mysem_open = {0, -1, SEM_UNDO}; | |
struct sembuf mysem_close = {0, 1, SEM_UNDO}; | |
union snum | |
{ | |
int val; | |
}; | |
static int semid; | |
int main(int argc, char **argv) | |
{ | |
int thr_id; | |
int status; | |
int i; | |
union snum s_union; | |
pthread_t pt[MAX_THREAD]; //스레드 2개 | |
//세마포어 생성 | |
/* | |
세마포어는 커널이 관리한다. | |
semget을 이용해서 커널에 세마포어의 생성 혹은 기존에 만들어진 세마포어의 접근을 요청할 수 있다. | |
semget의 인자는 다음과 같다. | |
key : 세마포어에 접근하기 위한 key 값 | |
nsems : 세마포어 계수로 접근제하하려는 자원의 수 | |
semflg : 세마포어 동작제어를 위한 옵션 | |
*/ | |
semid = semget(2345, 1, 0600|IPC_CREAT); | |
if(semid == -1) | |
{ | |
perror("semget error"); | |
return 1; | |
} | |
s_union.val = 1; | |
//세마포어의 세부값 제어 | |
//SETVAL: 세마포어의 semval 값을 arg.val로 설정한다. | |
/* | |
네 번째 인자는 제어 명령에 따른 선택 사항입니다. | |
네 번째 항목이 필요할 경우 다음과 같은 공용체(union)을 사용합니다. | |
이 공용체는 프로그램에서 명시적으로 선언하고 사용해야 합니다. | |
union semun { | |
int val; | |
struct semid_ds *buf; | |
ushort_t *array; | |
} arg; | |
*/ | |
if(semctl(semid, 0, SETVAL, s_union) == -1) | |
{ | |
return 1; | |
} | |
for(i = 0; i < MAX_THREAD; i++) | |
{ | |
//스레드 생성 | |
thr_id = pthread_create(&pt[i], NULL, myThreadFunc, (void *)&i); | |
//성공할경우 쓰레드식별자인 thread에 쓰레드 식별번호를 저장하고, 0을 리턴한다. 실패했을경우 0 이 아닌 에러코드 값을 리턴한다. | |
if(thr_id < 0) | |
{ | |
perror("Thread Create Error"); | |
return 1; | |
} | |
sleep(1); | |
} | |
for(i = 0; i < MAX_THREAD; i++) | |
{ | |
//pthread_join 는 실별번호 th 로 시작된 쓰레드가 종료되는걸 기다린다. | |
pthread_join(pt[i], NULL); | |
/* | |
joinable 쓰레드가 종료되면, 종료된다고 하더라도 즉시 메모리 자원등이 해제 되지 않는다. | |
pthread_join 함수를 만나야지만 자원이 해제된다. | |
그럼으로 모든 joinable 쓰레드에 대해서는 반드시 pthread_join 을 호출해주어야 한다. | |
그렇지 않을경우 메모리 누수가 발생할것이다. | |
*/ | |
} | |
} | |
void *myThreadFunc(void *data) | |
{ | |
int thread_num = *(int *)data; | |
int lnum; | |
printf("Thread Create : %d\n", thread_num); | |
while(1) | |
{ | |
/* | |
*잠금과 해제와 같은 세마포어 연산을 수행하려면 semop 함수를 사용합니다. | |
*semop 함수는 semid가 가리키는 세마포어에 크기가 nsops인 sembuf 구조체로 지정한 연산을 실행합니다 | |
sedmid: semget 함수로 생성한 세마포어 식별자 | |
sops: sembuf 구조체의 주소 | |
nsops: sops가 가리키는 구조체의 크기 | |
*/ | |
semop(semid, &mysem_open, 1); | |
lnum = count; | |
sleep(1); | |
lnum = lnum+1; | |
count = lnum; | |
printf("[스레드%d번] count : %d\n", thread_num, count); | |
semop(semid, &mysem_close, 1); | |
} | |
} | |
결과

출처: 유닉스 프로그래밍 (한빛미디어) , joinC
'컴퓨터 기초 > 운영체제 실습' 카테고리의 다른 글
c언어 파일함수로 특정 문자 찾기 (0) | 2021.01.07 |
---|---|
c언어 파일입출력 함수를 이용해서 파일의 단어수 체크하기 (0) | 2021.01.06 |
18.파이프 (0) | 2020.07.20 |
17.메모리 매핑 (0) | 2020.07.20 |
16.스레드 (0) | 2020.07.16 |