세마포어는 뮤텍스와 유사한 동기화 객체이다?

 

-      뮤텍스는 하나의 공유 자원을 보호하기 위해 사용하지만 세마포어는 제한된 일정 개수를 가지는 자원을 보호하고 관리한다.

 

 

세마포어는 사용가능한 자원의 개수를 카운트하는 동기화 객체이다?

        

-      유효자원이 0이면 즉 하나도 사용 할 수 없으면 세마포어는 비신호상태가 되며 1이상이면, 즉 하나라도 사용할 수 있으면 신호상태가 된다.

 

 

 

신호상태

스레드의 실행을 허가하는 상태이다.

신호상태의 동기화 객체를 가진 스레드는 계속 실행할 수 있다. (신호등의 파란불)

비신호상태

스레드의 실행을 허가하지 않은 상태이다.

신호상태가 될 때까지 스레드는 블록된다. (신호등의 빨간불)



세마포어 개념을 위한 예)

 

-      네트워크를 통해 프로그램을 다운받는 프로그램을 만든다.

 

n  동시에 여러 개의 자료를 다운로드 받는 것이 가능하기는 하지만 별로 효율적이지는 않다.

n  어차피 네트워크의 대역폭은 정해져 있는 것이므로 한꺼번에 다운로드 받는다고 해서 빨라지는 것도 아니고 그렇다고 순서대로 받는 것은 불편하다.

n  동시에 받되 최대 3개까지만 동시 다운로드가 가능하도록 하고싶다.

n  실제 다운로드 툴이나 웹 브라우저도 일정 개수까지만 동시 다운로드를 지원한다.

 

 

 

 

이경우 자원이란 다운로드 권한이라 표현할 수 있을 것이며 세마포어는 이 권한을 카운트 한다. 세마포어의 초기값을 3으로 설정하고 사용자의 요구가 있을 때마다 다운로드 스레드를 생성한다. 스레드에서는 WaitForSingleObject함수를 호출하여 자신이 사용할 수 있는 권한이 아직 남아 있는지 검사해 보고 가능하다면 다운로드 루프로 진입한다. 이때 대기 함수는 세마포어의 카운트를 1감소시켜 자원이 하나 줄어들었음을 기록한다.

 

두번째, 세번째 스레드가 다운로드를 시작하면 세마포어의 카운트는 0이 되며 비신호상태가 된다. 그러면 이후부터 생성되는 스레드는 다운로드 루프로 진입하지 못하고 자원이 사용가능해질 때 까지 대기하게 된다. 대기중에 첫번 째 스레드가 다운로드를 완료하면 세마포어를 풀 것이고 그러면 세마포어는 다시 1증가하여

신호상태가된다.

 

 

 

세마포어 함수정리

 

 

 

세마포어 생성함수

HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG llnitialCount, Long lMaximumCount, LPCTSTR lpName);

 

lpSemaphoreAttributes

보안속성

llnitialCount

초기값(최대사용개수에 대한 값)

lMaximumCount

최대사용개수(몇 개의 스레드를 실행시킬수 있나)

lpName

이름

첫번째 , 네번째 인자는 프로세스간의 동기화에 사용된다. 한 프로세스에서만 사용 할 시 NULL 값을 주면된다.

 

 

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);

 

hHandle

하나의 동기화 객체가 신호상태가 되기를 기다린다.

dwMilliseconds

타임 아웃 시간

<dwMilliseconds>

1/1000초 단위로 지정하는데 이 시간이 경과하면 설사 동기화 객체가 비신호상태이더라도 즉시 리턴함으로써 무한 대기를 방지한다. 타임 아웃을 INFINITE로 짖어하면 신호상태가 될 때까지 무한정 대기한다.

 

 

 

WaitForSingleObject 리턴값

 

WAIT_OBJECT_0

hHandle 객체가 신호상태가 되었다.

WAIT_TIMEOUT

타임 아웃 시간이 경과하였다..

WAIT_ABANDONED

포기된 뮤텍스

 

 

 

세마포어의 카운트를 증가시켜주는 함수

 

BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);

 

hSemaphore

자원을 반환할  세마포어

lReleaseCount

자신이 사용한 자원의 개수를 알려준다.

lpPreviousCount

