메모리의 Bus lock을 lock이라는 어셈블리 명령어를 사용하여
C언어만으로는 만들수 없는 Lock을 구현한것입니다.
이 소스는 SMP에서도 잘 돌아갈것입니다.
이유는 Memory의 bus를 lock하는 것이기 때문에 가능한 것입니다.
x86계열 호환입니다. 코드는 VC++ 또는 gcc 로 컴파일 가능합니다.
*** 예외상황 정리 ***
1. lock상호간의 race condition(경쟁조건)
만약 A,B 두개의 lock 키를 다룬다고 하죠.
그리고 (a), (b) 두개의 thread가 움직입니다.
이때 (a)에서는 Lock(A)->Lock(B)->Unlock(B)->Unlock(A) 순으로 lock을 걸겠습니다.
그런데 (b)에서는 Lock(B)->Lock(A)->Unlock(A)->Unlock(B)로 lock을 건다고
상황을 만들어보세요.
과연 이 두개의 thread는 완전히 race condition을 극복할수 있다고 생각하십니까?
2. Dead lock (헤어나지 못하는 조건)
Lock(A)->Lock(A)->Unlock(A)->Unlock(A) 이런 상황은 당연히 벗어날수 없는
무한 lock상태에 돌입한다는 것은 누구나 쉽게 떠오를것입니다.
하지만 코딩하다보면 심심치 않게 버그로 남는 상황이 이러한 것입니다.
3. Kernel의 특권 ! 불가피한 dead lock 회피
CLI->STI만으로 CPU가 몇백개가 병별로 처리되더라도 벗어날수 있는 특권!
하지만 잘 안사용하겠죠?
성능저하가 상상초월.
하지만 CLI->do{ ...->SLEEP ON->... }while(isok)->STI는 좀 낮겠군요.
의도는 아시겠죠? 비선점형에서 선점형으로 진입하는...
C언어만으로는 만들수 없는 Lock을 구현한것입니다.
이 소스는 SMP에서도 잘 돌아갈것입니다.
이유는 Memory의 bus를 lock하는 것이기 때문에 가능한 것입니다.
x86계열 호환입니다. 코드는 VC++ 또는 gcc 로 컴파일 가능합니다.
*** 예외상황 정리 ***
1. lock상호간의 race condition(경쟁조건)
만약 A,B 두개의 lock 키를 다룬다고 하죠.
그리고 (a), (b) 두개의 thread가 움직입니다.
이때 (a)에서는 Lock(A)->Lock(B)->Unlock(B)->Unlock(A) 순으로 lock을 걸겠습니다.
그런데 (b)에서는 Lock(B)->Lock(A)->Unlock(A)->Unlock(B)로 lock을 건다고
상황을 만들어보세요.
과연 이 두개의 thread는 완전히 race condition을 극복할수 있다고 생각하십니까?
2. Dead lock (헤어나지 못하는 조건)
Lock(A)->Lock(A)->Unlock(A)->Unlock(A) 이런 상황은 당연히 벗어날수 없는
무한 lock상태에 돌입한다는 것은 누구나 쉽게 떠오를것입니다.
하지만 코딩하다보면 심심치 않게 버그로 남는 상황이 이러한 것입니다.
3. Kernel의 특권 ! 불가피한 dead lock 회피
CLI->STI만으로 CPU가 몇백개가 병별로 처리되더라도 벗어날수 있는 특권!
하지만 잘 안사용하겠죠?
성능저하가 상상초월.
하지만 CLI->do{ ...->SLEEP ON->... }while(isok)->STI는 좀 낮겠군요.
의도는 아시겠죠? 비선점형에서 선점형으로 진입하는...
| 코드: |
/* Code by JaeHyuk Cho <mailto:minzkn@infoeq.com> Made in KOREA http://minzkn.pe.ky */ #ifndef DEF_SOURCE_lock_c #define DEF_SOURCE_lock_c "lock.c" #include <stdio.h> #include <sched.h> #define __DEF_FCTYPE__ static int __DEF_FCTYPE__ __MZ_Lock__(int * volatile s_LockFlag, int s_Switch); static int __DEF_FCTYPE__ __MZ_AtomicExchange__(int * volatile s_To, int s_Value); int MZ_InitLock(int * volatile s_Key); void MZ_Lock(int * volatile s_Key); void MZ_UnLock(int * volatile s_Key); static int __DEF_FCTYPE__ __MZ_Lock__(int * volatile s_LockFlag, int s_Switch) { int s_Return; int s_LockTime = 0, s_TimeOut; s_TimeOut = (s_Switch >> 2) * 100; L_SpinLoop:; s_Return = __MZ_AtomicExchange__(s_LockFlag, s_Switch); if(s_Switch != 0 && s_Return != 0) { if(s_TimeOut != 0 && s_LockTime > s_TimeOut) { fprintf(stderr, "%s: %s - [ERROR] Dead-lock instead of off (%d second timeout) !!!\n", __FILE__, __FUNCTION__, s_TimeOut * 100); s_Return = __MZ_AtomicExchange__(s_LockFlag, 0); } else { sched_yield(); s_LockTime++; goto L_SpinLoop; } } return(s_Return); } static int __DEF_FCTYPE__ __MZ_AtomicExchange__(int * volatile s_To, int s_Value) { register int s_Return; #ifdef WIN32 MZ_ASM { PUSH EBX; MOV EAX, s_Value; MOV EBX, s_To; LOCK XCHG DWORD PTR [EBX], EAX; POP EBX; } #else MZ_ASM( "\n\t" "lock xchgl %1, (%2)\n\t" "\n\t" : "=a"(s_Return) : "a"(s_Value), "r"(s_To) ); #endif return(s_Return); } int MZ_InitLock(int * volatile s_Key) { if(s_Key)MZ_UnLock(s_Key); return(0); } void MZ_Lock(int * volatile s_Key) { (void)__MZ_Lock__(s_Key, 1); } void MZ_UnLock(int * volatile s_Key) { (void)__MZ_Lock__(s_Key, 0); } #endif /* End of source */ |
| 코드: |
| /* Copyright (C) Information Equipment co.,LTD All rights reserved. Code by JaeHyuk Cho <mailto:minzkn@infoeq.com> CVSTAG="$Header$" */ #include <sys/types.h> #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sched.h> #include <pthread.h> #define def_test_threads (8) #define def_use_lock (1) #if defined(__i386__) || defined(_M_IX86) # define __def_mz_i386__ #endif #if defined(__powerpc__) || defined(_M_PPC) # define __def_mz_ppc__ #endif #if defined(__mips__) || defined(_M_MRX000) # define __def_mz_mips__ #endif static char g_mz_architecture[ 1 << 10 ] = {"not defined"}; static int g_mz_ctrl_break = 0; int g_mz_lock = 0; int (mz_atomic_exchange)(int * volatile s_to, int s_value) { #if defined(__def_mz_i386__) (void)strcpy((char *)(&g_mz_architecture[0]), "i386"); __asm__ __volatile__("xchgl (%2), %0\n\t" : "=r"(s_value) : "0"(s_value), "r"(s_to)); return(s_value); #elif defined(__def_mz_ppc__) int s_temp; (void)strcpy((char *)(&g_mz_architecture[0]), "ppc"); __asm__ __volatile__( "0:\n\t" "lwarx %0,0,%1\n\t" "stwcx. %2,0,%1\n\t" "bne- 0b\n\t" : "=&r"(s_temp) : "r"(s_to),"r"(s_value) : "cr0", "memory" ); return(s_temp); #elif defined(__def_mz_arm__) /* not tested */ int s_temp; (void)strcpy((char *)(&g_mz_architecture[0]), "arm"); __asm__ __volatile__( "swp %0, %1, [%2]" : "=r"(s_value) : "0"(s_value), "r"(s_to) : "memory" ); return(s_temp); #elif defined(__def_mz_mips__) int s_result, s_temp; (void)strcpy((char *)(&g_mz_architecture[0]), "mips"); __asm__ __volatile__( "1:\n\t" ".set push\n\t" ".set mips2\n\t" "ll %0,%3\n\t" "move %1,%4\n\t" "beq %0,%4,2f\n\t" "sc %1,%2\n\t" ".set pop\n\t" "beqz %1,1b\n" "2:\n\t" : "=&r" (s_result), "=&r" (s_temp), "=m" (*s_to) : "m" (*s_to), "r" (s_value) : "memory"); return(s_result); #else /* unknown architecture */ static pthread_mutex_t sg_mz_mutex_bus = PTHREAD_MUTEX_INITIALIZER; int s_temp, s_check; (void)strcpy((char *)(&g_mz_architecture[0]), "unknown"); pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock, (void *)(&sg_mz_mutex_bus)); s_check = pthread_mutex_lock((pthread_mutex_t *)(&sg_mz_mutex_bus)); s_temp = *(s_to); *(s_to) = s_value; s_check = pthread_mutex_unlock((pthread_mutex_t *)(&sg_mz_mutex_bus)); pthread_cleanup_pop(0); return(s_temp); #endif } void (mz_load_balance)(void) { sched_yield(); } void (mz_unlock)(int *s_this) { (void)mz_atomic_exchange(s_this, 0); } void (mz_lock)(int *s_this) { l_lock_wait:; if(mz_atomic_exchange(s_this, 1) == 0)return; mz_load_balance(); goto l_lock_wait; } void (mz_signal)(int s_signal) { switch(s_signal) { case SIGINT: case SIGTERM: if(g_mz_ctrl_break == 0)(void)fputs("\nbreak.\x1b[0m\n", stdout); g_mz_ctrl_break = 1; (void)signal(s_signal, mz_signal); break; case SIGSEGV: case SIGFPE: g_mz_ctrl_break = 1; (void)fputs("\nOOPS: segment fault\x1b[0m\n", stderr); (void)raise(s_signal); exit(0); default: break; } } void * (mz_test_thread)(void *s_argument) { int s_pid = (int)getpid(); (void)fprintf(stdout, "run thread %d\n", s_pid); do { #if def_use_lock != (0) mz_lock((int *)(&g_mz_lock)); #endif *((int *)s_argument) = 0; mz_load_balance(); *((int *)s_argument) = s_pid; #if def_use_lock != (0) mz_unlock((int *)(&g_mz_lock)); #endif mz_load_balance(); }while(g_mz_ctrl_break == 0); return(s_argument); } int (main)(void) { pthread_t s_pthread[ def_test_threads ]; int s_index; volatile int s_test_value = (int)getpid(); (void)signal(SIGINT, mz_signal); (void)signal(SIGTERM, mz_signal); (void)signal(SIGSEGV, mz_signal); (void)signal(SIGFPE, mz_signal); (void)mz_atomic_exchange((int *)(&g_mz_lock), 0); (void)fprintf(stdout, "architecture=%s\n", (char *)(&g_mz_architecture[0])); for(s_index = 0;s_index < def_test_threads;s_index++) { if(pthread_create((pthread_t *)(&s_pthread[s_index]), (pthread_attr_t *)0, mz_test_thread, (void *)(&s_test_value)) != 0) { (void)fputs("pthread_create error\n", stderr); break; } } (void)fprintf(stdout, "threads=%d\n", s_index); while(g_mz_ctrl_break == 0) { #if def_use_lock != (0) mz_lock((int *)(&g_mz_lock)); #endif if(s_test_value == 0)(void)fprintf(stdout, "\n\x1b[1;31munsafe value = %d\x1b[0m\n", s_test_value); else { (void)fprintf(stdout, "\rsafe value = %d", s_test_value); (void)fflush(stdout); } #if def_use_lock != (0) mz_unlock((int *)(&g_mz_lock)); #endif mz_load_balance(); } while((--s_index) >= 0)(void)pthread_join(s_pthread[s_index], (void **)0); (void)fputs("\n\x1b[0mExit.\n", stdout); return(0); } /* vim: set expandtab: */ /* End of source */ |


mutex.tar.gz

댓글을 달아 주세요