Windows 호환 Interlocked*() 함수 소스 (x86용)

익명 사용자의 이미지

질문 게시판에 어떤 분이 리눅스에서도 윈도처럼 InterlockedIncrement()같은 함수를 쓸 수 없는지 질문을 하셨더군요. 저의 Mogua 프로젝트(http://mogua.kldp.net)에서 구현한 Windows 호환 Interlocked*() 함수들의 소스를 여기 올립니다. 분량이 많지 않으므로 그냥 팁 정도로 봐주세요. :)

/*
 * Copyright 2003
 *     Bang Jun-Young.  All rights reserved.
 *
 * (라이센스 문구 생략. 자세한 사항은 별도 문의 요망)
 */

#include <windows.h>
#include "kernel32.h"

LONG WINAPI
InterlockedCompareExchange(LPLONG volatile Destination, LONG Exchange,
    LONG Comperand)
{
	LONG ReturnValue;

	__asm __volatile(
	    "lock cmpxchgl %2,(%1)"
	    : "=r" (ReturnValue)
	    : "r" (Destination), "r" (Exchange), "0" (Comperand)
	    : "memory");

	return ReturnValue;
}

LONG WINAPI
InterlockedDecrement(LPLONG volatile Addend)
{
	LONG ReturnValue;

	__asm __volatile(
	    "lock decl (%1)\n\t"
	    "movl (%1),%0"
	    : "=r" (ReturnValue)
	    : "r" (Addend)
	    : "memory");

	return ReturnValue;
}

LONG WINAPI
InterlockedExchange(LPLONG volatile Target, LONG Value)
{
	LONG ReturnValue;

	__asm __volatile(
	    "lock xchgl %2,(%1)"
	    : "=r" (ReturnValue)
	    : "r" (Target), "0" (Value)
	    : "memory");

	return ReturnValue;
}

LONG WINAPI
InterlockedExchangeAdd(LPLONG volatile Addend, LONG Value)
{
	LONG ReturnValue;

	__asm __volatile(
	    "lock xaddl %2,(%1)"
	    : "=r" (ReturnValue)
	    : "r" (Addend), "0" (Value)
	    : "memory");

	return ReturnValue;
}

LONG WINAPI
InterlockedIncrement(LPLONG volatile Addend)
{
	LONG ReturnValue;

	__asm __volatile(
	    "lock incl (%1)\n\t"
	    "movl (%1),%0"
	    : "=r" (ReturnValue)
	    : "r" (Addend)
	    : "memory");

	return ReturnValue;
}

리눅스에서 쓰려면 위 소스 앞에

#define WINAPI
typedef long LONG, *LPLONG;
정도만 추가해 주면 되겠습니다.

소스에 대해 짤막하게 설명을 드리면, 어셈블리 명령어 앞에 lock 접두사가 붙어 있는 걸 볼 수 있습니다. 그와 같은 명령어가 실행되면 버스에 LOCK# 신호가 인가됩니다. 그러면 현재 프로세서를 제외한 다른 프로세서가 공유 메모리에 접근하는 것이 순간적으로 차단됩니다. 따라서 공유 변수의 값을 변경할 때 스핀락을 걸어줄 필요없이 빠르게 락킹을 구현할 수가 있지요.

참, 한가지 더, xchg 명령은 lock 접두사 사용 여부와 상관없이 항상 LOCK# 신호를 발생시킵니다. 소스에서는 그냥 일관성을 유지하기 위해 lock 접두사를 달았습니다.

Forums: 
mastercho의 이미지

#include <windows.h>
#include "kernel32.h"

이건 모귤라 프로젝트에서 다운받아야 하는 라이브러리인가요?

근데 GUI부분의 함수들도 지원되는지...?

사실 이런게 있는지 좀 믿기지가 않는 ㅎㅎ

그럼 나중에 리눅스 함수와 윈도우 함수를 다 같이 사용할수 있다는 이야기가
되는건가요?

ㅎㅎ

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

익명 사용자의 이미지

mastercho wrote:
#include <windows.h>
#include "kernel32.h"

이건 모귤라 프로젝트에서 다운받아야 하는 라이브러리인가요?


위 두줄을 리눅스에선 삭제해 주세요. 그리고 모귤라(X) 모구아(O)

Quote:
근데 GUI부분의 함수들도 지원되는지...?

아직은 구현되지 않았습니다만, Mogua 프로젝트의 궁극적인 목표입니다.

Quote:
사실 이런게 있는지 좀 믿기지가 않는 ㅎㅎ

그럼 나중에 리눅스 함수와 윈도우 함수를 다 같이 사용할수 있다는 이야기가
되는건가요?

ㅎㅎ


같이 사용하는 것은 될 수도 있고 안될 수도 있습니다. 어차피 오픈소스란 개발자의 필요에 따라 발전하는 것이니까요. 8)
익명 사용자의 이미지