이전 카운트를 리턴받기 위한 참조 인수

세번째 인자는 이 값에 관심이 없으면 NULL을 넘기면 된다.

 

함수 사용 예제

프로시저실행시

HANDLE hSem;

hsem = CreateSemaphore(NULL, 3, 3 , NULL);

 

마우스왼쪽버튼클릭시

HANDLE hThread;

hThread = CreateThread(NULL, 0, ThreadDownLoad, NULL, 0 , &ThreadID);

 

스레드 진입시

// 다운로드 화면표시 초기화

WaitForSingleObject(hSem, INFINITE);

// 다운로드 실행화면 표시

설명

-      같은 프로세스 내에서 사용할 것이므로 이름을 줄 필요는 없으며 보안 속성도 지정하지 않았다.

-      다운로드 가능 최대 개수는 3으로 설정했으며 처음부터 3개의 자원이 다 사용 가능하므로 초기값도 3이다.

-      왼쪽버튼클릭시 스레드가 생성되고 다운로드 실행화면이 표시된다. 네번째 클릭하게되면  해당 스레드는 WaitForSingleObject에서 대기하다가 어느 스레드가 종료되면 세마포어가 신호상태로되고 기다리던 스레드는 실행하게된다.



출처 - 윈도우즈 API 정복 , 한빛미디어

'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

스레드  (0) 2010.01.24
이벤트  (0) 2010.01.24
파일매핑 사용순서  (0) 2010.01.24
Dll이란?  (0) 2010.01.24
파이프 추가내용  (0) 2010.01.20
Posted by 아몰라

 파일매핑 순서

1) CreateFile API로 파일을 연다.

2) CreateFileMapping API로 그 파일에 대한 파일 매핑 객체를 생성한다.

3) MapViewOfFile API로 파일 매핑 객체의 뷰를 생성한다.

 

HANDLE CreateFileMapping(                             //반환값: 객체 핸들 , 실패시 = INVALID_HANDLE_VALUE 반환

      HANDLE hFile,                                           //파일 핸들

      LPSECURITY_ATTIRBUTES lpAttrs,               //보안 속성

      DWORD flProtect,                                        //접근 허가 , 액세스 타입(PAGE_READONLY,

PAGE_READWRITE,PAGE_WRITECOPY)


      DWORD dwMaxSizeHigh,                            //매핑할 크기의 상위 32비트     , 둘다 0이면 전체파일

      DWORD dwMaxSizeLow,                            //매핑할 크기의 하위 32비트     , 둘다 0이면 전체파일

      LPCTSTR lpName);                                    //객체명 , 첫번째 인수를 NULL로 주었을때 다른 프로세스와
                                                                       동기화하기 위한 목적

 

뷰를 만들어 가상 주소 공간에 매핑! >>

매핑해서 파일 읽고 쓰려면 MapViewOfFile API로 '뷰'를 생성해야한다.

뷰는 실제 파일 내용이 매핑되는 가상 주소 영역.

 

LPVOID MapViewOfFile(                      //반환값: 맵핍항 프로그램상의 주소

      HANDLE hFileMapObj,                  //파일 매핑 객체 핸들 ,

      DWORD dwDesiredAccess,         //필요한 접근 제한 액세스 타입(FILE_MAP_WRITE,FILE_MAP_READ,FILE_
                                                     MAP_ALL_ACCESS,FILE_MAP_COPY)

      DWORD dwFileOfsHigh,              //뷰를 실행할 범위의 시작 오프셋 상위 32비트

      DWORD dwFileOfsLow,               //뷰를 실행할 범위의 시작 오프셋 하위 32비트

      SIZE_T dwBytesToMap);             //뷰를 실행할 범위의 크기

  

뷰의 메모리에서 범위를 지정하여 파일 갱신>>

 

BOOL FlushViewOfFile (                                   //반환값: 처리 성공 여부

      LPCVOID lpBaseAddr,                                //갱신할 영역 시작 주소

      DWORD dwNumberOfBytes);                        //갱신할 영역 크기

 

뷰 해제하기 >>

BOOL UnmapViewOfFil(                                    //반환값: 처리 성공 여부

      LPCVOID lpBaseAddr);                               //해체할 뷰의 시작주소

 

