[제13장] 세마포어, 메시지 큐, 공유 메모리

돼지털라이프 | 2007/06/25 19:07

=================================================================
  * 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_

Trackback Address :: http://badnom.com/trackback/295
Name
Password
Homepage
Secret
< PREV |  1  |  ...  737  |  738  |  739  |  740  |  741  |  742  |  743  |  744  |  745  |  ...  983  |  NEXT >