방준영 wrote:
참, 한가지 더, xchg 명령은 lock 접두사 사용 여부와 상관없이 항상 LOCK# 신호를 발생시킵니다. 소스에서는 그냥 일관성을 유지하기 위해 lock 접두사를 달았습니다.

참고로 lock을 거는 이유는 CPU가 2개 이상일때 bus를 잠근다는 뜻이죠?
또는 외부 장치가 접근하는것도...

inc, dec, mov, test, xchg 는 기본적으로 lock신호가 잡히는걸루
알고 있는데요. 아닌가보네. 누가좀 확인좀 해주세요. 나도 이거 헷갈림.
아마도 제 생각이 맞을듯 하지만 틀리다면
리턴값을 취해야 할때 문제가 좀 있을거 같네요.
그래서 다음과 같이 되어야 될듯 합니다.

InterLockerDec.. 만 예로 들면

     __asm __volatile( 
       "lock decl (%1)\n\t" 
       "lock movl (%1),%0" 
       : "=r" (ReturnValue) 
       : "r" (Addend) 
       : "memory"); 

그리고 Windows에서 사용하려면 다음과 같이 추가코드가 필요하겠군요.

#define WINAPI pascal
#define LONG long
#define LPLONG LONG *

LONG WINAPI 
InterlockedDecrement(LPLONG volatile Addend) 
{ 
   LONG ReturnValue; 
#if defined(WIN32)
   asm
   {
     MOV EAX, DWORD PTR Addend;
     LOCK dec DWORD PTR [EAX];
     MOV EAX, [EAX];
     MOV DWORD PTR ReturnValue, EAX; /* 안해도 되지만 ... 그냥 한거 */
   };
#else
     __asm __volatile( 
       "lock decl (%1)\n\t" 
       "movl (%1),%0" 
       : "=r" (ReturnValue) 
       : "r" (Addend) 
       : "memory"); 
#endif
   return ReturnValue; 
} 
익명 사용자의 이미지

minzkn wrote:
방준영 wrote:
참, 한가지 더, xchg 명령은 lock 접두사 사용 여부와 상관없이 항상 LOCK# 신호를 발생시킵니다. 소스에서는 그냥 일관성을 유지하기 위해 lock 접두사를 달았습니다.

참고로 lock을 거는 이유는 CPU가 2개 이상일때 bus를 잠근다는 뜻이죠?
또는 외부 장치가 접근하는것도...


예.

Quote:
inc, dec, mov, test, xchg 는 기본적으로 lock신호가 잡히는걸루
알고 있는데요. 아닌가보네. 누가좀 확인좀 해주세요. 나도 이거 헷갈림.

xchg만 그렇습니다.

Quote:
아마도 제 생각이 맞을듯 하지만 틀리다면
리턴값을 취해야 할때 문제가 좀 있을거 같네요.
그래서 다음과 같이 되어야 될듯 합니다.

InterLockerDec.. 만 예로 들면

     __asm __volatile( 
       "lock decl (%1)\n\t" 
       "lock movl (%1),%0" 
       : "=r" (ReturnValue) 
       : "r" (Addend) 
       : "memory"); 


리턴값과 같은 지역변수는 락을 걸 필요가 없습니다(컨텍스트마다 따로 생기므로). 그리고 mov에 lock 붙이면 invalid op 예외납니다.

Quote:
#define WINAPI pascal
#define LONG long
#define LPLONG LONG *

WINAPI는 Win32 API에서는 pascal이 아닌 stdcall로 정의되어 있습니다. 그리고 데이터타입은 typedef으로 정의하는 것이 원칙입니다.

Quote:
LONG WINAPI 
InterlockedDecrement(LPLONG volatile Addend) 
{ 
   LONG ReturnValue; 
#if defined(WIN32)
   asm
   {
     MOV EAX, DWORD PTR Addend;
     LOCK dec DWORD PTR [EAX];
     MOV EAX, [EAX];
     MOV DWORD PTR ReturnValue, EAX; /* 안해도 되지만 ... 그냥 한거 */
   };
#else
     __asm __volatile( 
       "lock decl (%1)\n\t" 
       "movl (%1),%0" 
       : "=r" (ReturnValue) 
       : "r" (Addend) 
       : "memory"); 
#endif
   return ReturnValue; 
} 

앗, 안해도 되는 걸 한 걸 들켰습니다... 8)