파일 매핑 객체 자체가 불 필요하게 될 경우엔 CloseHandle API로 닫기!!

 

 

이름붙은 파일 매핑 객체 핸들을 다른 프로세스에서 가져와 사용하기 >>

HANDLE OpenFileMapping(                               //반환값: 객체 핸들

      DWORD dwDesiredAccess,                         //필요한 접근 권한

      BOOL bInheritHandle,                                 //자식 프로세스에 상속할 것인가?

      LPCTSTR lpName);                                    //객체명  

[출처] 파일매핑|작성자 숙녀라면


'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

이벤트  (0) 2010.01.24
세마포어  (0) 2010.01.24
Dll이란?  (0) 2010.01.24
파이프 추가내용  (0) 2010.01.20
Hooking  (0) 2010.01.15
Posted by 아몰라

  라이브러리

라이브러리(Library)란 함수,데이터,타입 등 여러가지 프로그래밍 요소들의 집합이며 보통 LIB확장자를 가진다. (DLL사용시 h,dll파일 필요) 자주 사용되는 표준적인 함수를 매번 직접 작성해서 사용하는 것은 지나치게 시간 소모적이므로 표준화할 수 있는 함수를 미리 만들어서 모아 놓은 것이 라이브러리이다. 라이브러리를 한 번 구축해 놓기만 하면 다시 만들 필요없이 불러서 사용할 수 있으므로 개발 속도도 빨라지고 신뢰성도 확보할 수 있다.

 

한글 입출력을 하는 라이브러리를 생각해 보자. 한글은 무척 복잡한 언어이기 때문에 한글을 사용하는 모든 프로그램에서 한글 입출력 함수를 일일이 만들어 사용하기 어렵다. 그래서 누군가가 한 번만 한글 입출력 함수를 만들어 라이브러리로 배포하면 나머지 사람들은 이 라이브러리에 있는 함수를 불러 한글을 출력하기도 하고 입력받기도 한다.

 

  STATIC LINK

 

ProA.exe를 만드는 사람은 자신의 고유 코드만 ProA.cpp에 작성하고 HAN.LIB와 연결하면 한글 입출력 기능을 가진 ProA.exe. 실행 파일을 만들 수 있다. HAN.LIB에 있는 함수와 데이터는 링커에 의해 실행 파일에 그대로 옮겨지면 실행 파일의 일부분이 된다.

 이런 전통적인 라이브러리 연결 방법을 정적 링크(Static Link)라고 하면 컴파일시에 라이브러리에 코드를 실행파일에 복사한다.

 

 

  DLL

DLL은 동적 링크(Dynamic Link)를 사용한다. 동적 링크란 컴파일시에 함수의 코드가 실행 파일에 복사되는 것이 아니라 실행 중에 라이브러리에 있는 함수를 호출하는 방법을 말한다.

ProA.exe를 만드는 사람은 자신의 고유 코드만 ProA.cpp에 작성하고 이 소스를 컴파일하여 ProA.exe를 만든다. ProA.cpp에서는 한글 입출력 함수를 호출하지만 ProA.exe파일에는 한글 입출력 함수가 포함되어 있지 않다. 대신 ProA.exe는 한글 입출력 함수의 위치에 대한 정보를 가지고 있으며 이 프로그램이 실행될 때 HAN.DLL이 메모리에 같이 로드되며 ProA.exe에서 HAN.DLL에 있는 함수를 호출한다.

 

  DLL의 장점

     한 코드를 여러 프로그램이 동시에 사용하기 때문에 메모리가 절약된다.

     정적링크를 사용하는 경우 실행 파일에 라이브러리의 함수가 모두 포함되어 실행파일이 커지지만 DLL을 사용하는 프로그램은 크기가 작다.

     DLL을 교체하여 프로그램의 성능을 향상시키기 쉽다.

     리소스의 교체가 가능하다.

     코드의 양이 적어지므로 디버깅이 용이해진다

     혼합 프로그래밍이 가능해 진다

     프로그래머끼리 분담 작업이 용이하며 재사용성도 뛰어나다.

 

 

  정적링크와 동적 링크

정적 링크

