가끔씩 공유메모리에 있는 자료를 n개 프로세스/쓰레드가 읽고 쓰기 위해 접근할 때, 정말 충돌이 나긴할까? 물론 이론은 반드시 나긴 나는데, 이론은 이론이고... 임계영역이 I/O wait이 안 일어날 정도로 짧은 구간이더라도 충돌이 날까?
결론은 충돌난다. ㅡ_-) 덴장... 이래서 잠금장치가 여러개 있는 것이지...
아래 소스를 대충 컴파일해서 돌려보고 Ctrl+C를 눌러보면...
참고로 아래는 CentOS 5.2에서 살포시 돌려본 결과이다.
덧글: 혹시나 오해할까봐... MT/MP 환경에서 임계영역이 있다면 락을 걸어주는 것이 상식. 이건 그냥 가끔씩 이론은 이론이고 실제는 얼마나 충돌할까라는 맛뵈기 실험.
결론은 충돌난다. ㅡ_-) 덴장... 이래서 잠금장치가 여러개 있는 것이지...
아래 소스를 대충 컴파일해서 돌려보고 Ctrl+C를 눌러보면...
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
enum
{
SHM_STR_LEN = 32
};
typedef struct str_t
{
int len;
char buf[SHM_STR_LEN+1];
} str_t;
volatile size_t g_total_count(0);
volatile size_t g_crash_count(0);
int g_shmfd(-1);
str_t* g_buf(NULL);
// make random string...
void
makeRandStr(str_t& buf)
{
memset(&buf, 0x00, sizeof(buf));
int i, max(rand()%SHM_STR_LEN);
for ( i = 0; i < max; i++ )
{
buf.buf[i] = (rand()%10) + '0';
}
buf.len = max;
buf.buf[SHM_STR_LEN] = 0x00;
}
// check str_t structure...
bool
chkBuffer(const str_t& buf)
{
++ g_total_count;
int reallen(strlen(buf.buf));
bool res(reallen == buf.len);
if ( res )
{
//printf("success!\n");
return true;
}
//printf("crash! len=%d real=%d\nbuf=%s\n",
// buf.len, reallen, buf.buf);
++ g_crash_count;
return false;
}
// signal procedure for productor.
void
productor_sigint(int)
{
shmctl(g_shmfd, IPC_RMID, NULL);
exit(0);
}
// productor makes random-string and memcpy to shared-memory.
void
productor(void)
{
signal(SIGINT, productor_sigint);
str_t buf;
while (true)
{
makeRandStr(buf);
memcpy(g_buf, &buf, sizeof(buf));
//printf("rand: len=%d\n", g_buf->len, g_buf->buf);
}
}
// signal procedure for consumer.
void
consumer_sigint(int)
{
printf("\ntotal: %d\ncrash: %d\nfail-rate: %2.02f%%\n",
g_total_count, g_crash_count,
g_crash_count*100.0/g_total_count);
shmctl(g_shmfd, IPC_RMID, NULL);
exit(0);
}
// consumer reads memcpy from shared-memory and check.
void
consumer(void)
{
signal(SIGINT, consumer_sigint);
fprintf(stderr, "Test size: %d\n", sizeof(str_t));
fprintf(stderr, "Press Ctrl+C...");
str_t buf;
while (true)
{
memcpy(&buf, g_buf, sizeof(buf));
chkBuffer(buf);
}
}
// main function. make shared-memory and fork!
int
main(int argc, char* argv[])
{
key_t shmkey(ftok("/dev/null", 1));
if ( -1 == shmkey )
{
printf("%d %s\n", __LINE__, strerror(errno));
return 0;
}
g_shmfd = (shmget(shmkey, sizeof(str_t), 0700|IPC_CREAT));
if ( -1 == g_shmfd )
{
printf("%d %s\n", __LINE__, strerror(errno));
return 0;
}
g_buf = ((str_t*)shmat(g_shmfd, NULL, SHM_RND));
memset(g_buf, 0x00, sizeof(str_t));
if ( fork() )
{
productor();
}
else
{
consumer();
}
}
$ g++ -o shmtest -O3 shmtest.cpp아주 정확한 방법은 아니지만, 대충 충돌하는 것을 알 수 있을 것이다. 여러 시스템에서 돌렸을 때, 몇 퍼센트가 나올지 궁금하다. 누가 돌려주면 좋겠지만, 아쉽게도 아무도 안 돌릴 것 같다. orz OTL
참고로 아래는 CentOS 5.2에서 살포시 돌려본 결과이다.
$ ./shmtest Test size: 40 Press Ctrl+C... total: 186202017 crash: 985010 fail-rate: 0.53%
덧글: 혹시나 오해할까봐... MT/MP 환경에서 임계영역이 있다면 락을 걸어주는 것이 상식. 이건 그냥 가끔씩 이론은 이론이고 실제는 얼마나 충돌할까라는 맛뵈기 실험.
댓글
댓글 쓰기