원래 코드를 좀더 C 스타일로 썼더라면

LONG WINAPI 
InterlockedIncrement(LPLONG volatile Addend) 
{ 
   __asm __volatile( 
       "lock incl (%0)\n\t" 
       : /* no outputs */
       : "r" (Addend) 
       : "memory"); 

   return *Addend; 
}

정도 되었을 것입니다. 그런데 이렇게 하면 리턴값을 처리하는 과정에서 메모리를 참조하는 여분의 명령어가 생기더군요. 그래서 할 수 없이 출력을 ReturnValue로 받은 것이죠.
sliver의 이미지

방준영 wrote:
LONG WINAPI
InterlockedDecrement(LPLONG volatile Addend)
{
	LONG ReturnValue;

	__asm __volatile(
	    "lock decl (%1)\n\t"
	    "movl (%1),%0"
	    : "=r" (ReturnValue)
	    : "r" (Addend)
	    : "memory");

	return ReturnValue;
}


위의 코드에서 decl과 movl사이에서 컨텍스트 스위치가 되어

다른 쓰레드가 *Addend를 건들게 되면 리턴값이 이 함수의 의도(?)대로

안되는 것 같은데.. 맞나요?

익명 사용자의 이미지

sliver wrote:
위의 코드에서 decl과 movl사이에서 컨텍스트 스위치가 되어

다른 쓰레드가 *Addend를 건들게 되면 리턴값이 이 함수의 의도(?)대로

안되는 것 같은데.. 맞나요?


으악, minzkn님과 silvers님 말씀이 맞았습니다. 제가 엉뚱한 생각을 하고 있었군요. :oops: 지금 개선버전을 만들고 있으므로 잠시후 올리겠습니다.
익명 사용자의 이미지

Quote:
리턴값과 같은 지역변수는 락을 걸 필요가 없습니다(컨텍스트마다 따로 생기므로). 그리고 mov에 lock 붙이면 invalid op 예외납니다.

쩝.. 제 예제 코드는
그냥 생각나는데로 짠거라서 오타가 있어요.
뒤에 "\n\t"를 안붙였거든요.

원래 invalid op는 안나오는데요.

제가 좀 테스트해보고 올려야 하는데 워낙 이런거 궁색해서... 오타가 있으니 양해바래요.

익명 사용자의 이미지

아래 코드는 어떻습니까?

LONG WINAPI
InterlockedDecrement(LPLONG volatile Addend)
{
	LONG ReturnValue;

	__asm __volatile(
	    "xorl %0,%0\n\t"
	    "decl %0\n\t"
	    "lock xaddl %0,(%1)\n\t"
	    "decl %0\n\t"
	    : "=a" (ReturnValue)
	    : "r" (Addend), "0" (ReturnValue)
	    : "memory");

	return ReturnValue;
}

LONG WINAPI
InterlockedIncrement(LPLONG volatile Addend)
{
	LONG ReturnValue;

	__asm __volatile(
	    "xorl %0,%0\n\t"
	    "incl %0\n\t"
	    "lock xaddl %0,(%1)\n\t"
	    "incl %0\n\t"
	    : "=a" (ReturnValue)
	    : "r" (Addend), "0" (ReturnValue)
	    : "memory");

	return ReturnValue;
}
익명 사용자의 이미지

minzkn wrote:
Quote:
리턴값과 같은 지역변수는 락을 걸 필요가 없습니다(컨텍스트마다 따로 생기므로). 그리고 mov에 lock 붙이면 invalid op 예외납니다.

쩝.. 제 예제 코드는
그냥 생각나는데로 짠거라서 오타가 있어요.
뒤에 "\n\t"를 안붙였거든요.

원래 invalid op는 안나오는데요.


인텔 매뉴얼에 보면
Quote:
The LOCK prefix can be prepended only to the following instructions and only to those forms of the instructions where the destination operand is a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD and XCHG. ... An undefined opcode exception will be generated if the LOCK prefix is used with any instruction not in the above list.

라고 되어 있습니다. 실제로 코드를 짜보더라도 MOV앞에 LOCK을 붙이면 프로그램이 죽는 것을 볼 수 있습니다.
익명 사용자의 이미지

Quote:
실제로 코드를 짜보더라도 MOV앞에 LOCK을 붙이면 프로그램이 죽는 것을 볼 수 있습니다.

크크크 죄송합니다. 물의를 일어켜서...
테스트도 안해보고 답변쓰는 무식한 저를 용서하소소.....
테스트해보니 맞군요.

익명 사용자의 이미지

이거 어떤가요? 좋죠? 이번에는 테스트해서 올린겁니다.