컴파일 시에 함수가 실행 파일에 연결된다. 실행 파일에 함수의 코드가 복사되기 때문에 실행 파일의 크기가 커지는 단점이 있지만 실행 파일은 완전한 단독 실행 파일이 된다. 실행파일에 함수의 코드가 포함되어 있기 때문에 컴파일이 끝나면 라이브러라 파일(LIB)이 없어도 프로그램을 실행할 수 있다.

동적 링크

실행시에 함수가 실행 파일에 연결된다. 실행 파일에는 호출할 함수의 정보만 포함되고 실제 함수 코드는 복사되지 않ㅇ므로 실행 파일의 크기가 작아진다. 하지만 실행 파일은 함수에 대한 정보만 가지고 있을 뿐 실제 코드를 가지고 있지는 않으므로 프로그램 실행시에 DLL이 꼭 있어야 한다.

 

*  DLL관리

 윈도우가 DLL을 어떻게 관리하는지를 상상해 보자. 클라이언트 프로그램인 ProA.exe가 실행되면 이 프로그램의 실행에 필요한 HAN.DLL함수도 메모리로 같이 로드될 것이다. DLL은 자신을 로드한 프로세스의 가상 주소 공간에 맵핑되면 따라서 DLL은 메모리,스택,핸들을 프로세스와 공유하게 된다.

 

 두 번째 클라이언트인 ProB.exe가 실행될 때는 HAN.DLL이 이미 메모리에 올라와 있으면 HAN.DLL은 읽어올 필요가 없으며 ProB.exe만 읽어온다. 대신 윈도우는 ProB.exe의 주소 영역에 HAN.DLL이 로드된 메로리를 맵핑시켜 ProB.exe에서도 HAN.DLL의 함수를 자유롭게 호출할 수 있도록 한다. 세번째 클라이언트인 ProC.exe가 실행될 때도 마찬가지로 같은절차를 거칠 것이며 이 상태에서는 세 개의 클라이언트 프로그램이 하나의 DLL을 공유한다.

 

 

 DLL은 가상 메모리에 한 번 로드되면 다시 로드되지 않는다. 단 코드의 경우만 드렇지 DLL의 고유 변수는 클라이언트 프로그램이 실행될 때마다 매번 다시 메모리를 할당받아야 한다. 클라이언트 프로그램끼리 코드는 공유하지만 데이터는 공유할 수 없기 때문이다. C++클래스의 멤버 함수는 이 클래스로부터 만들어지는 모든 객체가 가 공유하지만 멤버 변수는 개별적으로 가지는 것과 마찬가지이다.

 

 예를 들어 HAN.DLL에 현재 입력 상태가 한글 모드인지 영문 모드인지를 기억하는 변수가 있다고 하자. 이 변수의 값은 클라이언트 프로그램별로 다를 수 있기 때문에 프로그램끼리 공유할 수 없다. 만약 이 변수를 공유한 상태에서 ProA.exe에서 한글 입력 상태로 바꾸면 ProB.exe ProC.exe도 같이 한글 입력 상태로 바뀌어 버릴 것이며 이는 논리적으로 바람직하지 못한 결과를 가져온다. 이런 식으로 DLL내부의 값을 기억하는 변수는 공유가 불가능하기 때문에 클라이언트 프로그램이 실행될 때마다 다시 메모리를 할당받아야 한다.

 

DLL이 메모리에서 지워지는 시기는 언제 쯤일까? 필요가 없어진 DLL은 당연히 메모리에서 사라져야 하지만 DLL은 사용자가 직접 사용하는 것이 아니기 때문에 종료시기를 쉽게 판단할 수 없다. DLL을 처음 메모리로 올린 ProA.exe가 종료될 때 HAN.DLL을 종료하는 방법이 가장 쉽게 떠올릴 수 있는 방법이다. 그러나 최초 DLL을 로드한 ProA.exe가 종료되어도 ProB.exe ProC.exe가 이 DLL을 계속 사용하고 있을 수도 있기 때문이다. DLL이 메모리에서 삭제되어야 할 시기는 DLL을 사용하는 모든 클라이언트 프로그램이 종료되었을 때이다.

 

