멀티스레드 프로그래밍 시 memmove() 함수는 안전한가요?

프로그래밍 일반에 관한 포럼입니다.

Moderator: 류광

Locked
비회원

멀티스레드 프로그래밍 시 memmove() 함수는 안전한가요?

Post by 비회원 »

안녕하세요.

스트레스 테스트 클라이언트를 만들고 있습니다.


20개의 쓰레드 까지는 아무런 이상 없이 잘 돌아가던 녀석이

30개 이상으로 끌어올리면 (10, 20, 30 단위로 설정할 수 있습니다.)

memmove 에서 꼭 애러가 납니다.

memmove는 메인 스레드에서 CriticalSection을 걸어 사용하고 있는데도

꼭 이부분에서 애러가 나는군요. ㅜㅜ

Code: Select all

--- .\Intel\MEMCPY.ASM  -----------------------------------------------------------------------------------------------
memmove:
10218C10   push        ebp
10218C11   mov         ebp,esp
10218C13   push        edi
10218C14   push        esi
10218C15   mov         esi,dword ptr [src]
10218C18   mov         ecx,dword ptr [count]
10218C1B   mov         edi,dword ptr [dst]
10218C1E   mov         eax,ecx
10218C20   mov         edx,ecx
10218C22   add         eax,esi
10218C24   cmp         edi,esi
10218C26   jbe         CopyUp (10218c30)
10218C28   cmp         edi,eax
10218C2A   jb          CopyDown (10218da8)
CopyUp:
10218C30   test        edi,3
10218C36   jne         CopyLeadUp (10218c4c)
10218C38   shr         ecx,2
10218C3B   and         edx,3
10218C3E   cmp         ecx,8
10218C41   jb          CopyUnwindUp (10218c6c)
[b]10218C43   rep movs    dword ptr [edi],dword ptr [esi][/b]
10218C45   jmp         dword ptr [edx*4+10218D58h]
CopyLeadUp:
10218C4C   mov         eax,edi
10218C4E   mov         edx,3
10218C53   sub         ecx,4


볼드체로 써놓은 곳에서 애러가 납니다. ㅠ.ㅠa

memmove 함수가 쓰레드에 안전하지 않은 함수라서 이런 오류가 발생하는 것이라면
대체 함수가 있겠죠? ㅠ.ㅠa

우욹. 도무지 애러의 이유를 모르겠네요.

가르침 부탁드립니다.
비회원

Post by 비회원 »

memmove 앞뒤로 크리티컬 섹션으로 묶어도, 메모리에 락을 거는건 아니니까

다른곳에서 같은 메모리에 접근하는 경우 문제가 발생하는게 아닐까 추측해봅니다.

memmove 로 접근하는 메모리 영역을 같이 사용하는 쓰레드에서는 모두 하나 함수로 접근하게 통일하고

그쪽에서 접근 메모리 주소를 계산해서, 대기시키거나 허용하는 동기화를 시켜면 어떨까요.
비회원

어? 저는 구경하던 비회원입니다.

Post by 비회원 »

딴지걸려는게 아니구요 답변해주신분의 글중
memmove 앞뒤로 크리티컬 섹션으로 묶어도, 메모리에 락을 거는건 아니니까
이부분을 보고 처음엔 아 그렇지 하다가

이내 헷갈리기 시작했습니다.

동기화객체를 쓴다는건 어느것을 쓰던 실질적으론 해당 함수 자체가 아니라

그에 사용되는 크리티컬 오브젝트가 중요한 것이 아닙니까?
이것이 저의 질문이자 글의 요지 입니다.

크리티컬오브젝트 g_crit;
스레드콜벡_1()
{
while(1)
{
크리티컬시작(g_crit);
printf("1을 출력하자");
크리티컬끝();
}
}


스레드콜벡_2()
{
while(1)
{
크리티컬시작(g_crit);
printf("2를 출력하자");
크리티컬끝();
}
}

위와 같은 경우 스레드콜벡1과 스레드콜벡2가 동시에 돌아간다 가정하면
1을출력하자와 2를 출력하자가 순서대로 출력되리라 봅니다만..

