C언어

리눅스(우분투 10.10) 공유 메모리 예제 2 공유메모리 제거

뮹실이 2013. 11. 18. 15:26








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
리눅스(우분투 10.10) 공유 메모리 예제 2

// 독립 프로세스가 공유메모리를 사용하는 예제


//==============================================================================
// 서버 코드
//==============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

// 공유 메모리 key 정의
// 다른 프로세스와 공유메모리를 사용하기 위해 같은 메모리 key를 사용해야함
#define SHARED_MEMORY_KEY 1005 
#define MEMORY_SIZE 200

// 플래그에 대한 상수
// 서버와 클라이언트의 동기화를 위해 정의
// 클라이언트가 서버에 데이터를 보냈을 때 
// 서버가 데이터가 왔다는것을 알수있는 방법이 필요한데
// 여러가지 방법이 있지만 
// 여기서는 메모리의 첫번째 바이트를 동기화 플래그로 사용하여 해결
// 즉 메모리 200byte 중 첫번째 1바이트는 동기화 플래그 byte,
// 나머지는 데이터 199byte
#define READ_CLIENT_FLAG 0
#define READ_SERVER_FLAG 1
#define PRINT_CLIENT_FLAG 2

int main()
{
    int shmid; // 공유 메모리 아이디를 저장할 변수
    char *buffer; // 공유 메모리 전체를 가리킬 포인터 변수
    char *string; // 문자열 부분을 가리킬 포인터 변수

    // make space that shared-memory
	// 공유 메모리 생성
	// 서버에서 공유 메모리를 생성하므로
	// shmget 함수에서 IPC_CREAT 옵션을 사용해
	// 공유메모리가 없는 경우 생성하도록 설정
    shmid = shmget((key_t)SHARED_MEMORY_KEY, (size_t)MEMORY_SIZE, 0777 | IPC_CREAT);
    if(shmid == -1)
    {
            perror("shmat failed : ");
            exit(0);
    }

    // attach shared memory
	// buffer 포인터 변수에 공유메모리를 가리키도록 하고
	// 문자열은 사용할 메모리 구조에서 1바이트 이후에 부분이므로
	// string = buffer + 1 의 지점을 가리키게 됨
    buffer = (char *)shmat(shmid, NULL, 0);
    if(buffer == (char *)-1){
        perror("shmat failed : ");
        exit(0);
    }
    string = buffer + 1;

	// buffer[0]의 위치는 동기화 플래그를 나타내므로
	// 클라이언트가 사용자의 정보를 읽는 READ_CLIENT_FLAG로 셋팅
    buffer[0] = READ_CLIENT_FLAG;

	// 클라이언트의 메시지(데이터)를 기다리는 부분
	// 동기화 플래그(buffer[0])가 READ_CLIENT_FLAG에서 
	// READ_SERVER_FLAG로 값이 변할 때까지 무한루프를 돌며 대기
	// 메시지가 오면 1.화면에 출력하고
	// 2.읽은 공유 메모리 메시지에 "by server"를 붙이고
	// 3.동기화 플래그를 PRINT_CLIENT_FLAG로 바꿔
	// 클라이언트가 메시지를 화면에 출력하도록 함 
	//
    while(1)
    {
        if(buffer[0] == READ_SERVER_FLAG)
        {
            puts(string);
            strcat(string, " by server");
            buffer[0] = PRINT_CLIENT_FLAG;
        }
        sleep(1);
    }

	//int shmdt(const void* shmaddr)
	shmdt(buffer); 
	// 공유 메모리와의 접속을 끊음
	// buffer : 공유메모리와의 연결을
	// 끊을 메모리의 포인터
	// 성공할 경우 0 반환
	// 실패할 경우 -1 반환

	//int shmctl(int shmid, int cmd, struct shmid_ds *buf)
	shmctl(shmid, IPC_RMID, NULL); // 공유메모리 제거
	// shmid : 공유메모리의 ID
	// IPC_RMID : 공유메모리 공간 삭제
	//            공유메모리에 접속되어 있는
	//            프로세스가 0일때까지 기다렸다가
	//            삭제
	// IPC_STAT : 공유메모리 공간 정보를 가져와
	//            buf 에 저장
	// IPC_SET : 공유메모리 공간에 대한 사용자 권한
	//           을 변경(사용자 or 슈퍼유저)
}



//==============================================================================
// 클라이언트 코드
//==============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

// 각 상수를 서버와 동일하게 설정해야함
#define SHARED_MEMORY_KEY 1005

#define READ_CLIENT_FLAG 0
#define READ_SERVER_FLAG 1
#define PRINT_CLIENT_FLAG 2

int main()
{
    int shmid;
    char *buffer;
    char *string;

    // make space that shared-memory
	// 서버와 다를 부분은 shmget 함수의 인자 부분
	// 이미 생성된 메모리에 접속하므로 shmget 세번째 인자에
	// 공유 메모리의 사이즈가 아닌 0을 입력
	// 네번째 인자 또한 같은 이유로 NULL 입력
	// NULL은 컴파일시 워닝 발생하므로 0으로 대체
    //shmid = shmget((key_t)SHARED_MEMORY_KEY, 0, NULL);
	shmid = shmget((key_t)SHARED_MEMORY_KEY, 0, 0);
    if(shmid == -1)
    {
            perror("shmat failed : ");
            exit(0);
    }

    // attach shared memory
    buffer = (char *)shmat(shmid, NULL, 0);
    if(buffer == (char *)-1){
        perror("shmat failed : ");
        exit(0);
    }

    buffer[0] = READ_CLIENT_FLAG;
    string = buffer  + 1;

	// 무한루프를 돌며
	// 동기화 플래그가 READ_CLIENT_FLAG 인 경우
	// 사용자의 입력을 받아 공유메모리 문자열 부분에 저장
	// 동기화 플래그가 PRINT_CLIENT_FLAG 인 경우
	// 서버가 변경한 공유메모리의 문자열을 화면에 출
    while(1)
    {
        if(buffer[0] == READ_CLIENT_FLAG)
        {
            printf("message : ");
            // gets 함수는 특정메모리에 정해지지 않은 사이즈를 넣기 때문에
			// 오버플로우 에러를 발생 시킬 소지가 있다
			// 문자열의 사이즈를 정할 수 있는 fgets 함수로 변경
			// fgets 함수 세번째 인자를 stdin으로 하면 키보드로 부터 입력을 받음
			//gets(string);
			fgets(string, 199, stdin);
			// 줄바꿈 문자(enter) 값을 없애기 위해 다음 줄 추가
			// fgets 함수는 gets 함수와 달리
			// 지정한 사이즈만큼 문자열을 읽기 때문에
			// 문자열에 리턴값이 포함되어 있다
			// 문자열 끝부분에 값을 '\0'으로 문장의 끝을 표시함
			string[strlen(string)-1] = '\0';

            buffer[0] = READ_SERVER_FLAG;
        }
        else if(buffer[0] == PRINT_CLIENT_FLAG)
        {
            puts(string);
            buffer[0] = READ_CLIENT_FLAG;
        }
        sleep(1);
    }
	//int shmdt(const void* shmaddr)
	shmdt(buffer); // 공유 메모리와의 접속을 끊음
	// buffer : 공유메모리와의 연결을
	// 끊을 메모리의 포인터
	// 성공할 경우 0 반환
	// 실패할 경우 -1 반환
}