윈도우는 DLL별로 사용 카운트라는 것을 유지하고 있으며 클라이언트 프로그램이 실행될 때마다 카운트를 1씩 증가시키고 클라이언트가 종료될 때마다 카운트를 1 감소시킨다. 이렇게 사용 카운트를 유지하면서 카운트가 0이 될 때 DLL을 메모리에서 삭제하면 아무런 문제가 없다.

ProA.exe실행

ProB.exe실행

ProC.exe실행

ProA.exe종료

ProC.exe종료

ProB.exe종료

HAN.DLL로드

 

 

 

 

HAN.DLL삭제

COUNT = 1

COUNT = 2

COUNT = 3

COUNT = 2

COUNT = 1

COUNT = 0

 

 

*  DLL접속

__declspec

DLL을 사용하려면 우선 함수를 제공하는 DLL에서는 자신이 제공하고자 하는 함수에 대한 정보를 밖으로 공개해 놓아야 하며 이 동작을 EXPORT라고 한다. 반대로 DLL을 사용하는 클라이언트에서는 어떤 DLL에 있는 어떤 함수를 사용하겠다고 선언해야 하는데 이 동작을 IMPORT라고 한다. 즉 함수를 제공하는 측은 어떤 함수를 제공하겠다는 선언이 있어야 하며 함수를 사용하는 측에서는 어떤 함수를 사용하겠다는 선언이 있어야 한다.

 

 

__declspec은 함수에 대한 정보를 제공하는 선언문이며 엑스포트 또는 임포트하는 함수 앞에 수식어로 이 문구가 있어야 한다. 원형은 다음과 같으며 앞의 밑줄이 두 개임을 유의하다

 

__declspec(extended-attribute) declaratory

 

__declspec문은 기억부류(Storage Class)에 관한 정보를 단순화,표준화하며 원래의 C++에는 없는 문장이지만 마이크로소프트에서 C++문법을 확장한 예 중 하나에 해단한다. 언뜻 보기에는 괄호가 있어 함수같지만 컴파일러가 제공하는 키워드이다. 기억 부류의 속성을 괄호 안에 인수로 지정한다. 사용가능한 인수는 네 가지

가 있다.

인수

설명

Thread

TSL(Thread Local Storage) 데이터로 지정한다. 이 지정자가 붙은 변수는 해당 스레드에서만 사용할 수 있는 변수가 된다.

Naked

접두(prolog),접미(epilog)를 생성하지 않는다. 어셈블리 언어를 사용하여 직접 접두,접미를 달고자 할 때 사용한다. 어셈블리 언어를 사용하여 가상 디바이스 드라이버를 작성할 때 이 기억부류를 사용한다. 함수에만 적용되며 변수에는 적용되지 않는다.

Dllimport

DLL에 있는 데이터,오브젝트,함수를 임포트한다. DLL에 있는 이렇게 생긴 함수를 앞으로 사용하겠다는 선언이다

dllexport

DLL에 있는 데이터,오브젝트,함수를 엑스포트한다. DLL이 사용하는 클라이언트(실행파일이거나 또는 다른 DLL)에게 DLL의 정보를 명시적으로 제공하는 역할을 한다.

Dllexport로 함수를 선언하면 DEF파일의 Exports란에 이 함수를 명시하지 않아도 되며 __export 키워드를 대체한다.

 

 

n  사용방법

 

DLL에서 엑스포트

extern “c” __deslpsec(dllexport) 함수원형;

client에서 임포트

extern “c” __deslpsec(dllimport) 함수원형;

 
암시적 연결 & 명시적 연결

  암시적 연결

함수가 어느 DLL에 있는지 밝히지 않고 그냥 사용한다. 프로젝트에 임포트 라이브러리를 포함해야 하며 윈도우즈는 임포트 라이브러리의 정보를 참조하여 알아서 DLL을 로드하고 함수를 찾는다. 클라이언트 프로그램이 로드될 때 DLL이 같이 로드되거나 이미 DLL이 로드되어 있으면 사용 카운트를 1 증가시킨다. 클라이언트 프로그램이 실행될 때 DLL이 로드되므로 실행시 연결이라고 한다.

n  임포트 라이브러리 DLL파일 찾는 순서

     클라이언트 프로그램이 포함된 디렉토리

     프로그램의 현재 디렉토리

     윈도우의 시스템 디렉토리

     윈도우 디렉토리

     PATH환경 변수가 지정하는 모든 디렉토리

