당연한 이치겠지만 shm_open에서 첫번째 인자로 받는 path는 실제 파일이 아니라 OS에서 shared memory object를 구분하기 위한 이름일 뿐이다. Linux 2.6 커널에서 /dev/shm를 마운트 하는데, 이것은 shm_open으로 연 객체를 사용자가 파일형태(실제 파일은 아니고 파일 흉내)로 shared memory object에 접근하도록 만들어놓은 interface일 뿐이다. IBM개발문서를 보면 /dev/shm이 tmpfs라는 file system이라는 것과 reboot하면 깔끔하게 내용이 날아가버린다는 사실도 확인할 수 있다.
아래는 간략한 shm_open을 이용한 테스트 프로그램이다. 이것을 위해서는 df -h 해서 /dev/shm 용량이 2기가 정도는 있어줘야 문제 없이 일을 수행할 수 있을 것이다. 이는 fstab을 고치거나 'mount -oremount,size=2g /dev/shm' 하면 대충 넘어갈 것이다. (정확한 사용량은 1,749,470,000 bytes로 약 1.6 giga bytes에 이른다. 쒧!)
리눅스에서 컴파일할 때 옵션이 몇개 더 필요하므로 아래와 같은 Makefile을 예쁘게 만들어주자.자, 이제 실행이다! 두근두근!
오~ 끝내주는데? 하악하악 초기화는 좀 뻘짓을 해서 원래 저렇게 오래걸린 거고, 읽고 쓰는 시간이 1.6기가에 무려 0.1초! 아름다운 성능일쎄.
주의! 저 shmobj 파일을 수단과 방법을 가리지 않고 지우지 않을 경우 고스란히 메모리에 남아 있을 것이다. shm_unlink든 unlink든 뭐든 후딱 지우자.
아래는 간략한 shm_open을 이용한 테스트 프로그램이다. 이것을 위해서는 df -h 해서 /dev/shm 용량이 2기가 정도는 있어줘야 문제 없이 일을 수행할 수 있을 것이다. 이는 fstab을 고치거나 'mount -oremount,size=2g /dev/shm' 하면 대충 넘어갈 것이다. (정확한 사용량은 1,749,470,000 bytes로 약 1.6 giga bytes에 이른다. 쒧!)
// 헤더 겁내 많네.
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
// 아름다운 STL
#include <map>
#include <iostream>
#include <string>
using namespace std;
const size_t g_total_count(170000); //!< 객체 개수
const string g_shm_id("/shmobj"); //!< shared memory object id
typedef int64_t ts_t; //!< Timestamp type
//! @breif 사용자데이터타입
typedef struct _user_t
{
char id[50+1];
char data[1024*10];
} user_t;
//! @brief 사용자데이터타입이 g_total_count만큼 있을 때
//! 필요한 용량.
const size_t g_total_size(g_total_count*sizeof(user_t));
user_t* g_users; //!< 맵핑할 주소를 저장할 포인터
//! @beief 현재 시간을 1/1,000,000초 단위로 알려준다.
ts_t
getTimestamp(void)
{
static struct timeval tv;
gettimeofday(&tv,NULL);
return (tv.tv_sec*1000000LL)+tv.tv_usec;
}
//! @brief 오류 메시지를 뿜고 프로세스를 죽여준다.
void
exitError(void)
{
cerr << strerror(errno) << endl;
exit( errno );
}
//! @brief 시그널 처리
void
sigFunc(int sig)
{
switch(sig)
{
case SIGBUS:
{
cerr << "SIGBUS" << endl;
exit(0);
}
}
cerr << "signal: " << sig << endl;
}
int
main(int,char**)
{
// mmap에 연결된 파일을 넘어선 곳을 접근하면 SIGBUS 발생
// 대충 에러 뿌려주고 잘 죽으라고 signal 처리.
signal(SIGBUS, sigFunc);
int fd, res;
ts_t pos;
// /hbd 라는 이름을 가지는 shared memory object를 만들라는 것.
// 이미 있으면 그냥 그거 열어서 쓴다.
fd = shm_open(g_shm_id.c_str(), O_RDWR|O_CREAT, 0666);
if ( -1 == fd )
{
exitError();
}
// 나중에 죽었을 때 코어 파일 떨구지 말라고 삽질해놓은 것.
// 실제로 쓰면 매우 위험하니 이런 짓 하지 말자.
struct rlimit rl;
memset(&rl, 0x00, sizeof(rl));
res = setrlimit(RLIMIT_CORE, &rl);
if ( -1 == res )
{
exitError();
}
cerr << "user_t size: " << sizeof(user_t) << endl;
cerr << "total count: " << g_total_count << endl;
cerr << "total size: "
<< (double)g_total_size/1024.0/1024.0
<< " mega bytes" << endl;
cerr << "initializing..." << endl;
// 자... 시간을 재자.
pos = getTimestamp();
// 초기화 시~작!
for (size_t i=0; i<g_total_count; i++)
{
user_t tmp;
res = write(fd, &tmp, sizeof(tmp));
if ( -1 == res )
{
cerr << "init count: " << i << endl;
exitError();
}
}
// 헉! 이만큼이나 걸렸어?
cerr << "initialization time: "
<< getTimestamp() - pos << "us" << endl;
// 받은 공유메모리를 실제 프로세스 메모리맵에 넣는다.
g_users = (user_t*)mmap(NULL, g_total_size,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 실패면 죽어라.
if ( MAP_FAILED == g_users )
{
exitError();
}
cerr << "read & write..." << endl;
pos = getTimestamp();
for ( size_t i = 0; i < g_total_count; i++ )
{
memset(g_users, 0x00, sizeof(user_t));
}
cerr << "read & write time: "
<< getTimestamp() - pos << "us" << endl;
cerr << "done." << endl;
return 0;
}
LDFLAGS=-lrt
CXXFLAGS=-g -O -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
all:shmobj
$ ./shmobj
user_t size: 10291
total count: 170000
total size: 1668.42 mega bytes
initializing...
initialization time: 4422271us
read & write...
read & write time: 138441us
done.
$ ls -al /dev/shm
total 1710144
drwxrwxrwt 2 root root 60 Mar 28 14:56 .
drwxr-xr-x 7 root root 5260 Nov 1 18:11 ..
-rw-rw-r-- 1 purewell purewell 1749470000 Mar 28 14:56 shmobj
오~ 끝내주는데? 하악하악 초기화는 좀 뻘짓을 해서 원래 저렇게 오래걸린 거고, 읽고 쓰는 시간이 1.6기가에 무려 0.1초! 아름다운 성능일쎄.
주의! 저 shmobj 파일을 수단과 방법을 가리지 않고 지우지 않을 경우 고스란히 메모리에 남아 있을 것이다. shm_unlink든 unlink든 뭐든 후딱 지우자.
댓글
댓글 쓰기