서버간 메모리 동기화
- 김중근(인프라본부 시스템기술팀), 2007년 1월
서버간 특정영역 혹은 사용하고 있는 전체 메모리가 일치해야하는 경우가 있다. 여러가지 방법이 있으나 가장 간단한 방법을 예를 들어 표현해보려고 한다. 이번에 소개하는 방법은 NFS, MMAP, 그리고 Ram Disk를 사용했으며 성능치도 뽑아보았다. 사실 직접적인 메모리 동기화가 아닌 nfs로 연결된 ramdisk 파일을 mmap을 이용하여 메모리에 엑세스하는 것처럼 그대로 사용한 방식이다.
OS Setting #
- 먼저 Ram Disk를 잡고 그 안에 파일을 생성한 뒤 NFS Server를 실행시켜 해당 마운트 포인트를 export한다.
- NFS server 에서도 그 마운트 포인트를 바로 엑세스하면 안되고 mount 서버IP:/마운트포인트이런 형식으로 nfs mount를 해야한다. (mmap이 Update Time을 기록하지 않는다. Kernel 2.6)
- NFS Server 상태
Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda1 4134900 2988756 936096 77% / none 1037468 0 1037468 0% /dev/shm /dev/sda3 11432640 8116576 2735316 75% /data /dev/ram0 15863 4253 10791 29% /ram 127.0.0.1:/ram 15872 4256 10816 29% /rr
- NFS Client 상태
Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda1 4134900 2432048 1492804 62% / none 1027716 0 1027716 0% /dev/shm /dev/sda3 29071616 24073352 4702912 84% /data NFS Server IP:/ram 15872 4256 10816 29% /ram
보안상 NFS Server IP를 표현하지 않았다.
결과 #
- 동시에 Server및 Client에서 프로그램 실행.
- 4M의 영역을 2048번 엑세스한 값의 건당 평균 결과이다.
- Test1 에서 mmap을 이용하여 Access 를 하며 Test2 에서는 로컬 메인메모리를 엑세스한 결과이다.
- NFS Server
[root@서버1 /hanmail/user] ./test Test1 Memory Mapping Address: 0xB7B02000 Count : 2047 Sync. Read 0.002398 Sec. SYnc. Write 0.004640 Sec. Test2 Memory Address: 0xB7700008 Count : 2047 Priv. Read 0.002606 Sec. Priv. Write 0.004740 Sec.
- NFS Client
[root@서버2 /hanmail/user] ./test Test1 Memory Mapping Address: 0xB71EB000 Count : 2047 Sync. Read 0.002665 Sec. SYnc. Write 0.004579 Sec. Test2 Memory Address: 0xB6DE9008 Count : 2047 Priv. Read 0.002823 Sec. Priv. Write 0.004621 Sec.
Source #
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/rtc.h>
#include <errno.h>
#define DEVICE_NAME "/rr/ram.dat" //Client "/ram/ram.dat"
#define LOOP_CNT 2048
void calc(struct timeval *s, struct timeval *e, struct timeval *res)
{
struct timeval r;
if(s->tv_usec > e->tv_usec) {
r.tv_usec = (1000000 + e->tv_usec) - s->tv_usec;
r.tv_sec = e->tv_sec - s->tv_sec - 1;
} else {
r.tv_usec = e->tv_usec - s->tv_usec;
r.tv_sec = e->tv_sec - s->tv_sec;
}
if(res == NULL)
printf("Diff %u.%06u Sec.\n", r.tv_sec, r.tv_usec);
else
memcpy(res, &r, sizeof(struct timeval));
}
unsigned int mmap_fd;
size_t mmap_size;
char *sync_malloc(size_t s)
{
char *ret;
mmap_size = s;
if((mmap_fd = open(DEVICE_NAME, O_RDWR | O_SYNC)) == -1) {
fprintf(stderr, "Device Open Error (%s)\n", DEVICE_NAME);
return NULL;
}
if((ret = (char *)mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, 0)) == MAP_FAILED) {
close(mmap_fd);
return NULL;
}
return ret;
}
void sync_free(char *s)
{
munmap(s, mmap_size);
close(mmap_fd);
}
int main(int argc, char *argv)
{
int j, i;
unsigned char *vret;
struct timeval start, end, wres[LOOP_CNT], rres[LOOP_CNT], rtot, wtot;
char buf[1024];
srand(time(NULL));
vret = sync_malloc(4*1024*1024);
if(vret == NULL) {
fprintf(stderr, "Allocation Error\n");
}
printf("\nTest1\nMemory Mapping Address: 0x%08X\n\n", vret);
memset(&rtot, 0, sizeof(struct timeval));
memset(&wtot, 0, sizeof(struct timeval));
for(j = 0; j < LOOP_CNT; j++) {
gettimeofday(&start, NULL);
for(i = 0; i < 1024*4*1024;i+=1024) {
memcpy(buf, vret + i, 1024);
}
gettimeofday(&end, NULL);
calc(&start, &end, &rres[j]);
memset(buf, rand(), 1024);
gettimeofday(&start, NULL);
for(i = 0; i < 1024*4*1024;i+=1024) {
memcpy(vret + i, buf, 1024);
}
gettimeofday(&end, NULL);
calc(&start, &end, &wres[j]);
rtot.tv_sec += rres[j].tv_sec;
rtot.tv_usec += rres[j].tv_usec;
wtot.tv_sec += wres[j].tv_sec;
wtot.tv_usec += wres[j].tv_usec;
printf("Count : %d\r", j);
}
printf("\n\nSync. Read\t\t %u.%06u Sec.\n", rtot.tv_sec / LOOP_CNT, rtot.tv_usec / LOOP_CNT);
printf("SYnc. Write\t\t %u.%06u Sec.\n", wtot.tv_sec / LOOP_CNT, wtot.tv_usec / LOOP_CNT);
sync_free(vret);
vret = malloc(4*1024*1024);
if(vret == NULL) {
fprintf(stderr, "Allocation Error\n");
}
printf("\n\nTest2\nMemory Address: 0x%08X\n\n", vret);
memset(&rtot, 0, sizeof(struct timeval));
memset(&wtot, 0, sizeof(struct timeval));
for(j = 0; j < LOOP_CNT; j++) {
gettimeofday(&start, NULL);
for(i = 0; i < 1024*4*1024;i+=1024) {
memcpy(buf, vret + i, 1024);
}
gettimeofday(&end, NULL);
calc(&start, &end, &rres[j]);
memset(buf, rand(), 1024);
gettimeofday(&start, NULL);
for(i = 0; i < 1024*4*1024;i+=1024) {
memcpy(vret + i, buf, 1024);
}
gettimeofday(&end, NULL);
calc(&start, &end, &wres[j]);
rtot.tv_sec += rres[j].tv_sec;
rtot.tv_usec += rres[j].tv_usec;
wtot.tv_sec += wres[j].tv_sec;
wtot.tv_usec += wres[j].tv_usec;
printf("Count : %d\r", j);
}
printf("\n\nPriv. Read\t\t %u.%06u Sec.\n", rtot.tv_sec / LOOP_CNT, rtot.tv_usec / LOOP_CNT);
printf("Priv. Write\t\t %u.%06u Sec.\n", wtot.tv_sec / LOOP_CNT, wtot.tv_usec / LOOP_CNT);
free(vret);
}