unsigned long LockInc(unsigned long * volatile s_Value)
{
 unsigned long s_Return;
 __asm__ volatile(
  "\n\t"
  "lock xaddl %1, (%2)\n\t"
  "incl %1\n\t"   
  "\n\t"
  : "=a"(s_Return)
  : "a"(1), "r"(s_Value)
 );	 
 return(s_Return);
}
mastercho의 이미지

이 대화에 끼려면 어셈블리를 배워야 하나? -_-;

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

익명 사용자의 이미지

minzkn wrote:
이거 어떤가요? 좋죠? 이번에는 테스트해서 올린겁니다.

예, 좋습니다. :) 그러나 두가지 사소한 문제가 있네요.

1. "\n\t"같은 행은 불필요합니다.
2. 입력란에 (1)이 s_Return과 동일한 레지스터를 사용한다면 "a" 대신 "0"을 쓰는 것이 좋겠습니다.

어쨌든 좋은 지적에 감사드립니다.

익명 사용자의 이미지

한가지 궁금한 점이 있었는데,

movl $1,%eax

xorl %eax,%eax
incl %eax
중에 어느쪽이 빠를까요? 언뜻 앞의 것이 빨라보이지만 명령어 길이가 5바이트로 길다는 문제가 있습니다. 뒤의 것은 명령어 개수는 2개지만 길이는 합쳐서 3바이트에 불과하구요.
익명 사용자의 이미지

방준영 wrote:
1. "\n\t"같은 행은 불필요합니다.

불필요한거 알면서 일부러 삽입한건데요.
이유는 별거 없고 -S를 줘서 어셈블리단계로 바꿀때
보기좋게 나오더라고요. 그래서 이렇게 쓰는 버릇이 있어요.

그리고 "a"대신에 "0"을 쓰는 경우가 맞는데
저도 그거 자주 쓰기는 하지만 컴파일러가 그거 선택하는데 고민할까봐
그냥 직접 지정해줬어요. 컴파일러가 너무 고생해서 배려한거뿐 (농담)

사실 긴 소스에서는 "0"을 쓰면 좋지만 짧은 코드에서는 굳이 그럴 이유는
없지 않을까 하는 생각입니다. 어떠세요?
굳이 짧은코드에 "0"을 사용해야 할까요?

분명 Return값은 "eax"로 리턴하는게 분명하고 긴 코드에서야 eax가 다른용도로
사용될수도 있어 백업이 가능하다는 뜻으로 "0"을 사용하는거 아닌가요?

잘못이해하고 있는건가?

sliver의 이미지

방준영 wrote:
한가지 궁금한 점이 있었는데,

movl $1,%eax

xorl %eax,%eax
incl %eax
중에 어느쪽이 빠를까요? 언뜻 앞의 것이 빨라보이지만 명령어 길이가 5바이트로 길다는 문제가 있습니다. 뒤의 것은 명령어 개수는 2개지만 길이는 합쳐서 3바이트에 불과하구요.

i386 문서를 보니
mov imm32, reg32
xor reg32, reg32
inc reg32
이 세 명령어 모두 2싸이클씩 걸리는 군요

따라서 386에서는 movl $1, %eax가 빠른듯 합니다.
그런데 IA-32 vol-2를 보니 i386문서와는 달리 싸이클에 대해 전혀 언급이 없네요..-_-;;
펜티엄 이상에서는 어떨지는 알 수가 없네요 :cry:

익명 사용자의 이미지

minzkn wrote:
방준영 wrote:
1. "\n\t"같은 행은 불필요합니다.

불필요한거 알면서 일부러 삽입한건데요.
이유는 별거 없고 -S를 줘서 어셈블리단계로 바꿀때
보기좋게 나오더라고요. 그래서 이렇게 쓰는 버릇이 있어요.


개인 취향의 문제였군요. 8)

Quote:
사실 긴 소스에서는 "0"을 쓰면 좋지만 짧은 코드에서는 굳이 그럴 이유는
없지 않을까 하는 생각입니다. 어떠세요?
굳이 짧은코드에 "0"을 사용해야 할까요?

모든 경우에 일률적으로 적용하긴 그렇지만 이 경우는 "0"을 사용하는 것이 더 적절해 보입니다. 혹시나 ReturnValue를 다른 레지스터에 배정할 경우에도 코드 변경부분을 더 적게 할 수 있으니까요.
익명 사용자의 이미지

익명사용자의 이미지

뒷북같아 미안합니다.
링크가 깨진것 같은데 되는 링크를 얻을 수 있을까요?

neodeath의 이미지

봉 많이 받으실꺼에요
neodeath(골)kornet.net

언제 고수되나.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.