앞서 'shared memory with mutex' 에서는 2개 이상의 프로세스에서 공유 메모리를 이용해 데이터를 공유할 때 Lock / Unlock을 위해 mutex를 사용하는 방법에 대해 살펴보았습니다.
이번 글에서는 공유메모리를 이용해 데이터를 공유할 때 여러 프로세스들이 semaphore을 이용해 Lock / Unlock 하는 방법에 대해 알아보겠습니다. 앞서 'shared memory with mutex'를 보신 분들이라면 쉽게 이해 하실 수 있는데요, 코드 중 mutex 관련된 코드 대신 semaphore 코드로 대체해 주는 것 밖에 없습니다. 저는 semaphore을 POSIX Spinlock API들을 사용하였습니다.
semaphore는 커널에서 관리해주는 정수로서 0이하의 음수 값으로는 변하지 않는 특징을 가지고 있습니다. 이 값을 이용해 Critical section에 대해 동기화를 진행해 주는 것이죠. 그럼 코드를 보시죠. 유의 깊게 보셔야 할 부분은 master 코드에서 line 69와 main 함수의 for문입니다.
[shm_sem_master.c]
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
|
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#define retm_if(expr, val, msg) do { \
if (expr) \
{ \
printf("%s\n", (msg)); \
return (val); \
} \
} while(0)
#define retv_if(expr, val) do { \
if (expr) \
{ \
return (val); \
} \
} while(0)
#define rete_if(expr, val, msg) do { \
if (expr) \
{ \
printf("%s, errno : %d, errstr : %s\n", msg, errno, strerror(errno)); \
return (val); \
} \
} while(0)
#define SHM_NAME "shm_mem_test"
typedef struct shm_struct {
sem_t sem;
int idx;
} shm_struct_t;
static shm_struct_t *shm;
static int __init_shared_memory(void)
{
int ret;
int shm_fd;
int shm_size;
shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (shm_fd == -1) {
printf("shm_open %d %s\n", errno, strerror(errno));
return -1;
}
shm_size = sizeof(shm_struct_t);
ret = ftruncate(shm_fd, shm_size);
rete_if(ret == -1, ret, "ftruncate");
shm = (shm_struct_t *)mmap(NULL, shm_size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
retm_if(shm == MAP_FAILED, -1, "mmap()");
close(shm_fd);
memset(shm, 0, shm_size);
ret = sem_init(&shm->sem, 1, 1);
rete_if(ret != 0, ret, "sem_init");
return 0;
}
static int __destory_shared_memory(void)
{
sem_destroy(&shm->sem);
munmap(shm, sizeof(shm_struct_t));
}
int main(int argc, char *argv[])
{
int ret;
int i;
ret = __init_shared_memory();
retm_if(ret != 0, ret, "__init_shared_memory");
while (shm->idx < 10);
for (i = 0; i < 1000000; i++)
{
sem_wait(&shm->sem);
shm->idx++;
sem_post(&shm->sem);
}
sleep(10);
printf("%d\n", shm->idx);
__destory_shared_memory();
return 0;
}
|
cs |
[shm_sem_slave.c]
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
|
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#define retm_if(expr, val, msg) do { \
if (expr) \
{ \
printf("%s\n", (msg)); \
return (val); \
} \
} while(0)
#define retv_if(expr, val) do { \
if (expr) \
{ \
return (val); \
} \
} while(0)
#define rete_if(expr, val, msg) do { \
if (expr) \
{ \
printf("%s, errno : %d, errstr : %s\n", msg, errno, strerror(errno)); \
return (val); \
} \
} while(0)
#define SHM_NAME "shm_mem_test"
typedef struct shm_struct {
sem_t sem;
int idx;
} shm_struct_t;
static shm_struct_t *shm;
static int __init_shared_memory(void)
{
int ret;
int shm_fd;
int shm_size;
shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (shm_fd == -1) {
printf("shm_open %d %s\n", errno, strerror(errno));
return -1;
}
shm_size = sizeof(shm_struct_t);
ret = ftruncate(shm_fd, shm_size);
rete_if(ret == -1, ret, "ftruncate");
shm = (shm_struct_t *)mmap(NULL, shm_size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
retm_if(shm == MAP_FAILED, -1, "mmap()");
close(shm_fd);
return 0;
}
static int __destory_shared_memory(void)
{
munmap(shm, sizeof(shm_struct_t));
}
int main(int argc, char *argv[])
{
int ret;
int i;
ret = __init_shared_memory();
retm_if(ret != 0, ret, "__init_shared_memory");
for (i = 0; i < 1000000; i++)
{
sem_wait(&shm->sem);
shm->idx++;
sem_post(&shm->sem);
}
__destory_shared_memory();
return 0;
}
|
cs |
위 코드를 보시면 master에서 sem_init을 해줍니다. semaphore를 사용하기 위한 초기화 해주는 과정인데요, 이 때 프로세스 간 shared memory에 할당된 spinlock 변수를 공유하기 위한 설정을 해줘야 합니다. 함수 원형을 보시면 아래와 같습니다.
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
여기서 유심히 보셔야 할 부분이 pshared 라는 parameter입니다. manual page를 한번 보시죠.
The pshared argument indicates whether this semaphore is to be shared between the threads of a process, or between processes.
If pshared has the value 0, then the semaphore is shared between the threads of a process, and should be located at some address that is visible to all threads (e.g., a global variable, or a variable allocated dynamically on the heap).
If pshared is nonzero, then the semaphore is shared between processes, and should be located in a region of shared memory (see shm_open(3), mmap(2), and shmget(2)). (Since a child created by fork(2) inherits its parent's memory mappings, it can also access the semaphore.) Any process that can access the shared memory region can operate on the semaphore using sem_post(3), sem_wait(3), and so on.
Manual page를 보시면 pshared parameter에 대해 굉장히 잘 설명되어 있습니다. 만약 여러분들이 0을 전달하게 되면 thread들 간의 공유가 되며, 0이 아닌 다른 값으로 설정하면 프로세스들 간의 공유가 가능하다고 설명하고 있습니다.
sem_init은 한번만 해주시면 되기에 전 master 코드에서 진행해 주었구요, 초기값은 1로 설저하였습니다. 그리고 각 lock /unlock 해야 하는 로직 부분에 sem_wait() 과 sem_post()를 이용하였습니다. 해당 함수가 궁금하시다면 찾아보시면 금방 이해 되실 겁니다.
감사합니다.
'Linux > System Programming' 카테고리의 다른 글
[TOCTOU] Time of check to time of use 문제 (0) | 2018.07.25 |
---|---|
[Inotify] 파일 이벤트 감시 (2) | 2018.06.11 |
[ODP] 실행시간 측정하기 (0) | 2018.05.17 |
ptrace - process tracer (writing....) (0) | 2018.03.07 |
Elf Header, Program Header, Section Header 얻어오기 (0) | 2018.01.02 |