만약 이 순서대로 DLL을 찾아보고 원하는 DLL이 발견되지 않으면 클라이언트 프로그램은 다음과 같은 에러메세지를 출력하고 실행을 종료한다.

 

 

 

※ 암시적 연결 실습

     새 프로젝트를 만든다(Win32 Dynamic-Link Library 형태로 생성)

 

     함수를 작성한다.

xyz.c

#define DLL_SOURCE

#include "xyz.h"   // 이 안에 있는 DLLFUNC __declspec(dllexport)로 된다.

 

int Add( int a, int b )

{

             return a + b;

}

xyz.h

#ifdef DLL_SOURCE

             #define DLLFUNC __declspec(dllexport)  

#else

             #define DLLFUNC __declspec(dllimport)

#endif

 

#include <windows.h>

 

EXTERN_C DLLFUNC int Add( int a, int b);

 

 

     컴파일하면 DLL파일,LIB파일이 생성된다.

 

     XYZ.Dll , XYZ.lib , XYZ.h 파일을 사용할 폴더에 복사한다.

     사용할 프로젝트에서 사용하고자 하는 DLL의 임포트 라이브러리를 프로젝트에 포함시킨다

 혹은  #pragma comment(lib, "xyz.lib")

Usxxyz.cpp

#include <windows.h> // user32.dll의 모든 함수 선언.

#include <stdio.h>

// DLL 사용하기

#include "xyz.h"                                                          // 관련 헤더 include

#pragma comment(lib, "xyz.lib")                      // 라이브러리 추가

void main()

{

MessageBox(0,"A","",MB_OK);

 

int s = Add(10,20);                           // DLL 함수 사용

printf("결과 : %d\n", s);

 

void* p1 = GetModuleHandle( "xyz.dll");

printf("xyz.dll 주소 : %p\n", p1);

printf("Add 주소 : %p\n",    Add);

}

 

정상적으로 값 30이 메시지박스로 뜨고 콘솔창에는 결과와 각각의 주소값이 출력

 


명시적연결의 사용법은 적지않았다
장점만 알아보고 가겠다.

    명시적 연결의 장점

  1. 필요할 때만 DLL을 읽어와 사용하기 때문에 메모리와 리소스가 절약된다. 암시적 연결의 경우 프로그램이 시작 될 때 DLL도 같이 메모리로 올라오므로 프로그램 실행중에 항상 DLL이 메모리에 상주하고 그만큼 메모리가 더 소비된다.
  2. 경우에 따라 사용할 DLL을 교체할 수 있다. LoadLibray에서 DLL 이름을 문자열로 줄 수 있으므로 상황에 맞게 DLL을 선택적으로 사용할 수 있다. 마찬가지로 호출할 함수도 문자열로 지정하므로 선택 가능하다.
  3. 필요한 DLL이 없는 경우에도 프로그램을 실행할 수 있다. 물론 이 경우 DLL에게 분담된 작업은 제대로 처리할 수 없겠지만 최소한 프로그램이 실행되지도 못하는 상황은 발생하지 않는다. DLL이 없을 경우 다른 방법으로 문제를 해결하거나 최소한 에러 메시지라도 보여줄 수 있다. 반면 암시적 연결의 경우는 DLL이 없으면 아예 실행을 시작할 수 조차 없다.
  4. 클라이언트 프로그램의 시작이 빠르다. 암시적 연결은 클라이언트 실행전에 DLL을 읽어와야 하는데 필요한 DLL의 개수가 수십개가 넘으면(실제로 그정도 된다)이 시간이 무시 못할 정도로 길어질 수도 있다.명시적 연결은 일단 프로그램이 실행된 후 필요할 때 DLL을 로드하므로 신속하게 실행된다. 최종 사용자 입장에서는 이 정도 시간 차이가 굉장히 크게 느껴진다.

 















'API & MFC > API & 시스템프로그래밍' 카테고리의 다른 글

세마포어  (0) 2010.01.24
파일매핑 사용순서  (0) 2010.01.24
파이프 추가내용  (0) 2010.01.20
Hooking  (0) 2010.01.15
서비스  (0) 2010.01.13
Posted by 아몰라