메모리영역이던 함수던 중요한건 크리티컬 객체가 아닌가 생각됩니다..


다시 원 주제로 돌아와서,

일단 스레드 공유영역이 의심되시면

임시 테스트 유닛을 작성 해보시는건 어떨까요?

가령 메인스레드 에서 크리티컬 섹션을 걸었는데 에러가 난다 하셨다면

아마 추측컨데 메인스레드와 n개의 스레드가 공유하는 데이터가 있고
n개의 스레드에서 이 데이터를 변경하는 구조라고 가정이 됩니다.

이때는 둘중 한곳은 무조건 참조만 하고 한곳은 쓰기만 하게끔 만드는
테스트유닛을 작성해보시는건 어떨까요?

스레드 갯수의 차이에 따라 문제여부가 달라진다면
가령 버퍼오버런이생겼을수도 있구요
memmove가 아니라 비슷한 역할을 하는 memcpy를 써보시는건 어떨까요?
seeper
Posts: 1483
Joined: 2003-06-06 23:19
Contact:

Post by seeper »

memmove 내에서 프로그램이 죽는 문제 같은데 그렇다면
대부분 잘못된 메모리를 접근했을겁니다.
(메모리 어드레스가 값으로 복사되기 때문에 memcpy 중에 잘못될일은 없을것 같습니다.)

잘못된 메모리가 아니라면 오작동했으면 했지 죽지는 않을겁니다.
CriticalSection 은 일반적으로 트랜잭션 개념입니다.
어차피 하나의 cpu는 한번에 하나의 인스트럭션만 처리하니까 동시에 실행되는 문제가 아니라
어떤 명령어 섹션(section)이 중간에 값이 바뀌면 안되는 경우를 의미합니다.

좀 복잡해지니 슈도 코드로 설명하는게 나을것 같습니다.

Code: Select all

void DelResource(int handle)
{
    LOCK();
    DelResourceData(handle); // 내부에 CS(Critical Section)가 없다.
    UNLOCK();
}

void GetResource(int handle, DATA* out)
{
    const DATA* data = GetResourceData(handle); // 내부에 CS가 없다.
    LOCK();
    memcpy(out, data, data->GetSize());
    UNLOCK();
}
위의 코드는 전혀 쓰레드에 안전하지 않은 코드입니다.
GetResource()가 호출된 시점에서 DelResource()가 호출된다면
const DATA* data가 가리키는곳이 이미 비워진 후입니다.
이런 상황이니 memcpy, memmove뿐만 아니라
쓰레드에 안전한 SuperSafeMemcpy나 SuperSafeMemmove를 만들어도 프로그램은 죽습니다.
data는 잘못된 주소값이니까요.

Code: Select all

void DelResource(int handle)
{
    LOCK();
    DelResourceData(handle); // 내부에 CS가 없다.
    UNLOCK();
}

void GetResource(int handle, DATA* out)
{
    LOCK();
    const DATA* data = GetResourceData(handle); // 내부에 CS가 없다.
    memcpy(out, data, data->GetSize());
    UNLOCK();
}
위의 코드는 안전해 졌습니다.
GetResourceData()와 DelResourceData()에서만 리소스를 가져오고 삭제한다면 안전한 코드가 되겠죠.
여기서 AddResourceData()를 추가했는데 이 주위에 LOCK()이 없다면 다시 불안정해지는 코드가됩니다.

쓰고보니 단순한 내용을 복잡하게 쓴 것 같습니다.. -_-;;
결론적으로는 memmove를 호출할때 인자값을 조사해보세요.
외부 함수에서 에러날때는 이걸 조사하는게 제일 좋습니다.
거기서 쓰레드 문제인지 아니면 다른 문제인지가 나오는거겠죠.
Last edited by seeper on 2006-04-02 16:30, edited 2 times in total.
seeper0 (a) gmail.com [email주소 무단수집거부]
비회원

Post by 비회원 »

아, 그렇죠. memmove 외에도 접근이 되면 문제가 발생하는거죠.

