가끔씩 공유메모리에 있는 자료를 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 환경에서 임계영역이 있다면 락을 걸어주는 것이 상식. 이건 그냥 가끔씩 이론은 이론이고 실제는 얼마나 충돌할까라는 맛뵈기 실험.
댓글
댓글 쓰기