[제13장] 세마포어, 메시지 큐, 공유 메모리
=================================================================
* Subject : [제13장] 세마포어, 메시지 큐, 공유 메모리
* Writer: w0rm9 (research.hackerschool.org)
* Date: 2004/02/18
=================================================================
/* 로그와 책을 중심으로 정리했으며, 소스는 책에 있는 소스를 사용했습니다.
* 능력이 안되서 소스를 분석하는 위주로... 테스트 했습니다.
*/
0x01. 세마포어
■ semget
사용법)
#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
=> 새로운 세마포어를 생성하거나 기존 세마포어의 세마포어 키를 구한다.
key는 세마포어를 액세스하기 위한 정수값, num_sems는 세마포어의 수, sem_flags는 IPC_CREAT와 같은 플래그 설정
■ semop
사용법)
#include <sys/sem.h>
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
=> 세마포어의 값을 변경하기 위해 사용
□ sem_ops의 멤버
struct sembuf {
short sem_num; // 세마포어 번호
short sem_op; // 세마포어가 변경되어야 하는 기준값
short sem_flg; // 보통 SEM_UNDO 설정함
}
■ semctl
사용법)
#include <sys/sem.h>
int semctl(int sem_id, int sem_num, int command, ...);
=> 세마포어 정보를 직접 제어하게 해준다.
sem_id는 세마포어 식별자, sem_num은 세마포어 번호,
□ command의 두 가지 흔 한 값
SETVAL 세마포어를 알려진 값으로 초기화하는데 사용된다.
IPC_RMID 더 이상 필요하지 않을 때 세마포어 식별자를 삭제하는 데 사용된다.
□ 네번째 파라미터 공용체 senmun의 멤버
union semun {
int val; // SETVAL의 값
struct semid_ds *buf; // IPC_STAT, IPC_SET을 위한 버퍼
unsigned short *array; // GETALL, SETALL을 위한 배열
}
테스트)
[w0rm9@work IPC]$ cat semun.h
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short int *array; /* array for GETALL, SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
[w0rm9@work IPC]$ cat sem1.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include "semun.h"
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = 'O'; // argc이 1보다 크지 않으면 op_char의 값은 0
srand((unsigned int)getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); // 1. 세마포어를 생성한다.
if (argc > 1) {
if (!set_semvalue()) { // 2. argc가 1보다 크면 set_semvalue()를 호출
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char = 'X'; // argc이 1보다 크면 op_char의 값은 1
sleep(2);
}
for(i = 0; i < 10; i++) { // 4. 10번 루프
if (!semaphore_p()) exit(EXIT_FAILURE); // 5. semaphore_p() 호출
printf("%c", op_char);fflush(stdout); // 7. op_char 출력
pause_time = rand() % 3;
sleep(pause_time);
printf("%c", op_char);fflush(stdout); // 8. op_char 다시 출력
if (!semaphore_v()) exit(EXIT_FAILURE); // 9. semaphore_v() 호출
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n%d - finished\n", getpid());
if (argc > 1) {
sleep(10);
del_semvalue(); // 11. argc가 1보다 클때만 del_semvalue() 호출
}
exit(EXIT_SUCCESS);
}
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0); // 3. 세마포어를 알려진 값으로 초기화 함
return(1);
}
static void del_semvalue(void)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) // 12. 세마포어 식별자를 삭제함.
fprintf(stderr, "Failed to delete semaphore\n");
}
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) { // 6. 세마포어 값을 -1로 변경
fprintf(stderr, "semaphore_p failed\n");
return(0);
}
return(1);
}
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1; // 10. 세마포어 값을 0으로 변경
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, "semaphore_v failed\n");
return(0);
}
return(1);
}
[w0rm9@work IPC]$ ./sem1
OOXXOOXXOOXXOOXXXXOOXXOOXXOOXXOOXXOOOOXX
12705 - finished
12706 - finished
0x02. 공유 메모리
■ shmget
사용법)
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
=> 공유 메모리를 생성한다.
key는 공유 메모리 세그먼트를 효과적으로 명명하는 정수값, size는 필요한 메모리의 양을 바이트 단위로 지정, shmflag는 IPC_CREAT와 같은 플래그 설정
■ shmat
사용법)
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
=> 공유 메모리에 대한 액세스를 허용하기 위해 프로세스의 어드레스 공간에 연결한다.
shm_id는 공유 메모리 식별자, shm_addr은 공유 메모리가 현재 프로세스에 연결되는 어드레스
□ shmflg
SHM_RND 공유 메모리가 연결되는 어드레스를 제어한다.
SHM_RDONLY 연결된 메모리를 읽기 전용으로 만듬
■ shmdt
사용법)
#include <sys/shm.h>
int shmdt(const void *shm_addr);
=> 현재 프로세스로부터 공유 메모리를 제거한다.
■ shmctl
사용법)
#include <sys/shm.h>
int shmctl(int shm_id, int command, struct shmid_ds *buf);
=> 공유 메모리를 제어하기 위한 함수
□ command의 값들
IPC_STAT 공유 메모리와 관련된 값을 반영하기 위해 shmid_ds 구조체에서 데이터를 설정한다.
IPC_SET 프로세스가 허용 권한을 가진다면 공유 메모리와 관련된 값을 shmid_ds 데이터 구조체에서 제공되는 것으로 설정한다.
IPC_RMID 공유 메모리 세그먼트를 삭제한다.
□ shmid_ds 구조체 멤버
struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
테스트)
[w0rm9@work IPC]$ cat shm_com.h
#define TEXT_SZ 2048
struct shared_use_st {
int written_by_you;
char some_text[TEXT_SZ];
};
[w0rm9@work IPC]$ cat shm1.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm_com.h"
int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid;
srand((unsigned int)getpid());
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT); // 1. 공유 메모리 생성
if (shmid == -1) {
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0); // 2. 공유 메모리를 액세스 할 수 있게 함
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shared_memory); // 3. 공유 메모리의 주소값 출력
shared_stuff = (struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0; // 4. 0 으로 설정하여
while(running) { // 5. 무한루프
if (shared_stuff->written_by_you) { // 6. 이 값이 0 일때만 출력함
printf("You wrote: %s", shared_stuff->some_text);
sleep( rand() % 4 );
shared_stuff->written_by_you = 0; // 7. 이 값을 0으로 설정
if (strncmp(shared_stuff->some_text, "end", 3) == 0) { // 8. end를 입력하면 종료
running = 0;
}
}
}
if (shmdt(shared_memory) == -1) { // 9. 공유 메모리 제거
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
if (shmctl(shmid, IPC_RMID, 0) == -1) { // 10. 공유 메모리 세그먼트 삭제
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
[w0rm9@work IPC]$ cat shm2.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm_com.h"
int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT); // 1. 공유 메모리 생성
if (shmid == -1) {
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0); // 2. 공유 메모리를 액세스 가능할 수 있게 함
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shared_memory); // 3. 공유 메모리의 주소값 출력
shared_stuff = (struct shared_use_st *)shared_memory;
while(running) {
while(shared_stuff->written_by_you == 1) { // 4. 1 이면 잠시 기다림
sleep(1);
printf("waiting for client...\n");
}
printf("Enter some text: "); // 5. 0 일때 입력을 받음
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SZ); // 6. 그 입력받은 값을 복사함
shared_stuff->written_by_you = 1; // 7. 입력을 받지 않고, 출력을 할 수 있도록 1로 설정
if (strncmp(buffer, "end", 3) == 0) { // 8. end를 입력하면 종료
running = 0;
}
}
if (shmdt(shared_memory) == -1) { // 공유 메모리 제거
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
[w0rm9@work IPC]$ gcc -o shm1 shm1.c;gcc -o shm2 shm2.c
[w0rm9@work IPC]$ ./shm1 &
[1] 12344
Memory attached at 40017000
[w0rm9@work IPC]$ ./shm2
Memory attached at 40017000
Enter some text: 사랑해
You wrote: 사랑해
waiting for client...
waiting for client...
Enter some text: 바보들~
You wrote: 바보들~
waiting for client...
waiting for client...
Enter some text: end
[w0rm9@work IPC]$ You wrote: end
0x03. 메시지 큐
■ msgget
사용법)
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
=> 메시지 큐를 생성하고 액세스한다.
■ msgsnd
사용법)
#include <sys/msg.h>
int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
=> 메시지 큐에 메시지를 추가하게 해준다.
msgid는 메시지 큐 식별자, msg_ptr은 전달되는 메시지에 대한 포인터, msg_sz는 msg_ptr이 가리키는 메시지의 크기, msgflg는 IPC_NOWAIT와 같은 플래그 설정
■ msgrcv
사용법)
#include <sys/msg.h>
int msgrcv(int msgid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
=> 메시지 큐로부터 메시지를 구한다.
msgid는 메시지 큐 식별자, msg_ptr은 전달되는 메시지에 대한 포인터, msg_sz는 msg_ptr이 가리키는 메시지의 크기, msgtype는 전달되는 순서대로 메시지를 구하기
원한다면 0, msgflg는 IPC_NOWAIT와 같은 플래그 설정
■ msgctl
사용법)
#include <sys/msg.h>
int msgctl(int msgid, int command, struct msgid_ds *buf);
=> 메시지 큐를 제어한다.
□ command의 값
IPC_STAT 메시지 큐와 관련된 갓을 반영하기 위해 msgid_ds 구조체에서 데이터를 설정한다.
IPC_SET 프로세스가 허용 권한을 가진다면 메시지 큐와 관련되는 값을 msgid_ds 데이터 구조체에서 제공되는 것으로 설정한다.
IPC_RMID 메시지 큐를 삭제한다.
□ msgid_ds 구조체의 멤버
struct msgid_ds {
uid_t msg_perm.uid;
uid_t msg_perm.gid;
mode_t msg_perm.mode;
}
테스트)
[w0rm9@work IPC]$ cat msg1.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st {
long int my_msg_type;
char some_text[BUFSIZ];
};
int main()
{
int running = 1;
int msgid;
struct my_msg_st some_data;
long int msg_to_receive = 0;
msgid = msgget((key_t)1234, 0666 | IPC_CREAT); // 1. 메시지 큐 생성
if (msgid == -1) {
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while(running) {
if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1) { // 2. 메시지 큐로 부터 메시지를 구한다.
fprintf(stderr, "msgrcv failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s", some_data.some_text); // 3. 구한 메시지를 출력한다.
if (strncmp(some_data.some_text, "end", 3) == 0) { // 4. end가 입력되면 루프를 종료한다.
running = 0;
}
}
if (msgctl(msgid, IPC_RMID, 0) == -1) { // 5. 메시지 큐를 삭제한다.
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
[w0rm9@work IPC]$ cat msg2.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main()
{
int running = 1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];
msgid = msgget((key_t)1234, 0666 | IPC_CREAT); // 1. 메시지 큐를 생성
if (msgid == -1) {
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
while(running) {
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin); // 2. 입력을 받음
some_data.my_msg_type = 1;
strcpy(some_data.some_text, buffer);
if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) { // 3. 메시지 큐에 메시지를 추가한다.
fprintf(stderr, "msgsnd failed\n");
exit(EXIT_FAILURE);
}
if (strncmp(buffer, "end", 3) == 0) { // 4. end가 입력되면 루프를 종료한다.
running = 0;
}
}
exit(EXIT_SUCCESS);
}
[w0rm9@work IPC]$ gcc -o msg1 msg1.c; gcc -o msg2 msg2.c
[w0rm9@work IPC]$ ./msg2
Enter some text: 사랑해
Enter some text: 히히
Enter some text: end
[w0rm9@work IPC]$ ./msg1
You wrote: 사랑해
You wrote: 히히
You wrote: end
_eof_