DB 레코드 락처럼 메모리락 걸리는게 아니다라는 얘기할려다가 memmove 만 묶으면 된다고 썼을까요 :)

콘솔 객체는 내부 동기화 처리를 하기때문에 콘솔 출력함수는 따로 동기화 처리안해도 될겁니다.
윈도에서 메세지 큐랑 이런게 몇개 있었던거 같네요.
비회원

답변 감사드립니다.

Post by 비회원 »

스레드 연 비회원 입니다.

답변 정말 감사드립니다.



먼저 경과를 말씀드리자면, 아직도 뚜렷한 해결책을 찾지 못하고 있습니다.
MFC에서 쓰레드를 20~30개 이상 불린다는게
이런 대책없는 결과를 가져올줄은 꿈에도 생각 못했습니다. (먼산)



리포트를 해드리면 아래와 같습니다.

memmove에서의 dest와 src는 별 이상이 없었으며 len 역시 양수의 정상적인 수치가 꾸준히 들어왔습니다.
헌데 어느 시점에서 갑작스럽게 src의 값이 엉뚱한 값으로 변하며 애러가 발생하고 어플이 뻗어버립니다.



오기로라도 원인을 찾아내어 고쳐보고 싶었지만,
시간사정상 본 솔루션을 보류해두고 콘솔 어플로 만들어야겠습니다.
콘솔 어플에서는 이러한 현상이 일어나지 않을 것이라는 결론에 도달했기 때문이죠. -_-;
다행히 몇시간 정도만 투자하면 기능들을 모두 똑같이 구현할 수 있을 정도 크기의 어플인지라
정말정말 다행이네요. (그래도 시간이 무척 아까워집니다. ...)

여튼 시간이 생겼을 때 다시 원인을 찾아봐야겠습니다.

엉뚱한 곳에서 src의 주소값이 변경됐다고 충분히 생각해볼 수 있겠지만
src에 들어오는 포인터가 수정되는 코드는
char *src; (편의성 선언)
...
src += len;
...
src -= len;
...

위에 보시는 바와 같이 단 두 줄 뿐인지라 현재로선 도저히 애러의 원인을 못찾겠군요.

답변 주신분들께 다시 한 번 감사의 인사 올리며 이만 줄이겠습니다.

감사합니다.
seeper
Posts: 1483
Joined: 2003-06-06 23:19
Contact:

Post by seeper »

그 src 해당부분만 다 찍어보세요.
len이 영향을 준다면 len도 같이 찍어봐야겠죠.
단지 세군대라면 세군데에서 모두 찍어보세요.
(경우에 따라서는 변화 전, 변화 후로 나눠서도 찍을수 있겠죠.)
seeper0 (a) gmail.com [email주소 무단수집거부]
Xine
Posts: 253
Joined: 2002-08-12 00:37

멀티스레드의 문제라기 보다는...

Post by Xine »

Code: Select all

rep movs    dword ptr [edi],dword ptr [esi]
여기에 멈췄을 때 watch 창에 edi, esi 값을 찍어보시고,

인자값이 dst == edi || src == esi 이면 memmove 함수로 넘겨진
dst 나 src 주소가 잘못된 것이고 아니라면 메모리오버런 일수 있으니
edi - dst <= len 인지 검사해보시면 되겠네요.

C Run-time Library 의 thread-safe 문제는 아닌듯...
이덕희
비회원

Post by 비회원 »

락을 건 상태에서,
src += len;
에서 힙 잡아주고,
src -= len;
에서 풀어주면 안정적일 것 같은데요....
미리 힙을 잡아주셨다가 len이 바뀌는 경우가 아닐까 생각되서요..
비회원

답변 감사드립니다.

Post by 비회원 »

답변 주셔서 감사합니다!

아무래도 len이 어떤 원인에 의해 유효하지 않은 값으로 변신하나봅니다.

len이 터무니 없는 수치일 경우를 예외처리 해주니 더이상 오류가 생기지 않는군요.


다시 한 번 감사드립니다. (꾸벅)
Locked