Files
RepoMain/ZCppMain/ZCProcess_Linux.H

4696 lines
160 KiB
C++

#ifndef __ZCPPMAIN__PROCESS_LINUX_H__
#define __ZCPPMAIN__PROCESS_LINUX_H__
#include "ZCppMain/ZMainHead.H"
#include <fstream>
#include <sys/time.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <pthread.h>
namespace ZNsMain
{
/*////////////////////////////////////////////////////////////////////////
■ 대부분의 쓰레드 함수는 성공하면 0 을 반환하는데 이 값을 enum 지정한다.
////////////////////////////////////////////////////////////////////////*/
namespace ZNsEnum
{
// 쓰레드 관련 함수가 에러일 경우에는 정책에 따라, ZEThread_Invalid 이외의 값을 반환할 수도 있다.
enum ZEThread
{
ZEThread_OK = 0 ,
ZEThread_Invalid= EINVAL , // 쓰레드 관련 함수 에러.
ZEThread_TimeOut= ETIMEDOUT
};/*
enum ZEThread*/
/*////////////////////////////////////////
■ barrier 의 대표적인 반환값.
■ barrier 에러를 체크할 때
BarrierClass VO_BarrierClass;
if(VO_BarrierClass.Init()==ZEBarrier_NO)
{
// some code
}
로 하지 말고.
if(VO_BarrierClass.Init()!=ZEBarrier_OK)
{
// some code
}
로 해야 안전하다.
////////////////////////////////////////*/
enum ZEBarrier
{
ZEBarrier_NO = -1,
ZEBarrier_OK = 0
};/*
enum ZEBarrier*/
}/*
namespace ZNsEnum*/
/*/////////////////////////////////////////////////////////////////////////////////////
■ 리눅스에서 pthread 라이브러리에 링크를 걸어줄 것.
■ 한컴 베포판 3.0 의 경우 sys/sem.h 에는 다음과 같은 설명이 나와 있다.
The user should define a union like the following to use it for arguments
for `semctl'.
union semun
{
int val; <= value for SETVAL
struct semid_ds *buf; <= buffer for IPC_STAT & IPC_SET
unsigned short int *array; <= array for GETALL & SETALL
struct seminfo *__buf; <= buffer for IPC_INFO
};
Previous versions of this file used to define this union but this is
incorrect. One can test the macro _SEM_SEMUN_UNDEFINED to see whether
one must define the union or not.
#define _SEM_SEMUN_UNDEFINED 1
■ IPC object 의 플래그는 IPC_CREAT, IPC_EXCL, 0 의 세 값중의 하나이며, 0 이면 해당 동
기화 object 가 존재할 때만 연다.
<semaphore.h> 파일에 있는 세마포어 함수는 프로세스의 경우에는 지원되지 않고 있다.
그래서 프로세스의 경우에는 <sys/sem.h> 의 세마포어 함수를 써야 한다.
-- 2004-07-12
/////////////////////////////////////////////////////////////////////////////////////*/
union semun
{
int val ; // value for SETVAL
struct semid_ds* buf ; // buffer for IPC_STAT & IPC_SET
unsigned short int* array; // array for GETALL & SETALL
struct seminfo* __buf ; // buffer for IPC_INFO
};/*
union semun*/
/*//////////////////////////////////////////////////////////////////
■ 윈도우에서는 동기화 object 에 이름을 부여할 수 있는데, 리눅스에
서는 숫자키만 부여할 수 있다. 그래서 호환을 위하여 이름으로부터
키를 유추하는 함수를 만들었다. 파일 이름을 안다면 ftok() 함수
key_t ftok(const char *path, int id);
를 사용하는 것도 방법이다.
//////////////////////////////////////////////////////////////////*/
static inline long ZfGetKeyNum(const char* const AP_Name)
{
int VI_Index =-1 ;
long VL_KeyID = 1 ;
if(AP_Name!=0)
{
while(AP_Name[++VI_Index]!=0)
{
VL_KeyID += AP_Name[VI_Index]*(VI_Index+1) ;
}/*
while(AP_Name[++VI_Index]!=0)*/
}/*
if(AP_Name!=0)*/
return VL_KeyID;
}/*
static inline long ZfGetKeyNum(const char* const AP_Name)*/
/*/////////////////////////////////////////////////////////////////////////////
■ 시그널은 쌓이지 않는다. 즉 동일한 이벤트가 연이어서 다섯 번 발생했다고 해서
프로세서에게 동일한 시그널을 다섯 번 전달해 주지 않는다. 이러한 문제점은
sa_mask 를 통해 해결할 수 있다. sa_mask 에 설정된 시그널들은 동일한 이벤트가
연이이서 다섯번 발생하는 경우 순차적으로 시그널을 발생시킨다. 즉 첫번째 시그
널이 처리되는 동안 나머지 시그널들은 블로킹 상태에 있게 된다.
-- TCP/IP 소켓 프로그래밍 (윤성우 저) 245 Page
■ 중요 Ansi, Posix 시그널 정의
#define SIG_ERR ((__sighandler_t) -1) // Error return.
#define SIG_DFL ((__sighandler_t) 0) // Default action.
#define SIG_IGN ((__sighandler_t) 1) // Ignore signal.
#define SIGHUP 1 // Hangup (POSIX)
#define SIGINT 2 // Interrupt (ANSI)
#define SIGQUIT 3 // Quit (POSIX)
#define SIGILL 4 // Illegal instruction (ANSI)
#define SIGTRAP 5 // Trace trap (POSIX)
#define SIGABRT 6 // Abort (ANSI)
#define SIGFPE 8 // Floating-point exception (ANSI)
#define SIGKILL 9 // Kill, unblockable (POSIX)
#define SIGUSR1 10 // User-defined signal 1 (POSIX)
#define SIGSEGV 11 // Segmentation violation (ANSI)
#define SIGUSR2 12 // User-defined signal 2 (POSIX)
#define SIGPIPE 13 // Broken pipe (POSIX)
#define SIGALRM 14 // Alarm clock (POSIX)
#define SIGTERM 15 // Termination (ANSI)
#define SIGSTKFLT 16 // Stack fault
#define SIGCHLD 17 // Child status has changed (POSIX)
#define SIGCONT 18 // Continue (POSIX)
#define SIGSTOP 19 // Stop, unblockable (POSIX)
#define SIGTSTP 20 // Keyboard stop (POSIX)
#define SIGTTIN 21 // Background read from tty (POSIX)
#define SIGTTOU 22 // Background write to tty (POSIX)
#define SIGSYS 31 // Bad system call
#define SIGUNUSED 31
◇ SIGHUP
- 터미널 인터페이스에 의해 연결의 단절이 감지되면 해당 제어 터미널과 연결된 제어 프로세스(세션 리더)에게 전달
- 세션 리더가 종료했을 때도 발생하는데 이때는 foreground 프로세스 그룹내의 모든 프로세스들에게 전달
- SIGHUP 시그널은 원래 모뎀 연결 끊김 등의 시리얼 라인이 끊어지면 발생하는 시그널이다.
- 이름 있는 시스템 데몬들은 SIGHUP 시그널을 configure file을 다시 읽어들이는 등의 초기화 신호로 해석한다.
. bootp(8), gated(8), inetd(8), mountd(8), named(8), nfsd(8), ypbind(8)
. pppd(8) 처럼 SIGHUP을 원래의 의도에 충실하게 세션 종료의 뜻으로 받아들이는 사례도 간혹 있는데, 요새는 보편적으로 이 역할을 SIGTERM이 맡는다.
. daemon은 제어 단말기 없이 돌기 때문에 kernel로부터 SIGHUP 신호를 수신하지 못한다.
그러므로 많은 daemon은 이 신호를 daemon의 구성 파일이 변경되어 daemon이 그 파일을 새로 읽어야 된다는 것을 알려주는 관리자로부터의 통지로 사용한다.
. daemon이 수신할 수 없는 다른 두 개의 신호로 SIGINT와 SIGWINCH가 있고 이들도 역시 어떤 변화를 daemon에게 통지하기 위해 사용될 수 있다.
◇ SIGINT
- 인터럽트 키 (DELETE 또는 Control-C)가 눌렸을 때 발생
◇ SIGQUIT
- Control-backslash 에 의해 발생
◇ SIGCHLD
- 프로세스가 종료하거나 정지하면, 부모 프로세스에게 전달된다.
- 부모 프로세스는 wait() 시스템 콜을 사용하여 무슨 일이 일어났는지 알아본다.
- 이 시그널에 대한 default 처리는 무시하는 것이다. 즉 프로세스가 이 신호를 받으려고 할 때만 전달된다.
◇ SIGSEGV
- 유효하지 않은 가상 메모리 주소를 참조하거나 사용 권한이 없는 메모리에 접근할 때 프로세스로 전달된다.
◇ SIGTERM
- kill 명령에 의해 기본적으로 발생
◇ SIGKILL
- "극단의 조치(extreme prejudice)"로 프로그램을 종료하는 데 사용된다.
- 시그널 catch 하거나 무시할 수 없다.
◇ SIGALRM
- alarm()이나 setitimer() 시스템 콜로 설정한 알람 시간이 초과 했을 때 프로세스로 전달된다.
◇ SIGTSTP
- Control-Z 키에 의해 발생
- 기본 처리 방법은 SIGCONT 신호를 받을 때까지 프로세스를 중단한다.
◇ SIGCONT
- 정지한 프로세스를 계속 실행시키려 할 때 발생
- 이 신호는 받을 수 있지만 블록하거나 무시할 수 없다.
- 기본 처리 방법은 중단된 프로세스를 재시작하는 것이다. 그러나 프로세스가 신호를 받지 않는다면 신호를 버린다.
- vi 에디터를 사용할 때
. Control-Z 를 눌러 수행을 잠시 정지시키면 쉘이 키 입력을 처리하게 되는데
. 이때 fg 명령을 실행시키면 쉘은 vi 에게 SIGCONT 시그널을 전달하며
. vi는 이 시그널에 대한 처리로 화면을 다시 그리고 사용자 키 입력을 받는 상태로 돌아간다.
◇ SIGSTOP
- SIGTSTP과 동일하나 catch 하거나 무시할 수 없다.
- 이 신호를 받으면 무조건 SIGCONT 신호를 받을 때까지 프로세스를 중단한다.
◇ SIGABRT
- abort() 함수의 호출로 발생
◇ SIGBUS
- 하드웨어 결함으로 발생
◇ SIGEMT
- 하드웨어 결함으로 발생
◇ SIGFPE
- divide-by-0나 부동 소숫점 오버플로우와 같은 산술 연산 오류에서 발생
◇ SIGILL
◇ SIGINFO
◇ SIGIO
◇ SIGIOT
◇ SIGPIPE
- pipe 통신에서 수신 프로세스가 종료했을 때 송신 프로세스가 파이프에 write 하면 발생
- 프로세스가 RST를 받은 소켓에 데이터를 쓰면, 커널은 그 프로세스에 ISGPIPE 신호를 보낸다.
- 이 신호의 기본 동작은 프로세스를 종료시키는 것이므로, 프로세스가 원하지 않는 종료를 피하기 위해서는 이 신호를 포착해야 한다.
◇ SIGPOLL
◇ SIGROF
◇ SIGPWR
◇ SIGSYS
◇ SIGTTIN
- background에 있는 프로세스가 제어 터미널로부터의 읽기를 시도한다.
◇ SIGTTOU
- background에 있는 프로세스가 제어 터미널로부터의 쓰기를 시도한다.
◇ SIGURG
- SIGIO와 SIGURG 라는 두 개의 신호는 소켓이 F_SETOWN 명령으로 소유주에게 할당되었을 때만 소켓에 대해 발생한다.
◇ SIGUSR1
◇ SIGUSR2
◇ SIGVTALRM
◇ SIGWINCH
◇ SIGXCPU
◇ SIGXFSZ
■ -- 2009-11-29 00:26:00
■ linux 에서의 sigaction 정의 -- 2011-06-05 22:55:00
struct sigaction {
union {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask ;
unsigned long sa_flags;
void (*sa_restorer)(void);
};
#define sa_handler _u._sa_handler
#define sa_sigaction _u._sa_sigaction
■ solaris 에서의 sigaction 정의 -- 2011-06-05 22:56:00
struct sigaction {
int sa_flags;
union {
#ifdef __cplusplus
void (*_handler)(int);
#else
void (*_handler)();
#endif
#if defined(__EXTENSIONS__) || defined(_KERNEL) || \
(!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \
(_POSIX_C_SOURCE > 2) || defined(_XPG4_2)
void (*_sigaction)(int, siginfo_t *, void *);
#endif
} _funcptr;
sigset_t sa_mask;
#ifndef _LP64
int sa_resv[2];
#endif
};
#define sa_handler _funcptr._handler
#define sa_sigaction _funcptr._sigaction
■ freebsd 에서의 sigaction 정의 -- 2011-06-05 22:59:00
struct sigaction {
union { void (*__sa_handler)(int);
void (*__sa_sigaction)(int, struct __siginfo *, void *);
} __sigaction_u; // signal handler
int sa_flags; // see signal options below
sigset_t sa_mask; // signal mask to apply
};
#define sa_handler __sigaction_u.__sa_handler
/////////////////////////////////////////////////////////////////////////////*/
class ZCSigAction : public sigaction
{
public:
typedef struct sigaction StSigAction;
public:
ZCSigAction()
{
::memset((sigaction*)this, 0, sizeof(StSigAction));
}/*
ZCSigAction()*/
StSigAction& GetBaseObj()
{
return (StSigAction&)(*this);
}/*
StSigAction& GetBaseObj()*/
void SetFlags(int AI_Flags)
{
this->sa_flags=AI_Flags; // RTS 에는 SA_SIGINFO 를 셋팅
}/*
void SetFlags(int AI_Flags)*/
void SetHandler(void (*APF_Handler)(int))
{
this->sa_handler=APF_Handler;
}/*
void SetHandler(void (*APF_Handler)(int))*/
void SetHandlerEx(void (*APF_Handler)(int, siginfo_t*, void*))
{
this->sa_sigaction=APF_Handler; // 주로 RTS 에서 사용됨
}/*
void SetHandlerEx(void (*APF_Handler)(int, siginfo_t*, void*))*/
bool EmptySet()
{
return sigemptyset(&this->sa_mask)==0 ;
}/*
bool EmptySet()*/
bool FillSet()
{
return sigfillset(&this->sa_mask)==0 ;
}/*
bool FillSet()*/
bool CutSet(int AI_SigNo)
{
return sigdelset(&this->sa_mask, AI_SigNo)==0 ;
}/*
bool CutSet(int AI_SigNo)*/
bool AddSet(int AI_SigNo)
{
return sigaddset(&this->sa_mask, AI_SigNo)==0 ;
}/*
bool AddSet(int AI_SigNo)*/
bool Act(int AI_SigNum, struct sigaction* AP_OldSigAction=0)
{
return ::sigaction(AI_SigNum, this, AP_OldSigAction)==0 ;
}/*
bool Act(int AI_SigNum, struct sigaction* AP_OldSigAction=0)*/
static ZTypUInt Alarm(ZTypUInt AI_Sec)
{
return ::alarm(AI_Sec);
/* AI_Sec 0 은 나머지 알람 요청을 취소. 나머지 알람 호출
이 전달되기 전에 남아있는 시간을 반환하거나 호출이
실패하면 -1 을 반환한다. */
}/*
static ZTypUInt Alarm(ZTypUInt AI_Sec)*/
static bool Kill(int AI_SigNo, pid_t AI_Pid=::getpid())
{
return ::kill(AI_Pid, AI_SigNo)==0 ;
}/*
static bool Kill(int AI_SigNo, pid_t AI_Pid=::getpid())*/
#ifdef __linux__
/*/////////////////////////////////////////////////////////////////////////
■ freebsd 8.2, solaris 5.11 에서는 F_SETSIG 이 정의가 되어 있지 않아서,
아래 SetupRTS() 함수를 사용할 수 없다. 즉 RTS 와 소켓을 연동할 수 없다.
-- 2011-06-09 04:25:00
/////////////////////////////////////////////////////////////////////////*/
// 주로 SIGRTMIN ~ SIGRTMAX 사이에 있는 RTS 를 설정한다.
static bool SetupRTS(int AI_FileID, int AI_SigNo=SIGRTMIN, int AI_Flag=O_RDWR | /*O_NONBLOCK|*/O_ASYNC, pid_t AI_PId=::getpid())
{
if (::fcntl(AI_FileID, F_SETFL , AI_Flag ) < 0)
{
return false;
}
if (::fcntl(AI_FileID, F_SETSIG, AI_SigNo) < 0)
{
return false;
}
if (::fcntl(AI_FileID, F_SETOWN, AI_PId ) < 0)
{
return false;
}/*
if (::fcntl(AI_FileID, F_SETOWN, AI_PId ) < 0)*/
return true;
}/*
static bool SetupRTS(int AI_FileID, int AI_SigNo=SIGRTMIN, int AI_Flag=O_RDWR | O_NONBLOCK| O_ASYNC, int pid_t=::getpid())*/
#endif //__linux__
public:
};/*
class ZCSigAction*/
class ZCSigSet
{
private:
::sigset_t mi_SigSet;
public :
::sigset_t& GetSigSet()
{
return mi_SigSet;
}/*
::sigset_t& GetSigSet()*/
bool Empty()
{
return ::sigemptyset(&mi_SigSet)==0;
}/*
bool Empty()*/
bool Fill()
{
return ::sigfillset(&mi_SigSet)==0;
}/*
bool Fill()*/
bool Add(int AI_SigNo)
{
return ::sigaddset(&mi_SigSet, AI_SigNo)==0;
}/*
bool Add(int AI_SigNo)*/
bool Cut(int AI_SigNo)
{
return ::sigdelset(&mi_SigSet, AI_SigNo)==0;
}/*
bool Cut(int AI_SigNo)*/
bool IsMember(int AI_SigNo)
{
return ::sigismember(&mi_SigSet, AI_SigNo)==1;
}/*
bool IsMember(int AI_SigNo)*/
bool ProcMask(int AI_How, sigset_t* AP_OldSigSet=0)
{
return ::sigprocmask(AI_How, &mi_SigSet, AP_OldSigSet)==0;
}/*
bool ProcMask(int AI_How, sigset_t* AP_OldSigSet=0)*/
/*//////////////////////////////////////////////////////////////
■ int AI_How 가 가질 수 있는 값.
SIG_BLOCK
The set of blocked signals is the union of the current
set and the set argument.
지정된 시그널 마스크를 현재 시그널 블록 마스크에 추가한다.
블록된 시그널은 해당 시그널이 발생했을때, 지정된 시그널 핸
들러가 실행을 완료할 때까지 블럭된다.
블록되지 않은 시그널이라면, 해당 시그널이 발생했을때, 지정
된 시그널 핸들러를 수행하다가, 다른 시그널이 발생하면, 실행
을 중지하고, 그 시그널의 핸들러의 실행이 끝난 후에, 중지된
지점부터 재시작하게 된다.
SIG_UNBLOCK
The signals in set are removed from the current set of
blocked signals. It is legal to attempt to unblock a
signal which is not blocked.
지정된 시그널 마스크를 현재 시그널 블록 마스크에서 제거한다.
SIG_SETMASK
The set of blocked signals is set to the argument set.
-- 2011-06-09 21:37:00
//////////////////////////////////////////////////////////////*/
bool Wait(int& ARRI_SigNo)
{
return ::sigwait(&mi_SigSet, &ARRI_SigNo)==0;
}/*
bool Wait(int& ARRI_SigNo)*/
int WaitInfo(siginfo_t& ARR_StSigInfo)
{
return ::sigwaitinfo(&mi_SigSet, &ARR_StSigInfo);
}/*
int WaitInfo(siginfo_t& ARR_StSigInfo)*/
/*//////////////////////////////////////////////////////////////////
■ SYNOPSIS
#include <signal.h>
int sigwaitinfo(const sigset_t *restrict set,
siginfo_t *restrict info);
int sigtimedwait( const sigset_t *restrict set ,
siginfo_t *restrict info,
const struct timespec *restrict timeout);
■ RETURN VALUES
Upon successful completion (that is, one of the signals
specified by set is pending or is generated) sigwaitinfo()
and sigtimedwait() will return the selected signal number.
Otherwise, the function returns -1 and sets errno to indi-
cate the error.
■ ERRORS
The sigwaitinfo() and sigtimedwait() functions will fail if:
EINTR The wait was interrupted by an unblocked, caught
signal.
ENOSYS The sigwaitinfo() and sigtimedwait() functions are
not supported.
The sigtimedwait() function will fail if:
EAGAIN No signal specified by set was generated within
the specified timeout period.
The sigwaitinfo() and sigtimedwait() functions may fail if:
EFAULT The set, info, or timeout argument points to an
invalid address.
The sigtimedwait() function may fail if:
EINVAL The timeout argument specified a tv_nsec value
less than zero or greater than or equal to 1000
million. The system only checks for this error if
no signal is pending in set and it is necessary to
wait.
■ -- 2011-06-02 01:14:00
//////////////////////////////////////////////////////////////////*/
static bool AddInfo(int AI_SignalNo, const union sigval AI_SigVal, int AI_ProcessID=::getpid())
{
return ::sigqueue(AI_ProcessID, AI_SignalNo, AI_SigVal)==0;
}/*
static bool AddInfo(int AI_SignalNo, const union sigval AI_SigVal, int AI_ProcessID=::getpid())*/
/*//////////////////////////////////////////////////////////////////
■ SYNOPSIS
#include <sys/types.h>
#include <signal.h>
union sigval{int sival_int; void* sival_ptr;};
int sigqueue(pid_t pid, int signo, const union sigval value);
■ RETURN VALUES
Upon successful completion, the specified signal will have
been queued, and the sigqueue() function returns 0. Other-
wise, the function returns -1 and sets errno to indicate the
error.
■ ERRORS
The sigqueue() function will fail if:
EAGAIN No resources are available to queue the signal.
The process has already queued SIGQUEUE_MAX sig-
nals that are still pending at the receiver(s),
or a system wide resource limit has been
exceeded.
EINVAL The value of signo is an invalid or unsupported
signal number.
ENOSYS The sigqueue() function is not supported by the
system.
EPERM The process does not have the appropriate
privilege to send the signal to the receiving
process.
ESRCH The process pid does not exist.
■ -- 2011-06-02 01:40:00
//////////////////////////////////////////////////////////////////*/
public:
};/*
class ZCSigSet*/
class ZCProcess
{
public:
// cf) typedef int pid_t
// 데몬을 만드는 용도로도 가능한 함수이다.
bool Exec(const char* AP_ExeName, bool AB_DoCloseStdInOut=true)
{
return this->Exec(AP_ExeName, NULL, AB_DoCloseStdInOut);
}/*
bool Exec(const char* AP_ExeName, bool AB_DoCloseStdInOut=true)*/
bool Exec(const char* AP_ExeName, char* const APA_Arg[], bool AB_DoCloseStdInOut=true)
{
/*/////////////////////////////////////////////////////////////////
■ APA_Arg 의 마지막 원소는 NULL 로 끝나야 한다.
char *aRgu[4];
aRgu[0] = "/test/test.exe";
aRgu[1] = "ABC"; // Argu 1
aRgu[2] = "10" ; // Argu 2
aRgu[3] = 0 ; // 아규먼트가 끝이라는 것를 꼭 지정하여야 한다.
execvp(aRgu[0] , aRgu);
■ execl 사용례
::execl( "./WinSEC_D.exe" ,
"./WinSEC_D.exe" ,
"WinSEC_Conf.txt",
NULL
////// );
/////////////////////////////////////////////////////////////////*/
if(AP_ExeName==0 || AP_ExeName[0]==0) return false;
int VI_ForkNum = ::fork(); /*####################*/
if(VI_ForkNum==0) // child
{
if(AB_DoCloseStdInOut==true)
{
::close(0);
::close(1);
}/*
if(AB_DoCloseStdInOut==true)*/
::setsid(); // 자기 자신을 세션의 리더로 만든다.
::execvp(AP_ExeName, APA_Arg);
return true;
}
else if(VI_ForkNum==-1) // error
{
return false;
}
else // 부모의 경우
{
return true;
}/*
else*/
}/*
bool Exec(const char* AP_ExeName, char* const APA_Arg[], bool AB_DoCloseStdInOut=true)*/
static pid_t Fork (){return ::fork ();}
static int GetPPID(){return ::getppid();}
static int GetUID (){return ::getuid ();}
static int GetGID (){return ::getgid ();}
static long GetPID (){return ::getpid ();}
public:
};/*
class ZCProcess*/
class ZCMemMap
{
public:
static void* LinkMap(
int AI_FileDesc, off_t AL_Offset=0, size_t AL_MapSize=0, int AI_Protect=PROT_READ | PROT_WRITE, int AI_Flags=MAP_SHARED, void* AP_BaseAddress=NULL)
{
return ::mmap(AP_BaseAddress, AL_MapSize, AI_Protect, AI_Flags, AI_FileDesc, AL_Offset);
}/*
static void* LinkMap(
int AI_FileDesc, off_t AL_Offset=0, size_t AL_MapSize=0, int AI_Protect=PROT_READ | PROT_WRITE, int AI_Flags=MAP_SHARED, void* AP_BaseAddress=NULL) */
/* munmap(void*,size_t)
Upon successful completion, munmap() returns 0. Otherwise,
it returns -1 and sets errno to indicate the error */
static bool UnMap(void* AP_BaseAddress, size_t AL_Size)
{
return ::munmap(AP_BaseAddress, AL_Size)==0;
}/*
static bool UnMap(void* AP_BaseAddress, size_t AL_Size)*/
public:
};/*
class ZCMemMap*/
/*/////////////////////////////////////////////////////////////////////////////
SHMGET(2) 리눅스 프로그래머 메뉴얼 SHMGET(2)
■ 이름
shmget - 공유 메모리 세그먼트를 할당한다.
■ 사용법
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);
■ 설명
shmget() 는 key 인자값과 관련된 공유 메모리 세그먼트 식별자를 반환한다.
만일, key 가 IPC_PRIVATE 값을 가지고 있거나 또는 key 가 IPC_PRIVATE 가
아 니고, key 와 연계되어 있는 공유메모리 세그먼트가 없다면 PAGE_SIZE 의
배수만큼의 size 를 가지고 있는 새로운 공유 메모리 세그먼트가 만들 어 진
다. IPC_CREAT 는 shmflg 에 명시되어 있다. (i.e. shmflg&IPC_CREAT 는
0이 아니다.)
shmflg 의 구성은 다음과 같다:
IPC_CREAT 새로운 세그먼트를 만든다. 만일 이 플래그가 사용되지 않는 다
면, shmget() 는 key와 관련된 세그먼트를 찾을 것이며, 사용자
가 그 세그먼트와 관련된 shmid 를 받을 허가권이 있는지 알 기
위해서 검사한다. 그리고 세그먼트가 파괴되었다는 표시를 하지
않도록 보장한다.
IPC_EXCL 세그먼트가 존재할경우 실패를 보장하기 위해 IPC_CREAT와 함께
사용된다.
mode_flags (lowest 9 bits)
소 유 자, 그룹, 그 외들을 보장하기 위해 허가권을 지정한다.
현재, 실행 허가권은 시스템에 의해 사용되지 않는다.
새로운 세그먼트가 생성된다면, shmflg 의 접근 허가권은세그먼트에 정의 되
어 있는 shmid_ds 의 shm_perm 멤버로 복사된다. shmid_ds 구조체:
struct shmid_ds {
struct ipc_perm shm_perm; // 퍼미션
int shm_segsz; // 세그먼트의 크기(bytes)
time_t shm_atime; // 마지막 접근 시간
time_t shm_dtime; // 마지막 제거 시간
time_t shm_ctime; // 마지막 변경 시간
unsigned short shm_cpid; // 생성자의 pid
unsigned short shm_lpid; // 마지막으로 작동한 프로세스 pid
short shm_nattch; // 현재 접근한 프로세스의 수
};
struct ipc_perm
{
key_t key;
ushort uid; // 소유자의 euid 와 egid
ushort gid;
ushort cuid; // 생성자의 euid 와 egid
ushort cgid;
ushort mode; // shmflg의 하위 9비트
ushort seq; // 연속 수(sequence number)
};
게다가, 생성되는 동안 시스템 콜은 시스템 공유 메모리 세그먼트 데이터 구
조 shmid_ds 를 다음과 같이 초기화한다.
shm_perm.cuid 와 shm_perm.uid 는 호출 프로세스의 유효 user-ID 로
설정된다.
shm_perm.cgid 와 shm_perm.gid 는 호출 프로세스의 유효 group-ID로
설정된다.
shm_perm.mode 의 하위 9비트들은 shmflg 의 하위 9비트들로 설정 된
다.
shm_segsz 는 size 값으로 설정된다.
shm_lpid, shm_nattch, shm_atime 그리고 shm_dtime 는 0 으로 설정
된다.
shm_ctime 는 현재 시간으로 설정된다.
만일 공유 메모리 세그먼트가 이미 존재한다면, 접근 허가권이 조사되며, 파
괴도도록 표시되어 있는지 알아보기 위해 검사한다.
■ SYSTEM CALLS
fork() fork() 후에 자식 프로세스는 연결된 공유 메모리 세그먼트들을 상속
한다.
exec() exec() 후에 연결된 모든 공유 메모리 세그먼트는 분리된다.(파괴 되
는것이 아니다)
exit() exit() 시 연결된 모든 공유 메모리 세그먼트는 분리된다.(파괴되는
것이 아니다)
■ 반환값
성공시 유효한 세그먼트 식별자 shmid 가 반환되며, 에러시 -1이 반환된 다.
■ 에러
실패시, errno 는 다음중 하나로 설정된다:
EINVAL 만일 SHMMIN > size, 또는 size > SHMMAX, 또는 size이 세그먼
트의 크기보다 크다면 이에러가 반환된다.
EEXIST IPC_CREAT | IPC_EXCL 이 지정되어 있고,
세그먼트가 존재하 면 이 에러가 반환된다.
EIDRM 세그먼트가 파괴나 제거되도록 표시되어 있다면 이 에러가 반환된다.
ENOSPC 가능한 모든 공유 메모리 id (SHMMNI) 를 가졌거나 요청된 size 의
세그먼트 할당이 시스템 전체 공유 메모리 제한값 (SHMALL) 을 초과할경우 반환된다.
ENOENT 주어진 key에 해당하는 세그먼트가 존재하지 않고,
IPC_CREAT 가 지정되지 않았다면 반환된다.
EACCES 사용자가 공유 메모리 세그먼트에 대한 접근 허가권이 없을때 반환된다.
ENOMEM 세그먼트를 위해 할당할 메모리가 없을때 반환된다.
※ 주의
IPC_PRIVATE 는 플레그 필드가 아니라 key_t 타입이다. 이 특별한 값이 key에
사용된다면, 시스템 콜은 shmflg 의 하위 9비트들외 모든것을 무시한다.
그리고 새 공유 메모리 세그먼트를 생성(성공시)한다.
다음은 shmget 시스템 콜에 영향을 주는 공유 메모리 세그먼트 자원들의 제한값들이다:
SHMALL 시스템 전체의 공유 메모리 세그먼트 최대 값: 정책 의존적이다.
SHMMAX 공유 메모리 세그먼트의 최대 크기(바이트수): 수행 의존적이다.(현재 4M)
SHMMIN 공유 메모리 세그먼트의 최소 크기(바이트수): 수행 의존적이다.
(PAGE_SIZE 가 유효한 최소 크기이지만, 현재는 1byte이다.)
SHMMNI 시스템 전체의 공유 메모리 세그먼트 최대 수: 수행 의존적이다(현재 4096)
수행시 프로세스당 공유 메모리 세그먼트의 특별한 제한은 없다. (SHMSEG)
/////////////////////////////////////////////////////////////////////////////*/
// 공유메모리 클래스
class ZCShareMemory
{
private:
enum{EInvalidID=-1};
private:
ZTypeID mh_FileMap;
void* mp_Address;
public :
ZCShareMemory()
{
mh_FileMap= EInvalidID;
mp_Address= 0 ;
}/*
ZCShareMemory()*/
ZTypeID GetID() const
{
return mh_FileMap;
}/*
ZTypeID GetID() const*/
bool IsValidID() const
{
return mh_FileMap != -1;
}/*
bool IsValidID() const*/
void* GetStartAddress() const
{
return mp_Address;
}/*
void* GetStartAddress() const*/
bool Create(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return Create((key_t)(ZNsMain::ZfGetKeyNum(AP_MapName)), AL_MapSize, AI_Flag);
}/*
bool Create(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool Create(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
if((mh_FileMap=::shmget((key_t)AL_MapID, AL_MapSize, AI_Flag))==-1)
{
if(errno==EEXIST)
{
return (mh_FileMap=::shmget(AL_MapID, 0, 0)) != -1;
}/*
if(errno==EEXIST)*/
return false;
}/*
if((mh_FileMap=::shmget((key_t)AL_MapID, AL_MapSize, AI_Flag))==-1)*/
return true;
}/*
bool Create(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool ShmGet(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return (mh_FileMap=::shmget((key_t)(ZNsMain::ZfGetKeyNum(AP_MapName)), AL_MapSize, AI_Flag))!=-1 ;
}/*
bool ShmGet(const char* AP_MapName, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool ShmGet(const char* AP_MapName)
{
return (mh_FileMap=::shmget((key_t)(ZNsMain::ZfGetKeyNum(AP_MapName)), 0 ,0))!=-1 ;
}/*
bool ShmGet(const char* AP_MapName)*/
bool ShmGet(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return (mh_FileMap=::shmget((key_t)AL_MapID, AL_MapSize, AI_Flag))!=-1 ;
}/*
bool ShmGet(long AL_MapID, long AL_MapSize, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool ShmGet(long AL_MapID)
{
// key 가 AL_MapID 인 공유메모리가 있는지 조사
return (mh_FileMap=::shmget((key_t)AL_MapID, 0, 0))!=-1 ;
}/*
bool ShmGet(long AL_MapID)*/
bool LinkMap()
{
// void *shmat(int shmid, const void *shmaddr, int shmflg);
return (mp_Address=::shmat(mh_FileMap, (void*)0, 0))!=NULL ;
// shmat() 의 3 번째 인수로 SHM_RDONLY 플래그를 지정하면
// 공유 메모리 세그먼트는 매핑되어지지만, 읽기전용(readonly)로 표시된다
}/*
bool LinkMap()*/
bool UnMap()
{
// cf) In Window : BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
bool VB_IsOK=(::shmdt(mp_Address)!=-1); mp_Address=0; return VB_IsOK;
}/*
bool UnMap()*/
bool Close()
{
bool VB_IsOK =
( ::shmctl(mh_FileMap, IPC_RMID, 0) != -1 ) ;
mh_FileMap=EInvalidID; return VB_IsOK;
/* IPC_RMID 명령어는 실제로 커널로 부터 세그먼트를 제거하는 것은 아니다.
제거하기 위해 세그먼트에 표시를 해둔다. 실제적인 제거 자체는 현재 세그먼트에
붙어 있는(attached) 마지막 프로세스가 적절히 분리됐을(detached) 때 일어난다.
물론, 현재 세그먼트에 붙어있는 (attached) 프로세스가 없으면, 제거는 즉시
이루어 진다. */
}/*
bool Close()*/
public:
};/*
class ZCShareMemory*/
/*////////////////////////////////////////////////////////////////////////////////////////
■ class ZCProcessMutex 는 프로세스의 뮤텍스.
Window 에서는 이름있는 mutex 를 쓰면 간단하지만, Linux 에서는 세마포어를 쓸 수도 있다.
pthread 라이브러리를 써서 공유메모리에 mutex 데이타를 넣어도 되지만 그만큼 잔손질이
필요하다.
Linux 에서는 다중쓰레드에서 쓰는 세마포어와 다중 프로세스에서 쓰는 세마포어가 다르다.
정확히는 pthread 가 프로세스 세마포어를 아직 지원하지 않는다. (2006-12-28 16:02:00)
////////////////////////////////////////////////////////////////////////////////////////*/
class ZCProcessMutex
{
private:
ZTypeID mh_Mutex;
private:
bool Init(int AI_InitNum=1)
{
// 리눅스에서만 가능
// 세마포어의 값을 초기화한다.
union semun SemUnion; SemUnion.val=AI_InitNum;
return ::semctl(mh_Mutex, 0, SETVAL, SemUnion)!=-1 ;
/*//////////////////////////////////////////////////////////
int semctl(int semid, int semnum, int cmd, ...)
If successful, the value returned by semctl() depends on cmd as follows:
GETVAL
The value of semval.
GETPID
The value of sempid.
GETNCNT
The value of semncnt.
GETZCNT
The value of semzcnt.
All others
0.
Otherwise, semctl() returns -1 and errno indicates the error
//////////////////////////////////////////////////////////*/
}/*
bool Init(int AI_InitNum=1)*/
/*private:*/
public :
ZCProcessMutex()
{
mh_Mutex=0;
}/*
ZCProcessMutex()*/
ZTypeID GetHandle() const
{
return mh_Mutex;
}/*
ZTypeID GetHandle() const*/
// Linux 의 경우 프로세스 세마포어로 Window 의 프로세스 뮤텍스를 구현한다.
// Window 의 뮤텍스는 쓰레드와 프로세스에 모두 사용할 수 있지만 Linux 의
// 경우에는 그렇지 않다. 2006-08-06 15:55:00
bool Make(const char* AP_MutexName, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return this->Make(ZNsMain::ZfGetKeyNum(AP_MutexName), AI_Flag);
}/*
bool Make(const char* AP_MutexName, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool Make(long AL_KeyID, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
// AI_Flag 는 IPC_CREAT,IPC_EXCL,0 의 세 값중의 하나이며
// 0 이면 해당 동기화object 가 존재할 때만 연다.
// int semget(key_t key,int nsems,int semflg)
if((mh_Mutex=::semget((key_t)AL_KeyID, 1, AI_Flag))==-1)
{
if(errno==EEXIST)
{
return (mh_Mutex=::semget((key_t)AL_KeyID, 1, 0)) != -1;
}/*
if(errno==EEXIST)*/
return false;
}/*
if((mh_Mutex=::semget((key_t)AL_KeyID, 1, AI_Flag))==-1)*/
this->Init(); return true;
}/*
bool Make(long AL_KeyID, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool Lock()
{
struct sembuf sembuf_Obj ;
sembuf_Obj.sem_num = 0 ; // semaphore 배열에서 몇 번째 semaphore 인지를 지정
sembuf_Obj.sem_op = -1 ;
sembuf_Obj.sem_flg = SEM_UNDO ;
return ::semop(mh_Mutex, &sembuf_Obj, 1)==0 ;
}/*
bool Lock()*/
bool UnLock()
{
struct sembuf sembuf_Obj ;
sembuf_Obj.sem_num = 0 ; // semaphore 배열에서 몇 번째 semaphore 인지를 지정
sembuf_Obj.sem_op = 1 ;
sembuf_Obj.sem_flg = SEM_UNDO ;
return ::semop(mh_Mutex, &sembuf_Obj, 1)==0 ;
}/*
bool UnLock()*/
bool Close()
{
// 커널에서 semaphore 가 제거된다. 이 함수 수행후에 ipcs 해 보면
// semaphore 목록에 나타나지 않는다. 따라서 이 함 수행 후에는 Lock()
// 에서 대기하고 있던 프로세스나 쓰레드는 한꺼번에 깨어나고 더이상
// 동기화되지 않는다. 프로세스가 이 함수를 수행하지 않고 종료해도 커
// 널은 자동적으로 semaphore 를 삭제하지 않는다.
union semun SemUnion ;
const bool CB_IsOK =
(::semctl(mh_Mutex, 0, IPC_RMID, SemUnion) != -1);
mh_Mutex=0; return CB_IsOK;
}/*
bool Close()*/
public:
};/*
class ZCProcessMutex*/
///////////////////////////////////////////////
/////////// end class ZCProcessMutex ///////////
///////////////////////////////////////////////
// posix mutex 로 프로세스간 동기화를 하는 클래스
class ZCProcessMutexPosix
{
private:
class ZCMutexData
{
protected:
friend class ZCProcessMutexPosix;
protected:
pthread_mutex_t mo_PThreadMutex ;
pthread_mutexattr_t mo_PThreadMutexAttr;
protected:
};/*
class ZCMutexData*/
private:
ZCMutexData* mp_CMutexData ;
ZCShareMemory mo_CShareMemory;
public :
ZCProcessMutexPosix()
{
mp_CMutexData=0;
}/*
ZCProcessMutexPosix()*/
bool Make(LPCTSTR AP_MapName)
{
return Make( ZNsMain::ZfGetKeyNum(AP_MapName) );
}/*
bool Make(LPCTSTR AP_MapName)*/
bool Make(long AL_MapKey)
{
if(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCMutexData))==false)
{
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MapKey)!=true)
{
return false;
}
if(mo_CShareMemory.LinkMap()==false)
{
mo_CShareMemory.Close(); return false;
}/*
if(mo_CShareMemory.LinkMap()==false)*/
mp_CMutexData=(ZCMutexData*)mo_CShareMemory.GetStartAddress();
}
else // mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCMutexData))==true
{
if(mo_CShareMemory.LinkMap()==false)
{
mo_CShareMemory.Close(); return false;
}/*
if(mo_CShareMemory.LinkMap()==false)*/
mp_CMutexData = (ZCMutexData*)mo_CShareMemory.GetStartAddress();
::memset(mp_CMutexData, 0x00, sizeof(ZCMutexData));
::pthread_mutexattr_init (&mp_CMutexData->mo_PThreadMutexAttr);
::pthread_mutexattr_settype (&mp_CMutexData->mo_PThreadMutexAttr, PTHREAD_MUTEX_RECURSIVE); // 이전에는 PTHREAD_MUTEX_ERRORCHECK 로 설정했었다. 2007-05-24 20:04:00
::pthread_mutexattr_setpshared(&mp_CMutexData->mo_PThreadMutexAttr, PTHREAD_PROCESS_SHARED );
::pthread_mutex_init (&mp_CMutexData->mo_PThreadMutex, (const pthread_mutexattr_t*)&mp_CMutexData->mo_PThreadMutexAttr);
}/*
else // mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCMutexData))==true*/
return true;
}/*
bool Make(long AL_MapKey)*/
bool Lock()
{
return this->LockRaw()==0;
}/*
int Lock()*/
int LockRaw()
{
return ::pthread_mutex_lock(&mp_CMutexData->mo_PThreadMutex);
// EDEADLK 를 반환하는 경우를 체크할 것.
}/*
int Lock()*/
#ifdef __USE_XOPEN2K
bool Lock(const struct timespec& AR_TimeSpec)
{
return this->LockRaw(AR_TimeSpec)==0;
}/*
int Lock(const struct timespec& AR_TimeSpec)*/
bool Lock(long AL_MiliSec)
{
return this->LockRaw(AL_MiliSec)==0;
}/*
int Lock(long AL_MiliSec)*/
int LockRaw(const struct timespec& AR_TimeSpec)
{
return ::pthread_mutex_timedlock(&mp_CMutexData->mo_PThreadMutex, &AR_TimeSpec);
// 성공이면 ZNsEnum::ZEThread_OK 를
// 시간초과이면 ZNsEnum::ZEThread_TimeOut 를 리턴
//
// EDEADLK 를 반환하는 경우를 체크할 것.
}/*
int Lock(const struct timespec& AR_TimeSpec)*/
int LockRaw(long AL_MiliSec)
{
timespec VO_TimeSpec;
VO_TimeSpec.tv_sec = time(NULL) + AL_MiliSec/1000 ;
VO_TimeSpec.tv_nsec = (AL_MiliSec%1000)* 1000*1000 ;
return ::pthread_mutex_timedlock
(&mp_CMutexData->mo_PThreadMutex, &VO_TimeSpec);
}/*
int Lock(long AL_MiliSec)*/
int LockTime(long AL_MiliSec)
{
return LockRaw(AL_MiliSec);
}/*
int LockTime(long AL_MiliSec)*/
#endif // __USE_XOPEN2K
bool UnLock()
{
return ::pthread_mutex_unlock(&mp_CMutexData->mo_PThreadMutex)==0;
}/*
bool UnLock()*/
void Close()
{
::pthread_mutex_destroy(&mp_CMutexData->mo_PThreadMutex);
mo_CShareMemory.UnMap();
mo_CShareMemory.Close();
}/*
void Close()*/
public:
};/*
class ZCProcessMutexPosix*/
#ifdef __USE_XOPEN2K
/*////////////////////////////////////////////////////////////////////////////////
■ 기록자/판독자 lock(read-write lock) 으로 Window 의 수동리셋 이벤트를 구현할 수
있다. 즉 특정 지점에서 다수의 쓰레드가 대기하다가 동시에 깨어나는 동작이 가능하
다. 물론 뮤텍스 조건 변수로 수동리셋 이벤트를 구현하는 것이 더 효과적이다.
■ POSIX.1b structure for a time value.
This is like a `struct timeval' but has nanoseconds instead of microseconds.
struct timespec
{
__time_t tv_sec ; // Seconds.
long int tv_nsec; // Nanoseconds. 10 억분의 1 초, 1 밀리초=1000*1000 나노초
};
////////////////////////////////////////////////////////////////////////////////*/
class ZCLockRW_Base
{
protected:
pthread_rwlock_t mo_pthread_rwlock_t;
public:
int Init()
{
return ::pthread_rwlock_init(&mo_pthread_rwlock_t, NULL);
}/*
int Init()*/
int LockRead()
{
return ::pthread_rwlock_rdlock(&mo_pthread_rwlock_t);
}/*
int LockRead()*/
int LockRead(const struct timespec& AR_TimeSpec)
{
return ::pthread_rwlock_timedrdlock(&mo_pthread_rwlock_t, &AR_TimeSpec);
}/*
int LockRead(const struct timespec& AR_TimeSpec)*/
int LockRead(long AL_MiliSec)
{
timespec VO_TimeSpec;
VO_TimeSpec.tv_sec =time(NULL)+AL_MiliSec/1000;
VO_TimeSpec.tv_nsec=(AL_MiliSec%1000)*1000*1000;
return ::pthread_rwlock_timedrdlock(&mo_pthread_rwlock_t, &VO_TimeSpec);
}/*
int LockRead(long AL_MiliSec)*/
int TryLockRead()
{
return ::pthread_rwlock_tryrdlock(&mo_pthread_rwlock_t);
}/*
int TryLockRead()*/
int LockWrite()
{
return ::pthread_rwlock_wrlock(&mo_pthread_rwlock_t);
}/*
int LockRead()*/
int LockWrite(const struct timespec& AR_TimeSpec)
{
return ::pthread_rwlock_timedwrlock(&mo_pthread_rwlock_t,&AR_TimeSpec);
}/*
int LockWrite(const struct timespec& AR_TimeSpec)*/
int LockWrite(long AL_MiliSec)
{
timespec VO_TimeSpec;
VO_TimeSpec.tv_sec =time(NULL)+AL_MiliSec/1000;
VO_TimeSpec.tv_nsec=(AL_MiliSec%1000)*1000*1000;
return ::pthread_rwlock_timedwrlock(&mo_pthread_rwlock_t, &VO_TimeSpec);
}/*
int LockWrite(long AL_MiliSec)*/
int TryLockWrite()
{
return ::pthread_rwlock_trywrlock(&mo_pthread_rwlock_t);
}/*
int TryLockWrite()*/
int UnLock()
{
return ::pthread_rwlock_unlock(&mo_pthread_rwlock_t);
}/*
int UnLock()*/
int Close()
{
return ::pthread_rwlock_destroy(&mo_pthread_rwlock_t);
}/*
int Close()*/
public:
};/*
class ZCLockRW_Base*/
class ZCLockRW : public ZCLockRW_Base
{
protected:
pthread_rwlockattr_t mo_pthread_rwlockattr_t;
public :
int InitAttr()
{
return ::pthread_rwlockattr_init(&mo_pthread_rwlockattr_t);
}/*
int InitAttr()*/
int Init()
{
return ::pthread_rwlock_init(&mo_pthread_rwlock_t, &mo_pthread_rwlockattr_t);
}/*
int Init()*/
bool SetShared(int AI_PShared=PTHREAD_PROCESS_PRIVATE)
{
return ::pthread_rwlockattr_setpshared(&mo_pthread_rwlockattr_t, AI_PShared)==ZNsEnum::ZEThread_OK;
}/*
bool SetShared(int AI_PShared=PTHREAD_PROCESS_PRIVATE)*/
bool GetShared(int& ARRI_PShared)
{
return ::pthread_rwlockattr_getpshared(&mo_pthread_rwlockattr_t, &ARRI_PShared)==ZNsEnum::ZEThread_OK;
}/*
bool GetShared(int& ARRI_PShared)*/
// 판독자,기록자 lock 의 우선순위 관련 함수, 현재 Posix 표준이 아니다.
// 이 함수로 우선순위를 설정하지 않았다면 기본적으로 기록자 lock 에
// 우선순위를 두는 것으로 설정되있는 베포판이 많다. -- 2006-12-30 16:21:00
int GetKind(int& ARRI_Kind) const
{
return ::pthread_rwlockattr_getkind_np(&mo_pthread_rwlockattr_t, &ARRI_Kind);
}/*
int GetKind(int& ARRI_Kind) const*/
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)
{
return ::pthread_rwlockattr_setkind_np(&mo_pthread_rwlockattr_t, AI_Kind);
}/*
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)*/
int CloseAttr()
{
return ::pthread_rwlockattr_destroy(&mo_pthread_rwlockattr_t);
}/*
int CloseAttr()*/
int Close()
{
return (ZCLockRW_Base::Close(), CloseAttr());
}/*
int Close()*/
public:
};/*
class ZCLockRW*/
class ZCLockRW_Process
{
protected:
ZCLockRW* mp_CLockRW ;
ZCShareMemory mo_CShareMemory;
public :
ZCLockRW_Process()
{
mp_CLockRW=0;
}/*
ZCLockRW_Process()*/
bool Make(long AL_MapKey)
{
if(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCLockRW))==false)
{
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MapKey)!=true)
{
return false;
}
if(mo_CShareMemory.LinkMap()==false)
{
mo_CShareMemory.Close(); return false;
}/*
if(mo_CShareMemory.LinkMap()==false)*/
mp_CLockRW=(ZCLockRW*)mo_CShareMemory.GetStartAddress();
}
else //!(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCLockRW))==false)
{
if(mo_CShareMemory.LinkMap()==false)
{
mo_CShareMemory.Close(); return false;
}/*
if(mo_CShareMemory.LinkMap()==false)*/
mp_CLockRW=(ZCLockRW*)mo_CShareMemory.GetStartAddress();
::memset(mp_CLockRW,0x00,sizeof(ZCLockRW));
mp_CLockRW->InitAttr();
mp_CLockRW->SetShared(PTHREAD_PROCESS_SHARED);
mp_CLockRW->Init();
}/*
else //!(mo_CShareMemory.ShmGet(AL_MapKey,sizeof(ZCLockRW))==false)*/
return true;
}/*
bool Make(long AL_MapKey)*/
bool Make(const char* AP_KeyName)
{
int Index =-1 ;
long VL_KeyID = 10 ;
if(AP_KeyName!=0)
{
while(AP_KeyName[++Index]!=0)
{
VL_KeyID += AP_KeyName[Index]*(Index+1) ;
}/*
while(AP_KeyName[++Index]!=0)*/
}/*
if(AP_KeyName!=0)*/
return this->Make(VL_KeyID);
}/*
bool Make(const char* AP_KeyName)*/
int LockRead()
{
return mp_CLockRW->LockRead();
}/*
int LockRead()*/
int LockRead(const struct timespec& AR_TimeSpec)
{
return mp_CLockRW->LockRead(AR_TimeSpec);
}/*
int LockRead(const struct timespec& AR_TimeSpec)*/
int TryLockRead()
{
return mp_CLockRW->TryLockRead();
}/*
int TryLockRead()*/
int LockWrite()
{
return mp_CLockRW->LockWrite();
}/*
int LockRead()*/
int LockWrite(const struct timespec& AR_TimeSpec)
{
return mp_CLockRW->LockWrite(AR_TimeSpec);
}/*
int LockWrite(const struct timespec& AR_TimeSpec)*/
int TryLockWrite()
{
return mp_CLockRW->TryLockWrite();
}/*
int TryLockWrite()*/
int UnLock()
{
return mp_CLockRW->UnLock();
}/*
int UnLock()*/
int GetKind(int& ARRI_Kind) const
{
return mp_CLockRW->GetKind(RR(ARRI_Kind));
}/*
int GetKind(int& ARRI_Kind) const*/
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)
{
return mp_CLockRW->SetKind(AI_Kind);
}/*
int SetKind(int AI_Kind=PTHREAD_RWLOCK_PREFER_READER_NP)*/
int Close()
{
int VI_Resutl=mp_CLockRW->Close();
mo_CShareMemory.UnMap();
mo_CShareMemory.Close();
return VI_Resutl;
}/*
int Close()*/
public:
};/*
class ZCLockRW_Process*/
#endif //__USE_XOPEN2K
/*//////////////////////////////////////////////////////////////////////////////////
■ class ZCProcessSemaphore 는 프로세스만을 위한 동기화 object. 리눅스에서는 프로세스
동기화에 Semaphore 만 쓸 수 있고, Window 에서는 프로세스 동기화에 Mutex 도 쓸 수
있다. 리눅스에서는 프로세스 동기화에 쓰이는 Semaphore 와 스레드 동기화에 쓰이는
세마포어가 함수와 용법이 다르다.
■ 시스템 호출 : semget() (SYSTEM CALL:semget())
새로운 세마퍼 집합을 만들거나 존재하는 집합에 접근하기 위하여, semget() 시스템 호출
을 사용한다.
SYSTEM CALL: semget();
PROTOTYPE: int semget ( key_t key, int nsems, int semflg );
RETURNS: semaphore set IPC identifier on success
-1 on error: errno = EACCESS (permission denied)
EEXIST (set exists, cannot create (IPC_EXCL))
EIDRM (set is marked for deletion)
ENOENT (set does not exist, no IPC_CREAT was used)
ENOMEM (Not enough memory to create new set)
ENOSPC (Maximum set limit exceeded)
□ NOTES:
semget()의 첫번째 아규먼트는 ftok()의 호출에 의해 반환된 키값이다.
이 키값은 커널안에 존재하는 다른 세마퍼 집합의 키값과 비교된다. 이때 열거나 접근하는 동작은 semflg 아규먼트의 내용에 따라 결정된다.
IPC_CREAT
커널안에 이미 존재하지 않는다면 세마퍼 집합을 만든다.
IPC_EXCL
IPC_CREAT와 같이 사용되어, 세마퍼가 이미 존재하면 실패한다.
IPC_CREAT가 혼자 사용된다면, semget()은 새로 만들어진 집합의 세마퍼 집합 확인자
(the semaphore set identifier)를 반환하거나 같은 키값을 가지고 이미 존재하는
집합의 확인자를 반환한다. IPC_EXCL가 IPC_CREAT와 함께 쓰였다면,
새 집합을 만들거나 이미 집합이 존재한다면 -1 값을 가지고 호출에 실패한다.
IPC_EXCL은 그 자체로는 의미가 없지만, IPC_CREAT와 조합되어 쓰여질 때
존재하지않는 세마퍼를 접근(access)하기 위해 열려지는 것을 막는 장치로 사용될 수 있다.
시스템 V IPC의 다른 형태들처럼 부가적인 8진 모드가 세마퍼 집합의 허가사항 형태에 마스크로 OR될 수 있다.
nsems 아규먼트는 새 집합안에서 만들어져야 하는 세마퍼의 갯수를 지정한다.
이것은 앞에서 설명했던 가상의 인쇄방의 프린터의 갯수를 의미한다.
집합안의 최대 세마퍼의 갯수는 linux/sem.h에 정의 되어있다.
#define SEMMSL 32 // <=512 id당 세마퍼의 최대 갯수
이미 존재하는 집합을 명확히 열고 있다면, nsems 아규먼트는 무시됨에 주목해라.
세마퍼 집합을 만들고 여는 wrapper 함수를 만들어 보자:
int open_semaphore_set( key_t keyval, int numsems )
{
int sid;
if ( ! numsems )
return(-1);
if((sid = semget( mykey, numsems, IPC_CREAT | 0660 )) == -1)
return(-1);
//endif
return(sid);
}
//int open_semaphore_set( key_t keyval, int numsems )
0600의 명시적인 허가사항을 사용함에 주의해라.
이 작은 함수는 세마퍼 집합 확인자(int)를 반환하거나 에러시 -1을 반환한다.
새 집합이 만들어졌다면, 공간을 할당하기 위해 세마퍼의 갯수와 마찬가지로 키값이 넘겨져야 한다.
이장의 끝에서 표현될 예제에서, 세마퍼 집합이 존재하는지 존재하지 않는지를
결정하기 위한 IPC_EXCL 플래그의 사용에 주목해라
□ -- NOTES --
■ int semctl ( int semid, int semnum, int cmd, union semun arg );
-1 on error: errno = EACCESS (permission denied)
EFAULT(invalid address pointed to by arg argument)
EIDRM (semaphore set was removed)
EINVAL(set doesn't exist, or semid is invalid)
EPERM (EUID has no privileges for cmd in arg)
ERANGE(semaphore value out of range)
□ semctl() 함수의 3 번째 인수가 가질 수 있는 여러 값
IPC_STAT
집합에 대한 semid_ds 구조를 조회하고,
semun union안의 buf 아규먼트의 주소지에 저장한다.
IPC_SET
집합에 대한 semid_ds 구조의 ipc_perm 멤버의 값을 지정한다.
semum union의 buf 아규먼트로 부터 값을 가져온다.
IPC_RMID
커널로 부터 집합을 제거한다.
GETALL
집합으로 부터 모든 세마퍼의 값을 얻는데 사용된다.
정수값들이 union의 배열 멤버에 의해 지정된 unsigned short integer 배열에 저장된다.
GETCNT
자원에 대해 현재 기다리고 있는 프로세스의 수를 반환한다.
GETPID
마지막 semop 호출을 수행한 프로세스의 PID를 반환한다.
GETZCNT
100% 자원 활용을 위해 현재 기다리고 있는 프로세스의 수를 반환한다.
SETALL
집합안의 모든 세마퍼의 값을 union의 배열 멤버안에 포함된 매칭되는 값으로 지정한다.
SETVAL
집합안의 개별적인 세마퍼의 값을 union의 val 멤버의 값으로 지정한다.
■ arg 아규먼트는 semun 타입의 예를 나타낸다.
이 특별한 연합체(union)는 linux/sem.h에 다음과 같이 선언되어있다.
union semun
{
int val; // SETVAL을 위한 값
struct semid_ds *buf; // IPC_STAT & IPC_SET을 위한 버퍼
ushort *array; // GETALL & SETALL를 위한 배열
struct seminfo *__buf; // IPC_INFO를 위한 버퍼
void *__pad;
};
val
SETVAL 명령어가 수행될 때 사용된다.세마퍼에 지정될 값을 지정한다.
buf
IPC_STAT/IPC_SET에서 사용된다.
커널안에서 사용되는 내부 세마퍼 자료 구조의 복사본을 나타낸다.
array
GETALL/SETALL 명령어에서 사용되는 포인터.
집합안에서 모든 세마퍼 값들을 조회하거나 지정하는데 사용되는
정수값들의 배열을 가리키고 있어야 한다.
남아있는 아규먼트인 _buf와 _pad는 커널 안의 세마퍼 코드내에서
내부적으로 사용되며 응용프로그램 개발자에게는 거의 쓸모가 없다.
사실상, 이런 두개의 아규먼트는 리눅스 운영체제에서 지정되며
다른 유닉스 구현에서는 찾을 수 없다.
이런 특별한 시스템 호출을 모든 시스템 V IPC 호출을 이해하는데
가장 어려운 점으로 꼽을 수 있으므로,실제로 이러한 다양한 예를 검사할 것이다.
마지막 아규먼트(union)은 GETVAL 명령어가 사용될 때 무시된다.
■ Posix 형 Semaphore 는 unistd.h 헤더 파일에 _POSIX_SEMAPHORES 옵션이
정의되었으면 사용할 수 있다.
-- POSIX(포직스) 쓰레드를 이용한 프로그래밍 (2011-05-23 20:31:00)
//////////////////////////////////////////////////////////////////////////////////*/
class ZCProcessSemaphore
{
private:
ZTypeID mh_Semaphore;
public :
ZCProcessSemaphore()
{
mh_Semaphore=0;
}/*
ZCProcessSemaphore()*/
// Linux 에서는 세마포어를 배열로 선언할 수 있으나
// 거의 쓸 일이 없고 원소 하나의 배열로 사용한다.
bool Make(const char* AP_SemaName, long AL_InitialCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return this->Make(ZNsMain::ZfGetKeyNum(AP_SemaName), AL_InitialCnt, AI_Flag);
}/*
bool Make(const char* AP_SemaName, long AL_InitialCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool Make(long AL_KeyID,int AL_InitialCnt,int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
if((mh_Semaphore=semget((key_t)AL_KeyID,1,AI_Flag))==-1)
{
if(errno==EEXIST)
return (mh_Semaphore=semget((key_t)AL_KeyID,1,0))!=-1 ;
return false;
}/*
if((mh_Semaphore=semget((key_t)AL_KeyID,1,AI_Flag))==-1)*/
union semun SemUnion; SemUnion.val=AL_InitialCnt;
return semctl(mh_Semaphore, 0, SETVAL,SemUnion) != -1 ;
}/*
bool Make(long AL_KeyID,int AL_InitialCnt,int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
// 아래 함수는 뮤텍스처럼 사용하는데 좋다.
bool Make(int AL_InitialCnt=1, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return Make(this->GetUniqueSemaKey(), AL_InitialCnt, AI_Flag);
}/*
bool Make(int AL_InitialCnt=1, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
// 비신호상태의 세마포어를 만든다.
bool MakeZero(int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return Make(this->GetUniqueSemaKey(), 0, AI_Flag);
}/*
bool MakeZero(int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
static long GetUniqueSemaKey()
{
static long SL_UniqueID=0 ;
const long CL_AddID =100000;
return ( SL_UniqueID += CL_AddID ) + ::getpid() ;
}/*
static long GetUniqueSemaKey()*/
bool MakeArr(long AL_KeyID, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
if((mh_Semaphore=semget((key_t)AL_KeyID, AI_SemaCnt, AI_Flag))==-1)
{
if(errno==EEXIST)
return (mh_Semaphore=semget((key_t)AL_KeyID, AI_SemaCnt,0))!=-1 ;
return false;
}/*
if((mh_Semaphore=semget((key_t)AL_KeyID, AI_SemaCnt, AI_Flag))==-1)*/
union semun SemUnion; SemUnion.val=AL_InitialCnt;
// 초기화는 SETALL 옵션을 사용해도 되는데 SemUnion.array 에 배열을 할당하고
// 초기화하고 대입하는 과정을 생각하면 그냥 for 문으로도 상관없을 것 같다.
__for0(ZTypInt, i, AI_SemaCnt)
{
if(semctl(mh_Semaphore, i, SETVAL,SemUnion) != -1 ) return false;
}/*
__for0(ZTypInt, i, AI_SemaCnt)*/
return true;
}/*
bool MakeArr(long AL_KeyID, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool MakeArr(const char* AP_SemaName, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return MakeArr(ZNsMain::ZfGetKeyNum(AP_SemaName), AL_InitialCnt, AI_SemaCnt, AI_Flag);
}/*
bool MakeArr(const char* AP_SemaName, int AL_InitialCnt, int AI_SemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
// bool MakeStd() 는 class ZCProcessSemaphore 가 Window, Linux 양쪽에서 호환하기 위한 것,
// 인수 AI_MaxSemaCnt 는 사용하지 않고 있다.(Window 에서만 의미가 있다.)
bool MakeStd(const char* AP_SemaName, int AL_InitialCnt, int AI_MaxSemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)
{
return this->Make(ZNsMain::ZfGetKeyNum(AP_SemaName), AL_InitialCnt, AI_Flag);
}/*
bool MakeStd(const char* AP_SemaName, int AL_InitialCnt, int AI_MaxSemaCnt, int AI_Flag=IPC_CREAT | IPC_EXCL | 0664)*/
bool Open(const char* AP_SemaName)
{
return (mh_Semaphore=semget((key_t)(ZNsMain::ZfGetKeyNum(AP_SemaName)), 1, 0))>0 ;
}/*
bool Open(const char* AP_SemaName)*/
// 현재 세마포어의 값을 가져온다.
int GetValue(int AI_SemaNum=0)
{
return semctl(mh_Semaphore, AI_SemaNum, GETVAL, 0/*union semun arg*/) ;
}/*
int GetValue(int AI_SemaNum=0)*/
bool GetValue(union semun& ARR_SemUnion)
{
return semctl(mh_Semaphore, 0/*ignored*/, GETALL, ARR_SemUnion) !=-1 ;
}/*
bool GetValue(union semun& ARR_SemUnion)*/
bool IsZeroCount()
{
return this->GetValue()==0;
}/*
bool IsZeroCount()*/
bool Lock(ZTypUInt AI_ReleaseCount=1)
{
return LockRaw(AI_ReleaseCount)!=-1;
}/*
bool Lock(ZTypUInt AI_ReleaseCount=1)*/
// LockRaw() 는 성공이면 ZEThread_OK, 시간 초과는 ZEThread_TimeOut 을 반환한다.
// 리눅스의 Posix 세마포어에서도 같다.
int LockRaw(ZTypUInt AI_ReleaseCount=1)
{
// cf) Window 의 ReleaseSemaphore(mh_Semaphore,lReleaseCount,lpPreviousCount) 에서
// 두번째 인수의 역할을 AI_ReleaseCount 가 한다.
struct sembuf sem_b;
sem_b.sem_num= 0 ;
sem_b.sem_op =-AI_ReleaseCount;
sem_b.sem_flg= SEM_UNDO ;
return semop(mh_Semaphore, &sem_b,1) ;
}/*
int LockRaw(ZTypUInt AI_ReleaseCount=1)*/
bool UnLock(ZTypUInt AI_ReleaseCount=1)
{
// cf) Window 의 ReleaseSemaphore(mh_Semaphore,lReleaseCount,lpPreviousCount) 에서
// 두번째 인수의 역할을 AI_ReleaseCount 가 한다.
struct sembuf sem_b;
sem_b.sem_num=0;
sem_b.sem_op =AI_ReleaseCount;
sem_b.sem_flg=SEM_UNDO;
return semop(mh_Semaphore, &sem_b,1)!=-1 ;
}/*
bool UnLock(ZTypUInt AI_ReleaseCount=1)*/
bool Close()
{
union semun SemUnion; bool VB_IsOK =
(semctl(mh_Semaphore, 0, IPC_RMID ,SemUnion)!=-1) ;
mh_Semaphore=0; return VB_IsOK;
}/*
bool Close()*/
public:
};/*
class ZCProcessSemaphore*/
///////////////////////////////////////////////
/////////// end class ZCProcessMutex ///////////
///////////////////////////////////////////////
/*/////////////////////////////////////////////
#define SEM_FAILED 0
#define SEM_VALUE_MAX 1147483648
int sem_init (sem_t * sem, int pshared, unsigned int value);
int sem_destroy (sem_t * sem);
sem_t *sem_open (const char *name, int oflag, ...);
int sem_close (sem_t *sem);
int sem_wait (sem_t * sem);
int sem_trywait (sem_t * sem);
int sem_timedwait (sem_t * sem, const struct timespec *abstime);
int sem_post (sem_t * sem);
int sem_getvalue(sem_t * sem, int *sval);
cf)
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
DESCRIPTION
The sem_wait() function locks the semaphore referenced by sem
by performing a semaphore lock operation on that semaphore.
If the semaphore value is currently zero,
then the calling thread will not return from the call to sem_wait()
until it either locks the semaphore or the call is interrupted by a signal.
The sem_trywait() function locks the semaphore referenced by sem
only if the semaphore is currently not locked;
that is, if the semaphore value is currently positive.
Otherwise, it does not lock the semaphore.
Upon successful return,
the state of the semaphore is locked and remains locked
until the sem_post() function is executed and returns successfully.
The sem_wait() function is interruptible by the delivery of a signal.
HMUG - Mac OS X / Darwin man pages
Current Directory /man/2
--------------------------------------------------------------------------------
NAME
sem_open - initialize and open a named semaphore
SYNOPSIS
#include <semaphore.h>
sem_t* sem_open(const char *name, int flags)
sem_t* sem_open(const char *name, int flags, mode_t mode, unsigned int value)
DESCRIPTION
The named semaphore named name is initialized and opened as specified by
the argument flags and a semaphore descriptor is returned to the calling
process.
The flags specified are formed by or'ing the following values:
O_CREAT create the semaphore if it does not exist
O_EXCL error if create and semaphore exists
If O_CREATE if specified, sem_open() requires an additional two argu-
ments. mode specifies the permissions for the semaphore as described in
chmod(2) and modified by the process' umask value (see umask(2)). The
semaphore is created with an initial value, which must be less than or
equal to SEM_VALUE_MAX.
If O_EXCL is specified and the semaphore exists, sem_open() fails. The
check for the existence of the semaphore and the creation of the
semaphore are atomic with respect to all processes calling sem_open()
with O_CREAT and O_EXCL set.
When a new semaphore is created, it is given the user ID and group ID
which coorespond to the effective user and group IDs of the calling pro-
cess. There is no visible entry in the file system for the created object
in this implementation.
The returned semaphore descriptor is available to the calling process un-
til it is closed with sem_close(), or until the caller exits or execs.
If a process makes repeated calls to sem_open(), with the same name argu-
ment, the same descriptor is returned for each successful call, unless
sem_unlink() has been called on the semaphore in the interim.
If sem_open() fails for any reason, it will return a value of SEM_FAILED
and sets errno. On success, it returns a semaphore descriptor.
ERRORS
The named semaphore is opened unless:
[EACCES] The required permissions (for reading and/or writing) are
denied for the given flags; or O_CREAT is specified, the
object does not exist, and permission to create the
semaphore is denied.
[EEXIST] O_CREAT and O_EXCL were specified and the semaphore exists.
[EINTR] The sem_open() operation was interrupted by a signal.
[EINVAL] The shm_open() operation is not supported; or O_CREAT is
specified and value exceeds SEM_VALUE_MAX.
[EMFILE] The process has already reached its limit for semaphores or
file descriptors in use.
[ENAMETOOLONG]
name exceeded SEM_NAME_LEN characters.
[ENFILE] Too many semaphores or file descriptors are open on the
system.
[ENOENT] O_CREAT is not set and the named semaphore does not exist.
[ENOSPC] O_CREAT is specified, the file does not exist, and there is
insufficient space available to create the semaphore.
SEE ALSO
semctl(2), semget(2), semop(2), sem_close(2), sem_post(2),
sem_trywait(2), sem_unlink(2), sem_wait(2), umask(2)
HISTORY
sem_open() is specified in the POSIX Realtime Extension
(1003.1b-1993/1003.1i-1995).
Darwin Operating System June 8, 2000 2
/////////////////////////////////////////////*/
/*//////////////////////////////////////////////////////////
■ class ZCThreadSemaphore 는 윈도우와 상당히 호환되기 어렵다.
윈도우와 호환은 class ZCProcessSemaphore 이 약간 좋다.
■ sem_t 에는 0 을 대입할 수 없다.
//////////////////////////////////////////////////////////*/
class ZCThreadSemaphore
{
private:
sem_t mh_Semaphore;
public :
/*/////////////////////////////////////////////////////////////////////////////
■ sem_init() 함수는 이름없는 세마포어를 만들때 사용한다. 이름있는 세마포어의
경우 sem_open() 을 사용해야 하며, 파괴할 때도 sem_close() 와 sem_unlink() 를
사용해야 한다. 이 클래스에서는 이름없는 세마포어만 다룬다.
AI_Shared 가 0 이 아니면 다중 프로세스에서도 사용할 수 있으나 현재 Linux 의
sem_XXX 계열의 함수는 이 기능을 지원하지 않고 있다.(2004.7.12)
■ AI_InitValue==1 이라면 뮤텍스처럼 사용하려는 경우이다. 2007-09-30 14:32:00
/////////////////////////////////////////////////////////////////////////////*/
bool Make(int AI_InitValue=1,int AI_Shared=0)
{
return ::sem_init(&mh_Semaphore, AI_Shared, AI_InitValue) !=-1 ;
}/*
bool Make(int AI_InitValue=1,int AI_Shared=0);*/
bool MakeZero(int AI_Shared=0)
{
return ::sem_init(&mh_Semaphore,AI_Shared,0) !=-1 ;
}/*
bool MakeZero(int AI_Shared=0)*/
bool Lock()
{
return ::sem_wait(&mh_Semaphore)==0 ;
}/*
bool Lock()*/
int LockRaw()
{
return ::sem_wait(&mh_Semaphore);
}/*
int LockRaw()*/
#ifdef __USE_XOPEN2K
// cf) int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int LockRaw(const struct timespec& AR_TimeOut)
{
// timeout 이면 errno 가 ZNsEnum::ZEThread_TimeOut
if(::sem_timedwait(&mh_Semaphore, &AR_TimeOut)!=ZNsEnum::ZEThread_OK)
return errno;
//endif
return ZNsEnum::ZEThread_OK;
}/*
int Lock(const struct timespec& AR_TimeOut)*/
int LockRaw(long AL_MiliSec)
{
timespec VO_TimeSpec;
VO_TimeSpec.tv_sec =time(NULL)+AL_MiliSec/1000;
VO_TimeSpec.tv_nsec=(AL_MiliSec%1000)*1000*1000;
return ::sem_timedwait(&mh_Semaphore,&VO_TimeSpec);
}/*
LockRaw LockRaw(long AL_MiliSec)*/
int LockTime(long AL_MiliSec)
{
return LockRaw(AL_MiliSec);
}/*
int LockTime(long AL_MiliSec)*/
#endif //__USE_XOPEN2K
// sem_trywait() 은 이미 lock 이 걸려 있으면 -1 을
// 반환하고, errno 가 EAGAIN 으로 셋팅된다.
bool TryLock()
{
return ::sem_trywait(&mh_Semaphore)==0 ;
}/*
bool TryLock()*/
bool UnLock()
{
return ::sem_post(&mh_Semaphore)==0 ;
}/*
bool UnLock()*/
// sem_destroy() 함수는 이름없는 세마포어의 제거에만 사용되는 함수다.
// 이름있는 세마포어에 사용할 경우에 그 행위는 정의되어 있지 않다.
bool Close()
{
return (sem_destroy(&mh_Semaphore)==0);
}/*
bool Close()*/
int GetValue() const
{
int VI_Val=0; const bool CB_IsOK =
(::sem_getvalue((sem_t*)&mh_Semaphore, &VI_Val)!=0);
if(CB_IsOK) return -1; return VI_Val;
}/*
int GetValue() const*/
public:
};/*
class ZCThreadSemaphore*/
// 리눅스 쓰레드 뮤텍스는 복사해서 사용할 수 없다.
class ZCThreadMutex
{
private :
ZCThreadMutex(const ZCThreadMutex& rhs){}
ZCThreadMutex& operator=(const ZCThreadMutex& rhs)
{
return *this;
}/*
ZCThreadMutex& operator=(const ZCThreadMutex& rhs)*/
/*private :*/
protected:
pthread_mutex_t mo_Mutex;
public :
ZCThreadMutex()
{
}/*
ZCThreadMutex()*/
const pthread_mutex_t& GetMutexID() const
{
return mo_Mutex;
}/*
const pthread_mutex_t& GetMutexID() const*/
pthread_mutex_t& GetMutexID()
{
return mo_Mutex;
}/*
pthread_mutex_t& GetMutexID()*/
bool Make(const pthread_mutexattr_t* AP_MutexAttr=0)
{
return pthread_mutex_init(&mo_Mutex, AP_MutexAttr)==0 ;
}/*
bool Make(const pthread_mutexattr_t* AP_MutexAttr=0)*/
bool Lock()
{
return this->LockRaw()==0 ;
}/*
bool Lock()*/
int LockRaw()
{
return pthread_mutex_lock(&mo_Mutex) ;
}/*
int LockRaw()*/
#ifdef __USE_XOPEN2K
bool Lock(long AL_MiliSec)
{
return this->LockRaw(AL_MiliSec)==0 ;
}/*
bool Lock(long AL_MiliSec)*/
// cf) 30 초간 LOCK
//
// struct timespec ts_timeout;
// ts_timeout.tv_sec =time(NULL)+30;
// ts_timeout.tv_nsec=0;
int LockRaw(const struct timespec& AR_TimeOut)
{
return pthread_mutex_timedlock(&mo_Mutex, &AR_TimeOut) ;
}/*
int LockRaw(const struct timespec& AR_TimeOut)*/
int LockRaw(long AL_MiliSec)
{
timespec VO_TimeSpec;
VO_TimeSpec.tv_sec = time(NULL)+AL_MiliSec/1000 ;
VO_TimeSpec.tv_nsec= (AL_MiliSec%1000)*1000*1000 ;
return pthread_mutex_timedlock(&mo_Mutex, &VO_TimeSpec) ;
}/*
int LockRaw(long AL_MiliSec)*/
int LockTime(long AL_MiliSec)
{
return LockRaw(AL_MiliSec);
}/*
int LockTime(long AL_MiliSec)*/
#endif //__USE_XOPEN2K
int TryLock()
{
return pthread_mutex_trylock(&mo_Mutex);
}/*
int TryLock()*/
bool UnLock()
{
return pthread_mutex_unlock(&mo_Mutex)==0 ;
}/*
bool UnLock()*/
bool Close()
{
return pthread_mutex_destroy(&mo_Mutex)==0 ;
}/*
bool Close()*/
public:
};/*
class ZCThreadMutex*/
class ZCThreadMutexAttr
{
private:
ZCThreadMutexAttr(const ZCThreadMutexAttr& rhs)
{
}/*
ZCThreadMutexAttr(const ZCThreadMutexAttr& rhs)*/
ZCThreadMutexAttr& operator=(const ZCThreadMutexAttr& rhs)
{
return *this;
}/*
ZCThreadMutexAttr& operator=(const ZCThreadMutexAttr& rhs)*/
/*private :*/
protected:
pthread_mutex_t mo_Mutex ;
pthread_mutexattr_t mo_PThreadMutexAttr;
public :
ZCThreadMutexAttr()
{
}/*
ZCThreadMutexAttr()*/
const pthread_mutex_t& GetMutexID() const
{
return mo_Mutex;
}/*
const pthread_mutex_t& GetMutexID() const*/
pthread_mutex_t& GetMutexID()
{
return mo_Mutex;
}/*
pthread_mutex_t& GetMutexID()*/
bool Make(int AI_ThreadType=PTHREAD_MUTEX_RECURSIVE)
{
// PTHREAD_MUTEX_RECURSIVE mutex 는 같은 쓰레드에서 소유한 만큼 해제해야한다.
// 따라서 클래스 생성자에서 소유하고 소멸자에서 해제하기에 딱 좋다.
if(pthread_mutexattr_init(&mo_PThreadMutexAttr)!=0)
return false;
if(pthread_mutexattr_settype(&mo_PThreadMutexAttr,AI_ThreadType)!=0)
return false;
if(pthread_mutexattr_setpshared(&mo_PThreadMutexAttr,PTHREAD_PROCESS_SHARED)!=0)
return false;
//endif
/*///////////////////////////////////////////////////////////////////////////////
if(pthread_mutexattr_setpshared(&mo_PThreadMutexAttr,PTHREAD_PROCESS_PRIVATE)!=0)
return false;
//endif
도 가능한데, Mutex 를 프로세스 사이에 공유되게 하고 싶다면 PTHREAD_PROCESS_SHARED
을 기본값으로 하는 것이 맞다.
-- 2010-01-04 11:59:00
///////////////////////////////////////////////////////////////////////////////*/
return pthread_mutex_init(&mo_Mutex,(const pthread_mutexattr_t*)&mo_PThreadMutexAttr)==0 ;
}/*
bool Make(int AI_ThreadType=PTHREAD_MUTEX_RECURSIVE)*/
bool Make(const pthread_mutexattr_t* AP_MutexAttr)
{
return pthread_mutex_init(&mo_Mutex, AP_MutexAttr)==0 ;
}/*
bool Make(const pthread_mutexattr_t* AP_MutexAttr)*/
bool Lock()
{
return this->LockRaw()==0 ;
}/*
bool Lock()*/
#ifdef __USE_XOPEN2K
bool Lock(long AL_MiliSec)
{
return this->LockRaw(AL_MiliSec)==0 ;
}/*
bool Lock(long AL_MiliSec)*/
#endif //__USE_XOPEN2K
int LockRaw()
{
return pthread_mutex_lock(&mo_Mutex) ;
}/*
int LockRaw()*/
// cf) 30 초간 LOCK
//
// struct timespec ts_timeout;
// ts_timeout.tv_sec=time(NULL)+30;
// ts_timeout.tv_nsec=0;
#ifdef __USE_XOPEN2K
int LockRaw(const struct timespec& AR_TimeOut)
{
return pthread_mutex_timedlock(&mo_Mutex, &AR_TimeOut) ;
}/*
int LockRaw(const struct timespec& AR_TimeOut)*/
int LockRaw(long AL_MiliSec)
{
timespec VO_TimeSpec;
VO_TimeSpec.tv_sec = time(NULL)+AL_MiliSec/1000 ;
VO_TimeSpec.tv_nsec= (AL_MiliSec%1000)*1000*1000 ;
return pthread_mutex_timedlock(&mo_Mutex, &VO_TimeSpec) ;
}/*
int LockRaw(long AL_MiliSec)*/
#endif //__USE_XOPEN2K
int TryLock()
{
return pthread_mutex_trylock(&mo_Mutex);
}/*
int TryLock()*/
bool UnLock()
{
return pthread_mutex_unlock(&mo_Mutex)==0 ;
}/*
bool UnLock()*/
bool Close()
{
return pthread_mutexattr_destroy(&mo_PThreadMutexAttr)==0 &&
pthread_mutex_destroy (&mo_Mutex) ==0 ;
///////
}/*
bool Close()*/
public:
};/*
class ZCThreadMutexAttr*/
typedef ZCThreadMutexAttr ZCThreadMutexStd;
class ZCThreadMutexEasy : protected ZCThreadMutex
{
public:
ZCThreadMutexEasy()
{
if(this->ZCThreadMutex::Make()==false)
{
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCThreadMutexEasy() Fail To Mutex"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->ZCThreadMutex::Make()==false)*/
}/*
ZCThreadMutexEasy()*/
/*///////////////////////////////////////////////////////////////////////////////////////////////////////////
■ 아래 선언
ZCThreadMutexEasy(const ZCThreadMutexEasy& rhs) : ZCThreadMutex()
에서 ZCThreadMutex() 를 빼면 g++ 4.4.6 에서 -W 옵션을 주면, 아래 경고 메시지를 볼 수 있다.
warning: base class 'class std::ZCThreadMutex' should be explicitly initialized in the copy constructor
위 경고 메시지가 합리적이라고 생각되는 것이, 컴파일러는 기초 클래스의 복사 생성자 ZCThreadMutex(rhs) 을 호출
할 것이라고 예상했기 때문일 것이다.
-- 2013-05-05 06:49:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////*/
ZCThreadMutexEasy(const ZCThreadMutexEasy& rhs) : ZCThreadMutex()
{
if(this->ZCThreadMutex::Make()==false)
{
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCThreadMutexEasy(const ZCThreadMutexEasy&) Fail To Mutex"<<std::endl;
fileout.close();
::exit(1);
}/*
if(this->ZCThreadMutex::Make()==false)*/
}/*
ZCThreadMutexEasy(const ZCThreadMutexEasy& rhs)*/
~ZCThreadMutexEasy()
{
if(this->ZCThreadMutex::Close()==false)
{
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ~ZCThreadMutexEasy() Fail To Close Mutex"<<std::endl;
fileout.close();
::exit(1);
}/*
if(this->ZCThreadMutex::Close()==false)*/
}/*
~ZCThreadMutexEasy()*/
using ZCThreadMutex::Lock ;
using ZCThreadMutex::UnLock;
public:
};/*
class ZCThreadMutexEasy*/
class ZCThreadMutexAttrEasy : protected ZCThreadMutexAttr
{
public:
ZCThreadMutexAttrEasy()
{
if(this->ZCThreadMutexAttr::Make()==false)
{
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCThreadMutexAttrEasy() Fail To Mutex"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->ZCThreadMutexAttr::Make()==false)*/
}/*
ZCThreadMutexAttrEasy()*/
/*///////////////////////////////////////////////////////////////////////////////////////////////////////////
■ 아래 선언
ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&) : ZCThreadMutexAttr()
에서 ZCThreadMutex() 를 빼면 g++ 4.4.6 에서 -W 옵션을 주면, 아래 경고 메시지를 볼 수 있다.
warning: base class 'class std::ZCThreadMutexAttr' should be explicitly initialized in the copy constructor
위 경고 메시지가 합리적이라고 생각되는 것이, 컴파일러는 기초 클래스의 복사 생성자 ZCThreadMutexAttr(rhs) 을
호출할 것이라고 예상했기 때문일 것이다.
-- 2013-05-05 06:49:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////*/
ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&) : ZCThreadMutexAttr()
{
if(this->ZCThreadMutexAttr::Make()==false)
{
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&) Fail To Mutex"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->ZCThreadMutexAttr::Make()==false)*/
}/*
ZCThreadMutexAttrEasy(const ZCThreadMutexAttrEasy&)*/
~ZCThreadMutexAttrEasy()
{
if(this->ZCThreadMutexAttr::Close()==false)
{
std::fstream fileout("DEBUG.txt",std::ios::out | std::ios::app);
fileout<<std::endl<<"File : "<<__FILE__<<std::endl<<"Line : "<<__LINE__<<std::endl;
fileout<<"Error !! ~ZCThreadMutexAttrEasy() Fail To Close Mutex"<<std::endl;
fileout.close();
exit(1);
}/*
if(this->ZCThreadMutexAttr::Close()==false)*/
}/*
~ZCThreadMutexAttrEasy()*/
using ZCThreadMutexAttr::Lock ;
using ZCThreadMutexAttr::UnLock;
public:
};/*
class ZCThreadMutexAttrEasy*/
/*/////////////////////////////////////////////////////////
■ 아래 기본 뮤텍스 ZCThreadMutexEasy 은 재귀적으로 사용할
수 없어서(테스트해 보면 dead lock 이 발생한다) 다소 불
안하다.(Window Mutex 에 비해서)
typedef ZCThreadMutexEasy ZCDefLockEasy;
그래서 아래 선언으로 바꾼다. -- 2009-07-28 16:27:00
재귀적으로 Mutex 를 사용하는 상황을 만들지 말아야 한다.
-- 2013-07-22 12:28:00
■ freebsd 8.2 에서 테스트할 때, 자꾸
pthread_mutexattr_setpshared(
&mo_PThreadMutexAttr,PTHREAD_PROCESS_SHARED
)!=0
가 되는 현상이 있어서, ZCThreadMutexEasy 를 사용할 수 없
었다. 그래서 __MUTEX_EASY_DEFAULT_LOCK__ 가 정의되어 있
으면,
typedef ZCThreadMutexEasy ZCDefLockEasy;
선언을 사용하게 한다. -- 2011-05-19 22:00:00
기본적으로 ZCThreadMutexEasy 을 사용하고 ZCThreadMutexAttrEasy
은 __MUTEX_ATTR_EASY_DEFAULT_LOCK__ 이 정의되어 있을 때,
사용하는 것으로 한다. -- 2015-09-02 21:05:00
/////////////////////////////////////////////////////////*/
#ifdef __MUTEX_ATTR_EASY_DEFAULT_LOCK__
typedef ZCThreadMutexAttrEasy ZCDefLockEasy;
#else
typedef ZCThreadMutexEasy ZCDefLockEasy;
#endif
#ifdef __USE_XOPEN2K
/*///////////////////////////////////////////////////////////////////////
■ spin lock 은 짧은 시간 동기화에는 가장 좋다. 그런데 일반적으로 동기화
에 뮤텍스를 사용하자. spin lock 은 context switching 에 부담이 되어서
자칫 반응속도가 느려질 수 있다. 심지어 몇 초씩 걸러서 사용못하는 경우
도 발생한다.
■ 뮤텍스가 아닌 spin lock 으로 동기화를 하는 경우에는 동기화 영역이 정말
짧은 수행시간을 가져야 한다. 동기화 영역에서 콘솔에 출력하는 등의 '긴
처리'가 있으면 아주 나쁘다. 멀티 쓰레드에서 spin lock 으로 동기화를 할
때, 위와 같은 다소 '긴 처리'를 할 경우, 작업 쓰레드가 컨텍스트 스위칭
을 하면서 동기화 영역이 끝나지 않을 수 있고 또 다른 작업 쓰레드의 동기
화 영역에서 cpu 가 spin 하고 있다면 동기화가 끝나는 시점이 늦어지고,
심지어는 dead lock 에 빠질 수도 있다.
-- 2008-03-20 11:27:00
///////////////////////////////////////////////////////////////////////*/
class ZCSpinLock
{
private:
pthread_spinlock_t mh_SpinLock;
public :
int Init(int AI_ShareMode=PTHREAD_PROCESS_PRIVATE)
{
// 프로세스간에 spin lock 을 공유한다면 PTHREAD_PROCESS_SHARED 를 준다.
// 그리고 pthread_spinlock_t 도 공유메모리에 있어야 한다. 절차가 복잡하다.
return pthread_spin_init(&mh_SpinLock,AI_ShareMode);
}/*
void Init(int AI_ShareMode=PTHREAD_PROCESS_PRIVATE)*/
int Close()
{
return pthread_spin_destroy(&mh_SpinLock);
}/*
void Close()*/
int Delete() // for windows compatability
{
return this->Close();
}/*
void Delete()*/
int Lock()
{
return pthread_spin_lock(&mh_SpinLock);
}/*
void Lock()*/
int TryLock()
{
return pthread_spin_trylock(&mh_SpinLock);
}/*
void TryLock()*/
int UnLock()
{
return pthread_spin_unlock(&mh_SpinLock);
}/*
void UnLock()*/
public:
};/*
class ZCSpinLock*/
class ZCSpinLockEasy : protected ZCSpinLock
{
public:
/*#######################################################################
■ 소멸자를 제외한 각 멤버 함수 선언에 throw(ZtCExceptBase<int, int>) 을
붙였는데, g++ 7.3.0 에서 경고가 나서 지웠다.
CProcess_Linux.H:2429:25: warning:
dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
ZCSpinLockEasy() throw(ZtCExceptBase<int, int>)
-- 2018-06-26 15:12:00
#######################################################################*/
ZCSpinLockEasy() /*throw(ZtCExceptBase<int, int>)*/
{
int VI_Return=this->Init();
if(VI_Return!=0)
{
throw ZtCExceptBase<int, int>(VI_Return);
}/*
if(VI_Return!=0)*/
}/*
ZCSpinLockEasy()*/
ZCSpinLockEasy(const ZCSpinLockEasy& rhs)
{
int VI_Return=this->Init();
if(VI_Return!=0)
{
throw ZtCExceptBase<int, int>(VI_Return);
}/*
if(VI_Return!=0)*/
}/*
ZCSpinLockEasy(const ZCSpinLockEasy& rhs)*/
void Lock()
{
int VI_Return=this->ZCSpinLock::Lock();
if(VI_Return!=0)
{
throw ZtCExceptBase<int, int>(VI_Return);
}/*
if(VI_Return!=0)*/
}/*
void Lock() throw(ZtCExceptBase<int, int>)*/
void UnLock()
{
int VI_Return=this->ZCSpinLock::UnLock();
if(VI_Return!=0)
{
throw ZtCExceptBase<int, int>(VI_Return);
}/*
if(VI_Return!=0)*/
}/*
void UnLock() throw(ZtCExceptBase<int, int>)*/
~ZCSpinLockEasy()
{
this->Delete();
}/*
ZCSpinLockEasy()*/
public:
};/*
class ZCSpinLockEasy : protected ZCSpinLock*/
typedef ZCSpinLock ZCCriticSect ;
typedef ZCSpinLockEasy ZCCriticSectEasy;
#else // !__USE_XOPEN2K
typedef ZCThreadMutex ZCCriticSect ;
typedef ZCThreadMutexEasy ZCCriticSectEasy;
#endif //!__USE_XOPEN2K
template<typename TAutoKey> class ZtCAutoKeyRev;
/*////////////////////////////////////////////////
■ class ZtCAutoKey<> : Lock 을 설정하고 해제한다.
열고 닫을 수 있는 열쇠와 같아 Key 라고 하였다.
--
이런 식의 기법을 RAII
Resource Acquisition Is Initialization
라고 한다는 것을 알았다. -- 2015-03-10 15:08:00
////////////////////////////////////////////////*/
template< typename TCriticSectEasy=ZCThreadMutexEasy
>
class ZtCAutoKey /////////////////////////////////////
{
public :
template<typename TAutoKey> friend class ZtCAutoKeyRev;
private:
ZtCAutoKey(const ZtCAutoKey&){}
private:
TCriticSectEasy& mr_SyncEasy;
#ifdef _DEBUG_CAUTOKEY_
static int msi_CallCnt;
#endif //_DEBUG_CAUTOKEY_
public:
ZtCAutoKey(TCriticSectEasy& AR_SyncEasy):mr_SyncEasy(AR_SyncEasy)
{
#ifdef _DEBUG_CAUTOKEY_
cout<<"▶▶▶▶ ZtCAutoKey:: ZtCAutoKey() "<<this<<", "<<++msi_CallCnt<<" ▶▶▶▶"<<endl;
#endif //_DEBUG_CAUTOKEY_
mr_SyncEasy.Lock();
}/*
ZtCAutoKey(TCriticSectEasy& AR_SyncEasy)*/
~ZtCAutoKey()
{
#ifdef _DEBUG_CAUTOKEY_
cout<<"◀◀◀◀ ZtCAutoKey::~ZtCAutoKey() "<<this<<", "<<--msi_CallCnt<<" ◀◀◀◀"<<endl;
#endif //_DEBUG_CAUTOKEY_
mr_SyncEasy.UnLock();
}/*
ZtCAutoKey()*/
public:
};/*
template<typename TCriticSectEasy=ZCThreadMutexEasy> class ZtCAutoKey */
#ifdef _DEBUG_CAUTOKEY_
template<typename TCriticSectEasy>
int ZtCAutoKey<TCriticSectEasy>::msi_CallCnt = 0 ;
#endif //_DEBUG_CAUTOKEY_
/*////////////////////////////////////////////////////////////////
■ ZtCAutoKeyRev<> 는 ZtCAutoKey<> 을 생성자에서 인수로 받아서 생
성자에서 Lock 을 해제하고, 소멸자에서 다시 Lock 을 걸어 준다.
그런데 이렇게 해야 될 상황 자체를 만들지 말자. 억지로 사용하다
보면 자주 Dead Lock 상태에 빠진다.
※ 2008-04-09 21:01:00
////////////////////////////////////////////////////////////////*/
template<typename TAutoKey> class ZtCAutoKeyRev
{
private:
TAutoKey& mr_CAutoKey;
public :
ZtCAutoKeyRev(TAutoKey& AR_CAutoKey):mr_CAutoKey(AR_CAutoKey)
{
mr_CAutoKey.mr_SyncEasy.UnLock();
}/*
ZtCAutoKeyRev(TAutoKey& AR_CAutoKey)*/
~ZtCAutoKeyRev()
{
mr_CAutoKey.mr_SyncEasy.Lock();
}/*
~ZtCAutoKeyRev()*/
public:
};/*
template<typename TAutoKey> class ZtCAutoKeyRev */
#ifdef __USE_XOPEN2K
class ZCSpinLockProcess
{
public :
enum{EReturn_ShareMemNone = -2}; // 해당 공유메모리 없음
enum{EReturn_ShareMemMapErr= -3}; // 공유메모리 Map 실패
private:
pthread_spinlock_t* mph_SpinLock ;
ZCShareMemory mo_CShareMemory;
public :
int Init(long AL_MemMapkey)
{
if(mo_CShareMemory.ShmGet(AL_MemMapkey, sizeof(pthread_spinlock_t))==false)
{
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MemMapkey)!=true)
{
return this->EReturn_ShareMemNone;
}/*
if(errno!=EEXIST || mo_CShareMemory.ShmGet(AL_MemMapkey)!=true)*/
if(this->mo_CShareMemory.LinkMap()==false)
{
this->mo_CShareMemory.Close(); return this->EReturn_ShareMemMapErr;
}/*
if(this->mo_CShareMemory.LinkMap()==false)*/
mph_SpinLock =
(pthread_spinlock_t*)mo_CShareMemory.GetStartAddress();
return 0;
}
else // mo_CShareMemory.ShmGet(AL_MemMapkey, sizeof(pthread_spinlock_t))!=false
{
if(this->mo_CShareMemory.LinkMap()==false)
{
this->mo_CShareMemory.Close(); return this->EReturn_ShareMemMapErr;
}/*
if(this->mo_CShareMemory.LinkMap()==false)*/
mph_SpinLock=(pthread_spinlock_t*)mo_CShareMemory.GetStartAddress();
::memset((void*)mph_SpinLock, 0x00, sizeof(pthread_spinlock_t));
return ::pthread_spin_init(mph_SpinLock, PTHREAD_PROCESS_SHARED);
}/*
else // mo_CShareMemory.ShmGet(AL_MemMapkey,sizeof(pthread_spinlock_t))!=false*/
}/*
int Init(long AL_MemMapkey)*/
int Close()
{
mo_CShareMemory.UnMap();
mo_CShareMemory.Close();
return ::pthread_spin_destroy(mph_SpinLock);
}/*
void Close()*/
int Lock()
{
return ::pthread_spin_lock(mph_SpinLock);
}/*
void Lock()*/
int TryLock()
{
return ::pthread_spin_trylock(mph_SpinLock);
}/*
void TryLock()*/
int UnLock()
{
return ::pthread_spin_unlock(mph_SpinLock);
}/*
void UnLock()*/
public:
};/*
class ZCSpinLockProcess*/
typedef ZCSpinLock ZCCriticSect;
#endif //__USE_XOPEN2K
/*//////////////////////////////////////////////////////////////////////////////////////
■ http://blog.naver.com/kimsk99?Redirect=Log&logNo=50004383787
윈도우 프로그램을 하면서 쓰레드를 생성할 때, CreateThread와 _beginthread라는 두 함수
중 어떤 함수를 사용해야 할 지 고민하게 된다. 난 항상 뭔가 기본 API같은 CreateThread
를 사용해 왔었다. 그런데 오늘 책을 읽다가 그게 잘못되었다는 것을 알았다. MSDN 에 나
온 설명을 자세히 읽어보면 나오는 내용이었지만 보통 자세히는 안 읽으니 그동안 몰랐
었다. 일단 CreateThread 는 윈도우 SDK에서 제공하는 기본 API이다. 특별한 라이브러리와
링크하지 않아도 윈도우라면 컴파일되고 돌아간다. 하지만
_beginthread(또는 _beginthreadex)
는 standard C library에 속한 함수로 링크해야 동작한다. 그렇기 때문에 기능에서도 차이
가 난다. 사실 기능에서 차이가 나지 않는다면 이렇게 구별할 이유가 없었을 것이다.
_beginthread 와 같은 경우 인자의 종류가 달라서 쓰레드 생성시 필요한 설정을 할 수 없
는 경우가 있다. 그러나 비슷한 함수인 _beginthreadex 는 완전히 CreateThread와 같은
인자를 가지고 있다. 그렇기 때문에 CreateThread가 쓰인 모든 곳은 _beginthreadex로 치
환이 가능하다.
내부동작에서 가장 큰 차이점은 _beginthread 는 standard C library에서 사용하는 영역에
대한 TLB 초기화를 해준다는 것이다. standary C library에 있는 함수 중 일부는 thread-
safety를 위해서 TLB 를 사용하는데 초기화가 되지 않는다면 문제가 발생할 수 있다.
_beginthreadex 함수를 사용해서 쓰레드를 구동했다면 스레드 함수 내에서 쓰레드를 종료
하기 위해서 ExitThread 함수를 사용하기 보다는 _endthreadex 함수를 사용하길 권장한다.
대부분의 경우 C library를 사용하기 때문에 쓰레드의 생성은 _beginthreadex 를 사용해야
한다.
uintptr_t _beginthreadex
(
void *security,
unsigned stack_size,
unsigned ( *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
HANDLE CreateThread
(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE AP_StartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
CreateThread 는 _beginthreadex 로 대체될 수 있고 그래야 한다.
dwCreationFlags
[in] Flags that control the creation of the thread.
If the CREATE_SUSPENDED flag is specified,
the thread is created in a suspended state,
and will not run until the ResumeThread function is called.
If this value is zero,
the thread runs immediately after creation.
If the STACK_SIZE_PARAM_IS_A_RESERVATION flag is specified,
the dwStackSize parameter specifies the initial reserve size of the stack.
Otherwise, dwStackSize specifies the commit size.
Windows 2000/NT and Windows Me/98/95:
The STACK_SIZE_PARAM_IS_A_RESERVATION flag is not supported.
lpThreadId
[out] Pointer to a variable that receives the thread identifier.
If this parameter is NULL, the thread identifier is not returned.
Windows Me/98/95: This parameter may not be NULL.
cf) DWORD ResumeThread( HANDLE hThread );
■ IN WINBASE.H
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)
(
LPVOID lpThreadParameter
);
typedef struct _SECURITY_ATTRIBUTES
{
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
LPTHREAD_START_ROUTINE AP_StartAddress 는 시작함수포인터
원형은 DWORD WINAPI ThreadFunc(LPVOID lpRarameter)
■ MFC 의 동기화 object
아래 object 를 쓰기 위해서는 <afxmt.h> 가 필요
1) CCriticalSection cs;
cs.Lock();
// 작업코드
cs.Unlock();
2) CEvent event;
Thread 들이 참조할 수 있도록 전역 또는 상위 블럭에
CEvent object 를 선언하고 Thread CallBack 함수에서
UINT ThreadFunc(LPVOID pParam)
{
while(TRUE)
{
CEventOvj.Lock(); // 잠이든다.
// 이벤트가 발생할 때 실행될 코드를 적는다.
// 그리고 외부에서
// CEventOvj.PulseEvent(); 이나
// CEventOvj.SetEvent(); 를 호출하면 깨어난다.
// Thread 가 Lock 이 걸려 있지 않은 상태에서
// PulseEvent 함수를 먼저 호출하면 아무일도 일어나지 않고
// 바로 다음 순간에 Lock() 를 호출하면 다시 잠을 잔다.
// SetEvent() 의 경우에는 그냥 통과한다.
}
//while(TRUE)
}
//UINT ThreadFunc(LPVOID pParam)
# Window Thread Callback Type;
DWORD WINAPI ThreadFunc(LPVOID lpParameter);
cf) typedef unsigned long DWORD;
# Linux Thread Callback Type:
void* ThreadFunc()(void*)
■ -- MFC 의 동기화 object
//////////////////////////////////////////////////////////////////////////////////////*/
typedef pthread_t ThreadID;
#define _THREAD_RETURN_ void*
namespace ZNsIFace
{
class ZCThread_BASE
{
};/*
class ZCThread_BASE*/
}/*
namespace ZNsIFace*/
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCThreadAttr : public TTypeBase ////////
{
protected:
pthread_attr_t mh_ThreadAttr;
public :
ZtCThreadAttr()
{
}/*
ZtCThreadAttr()*/
ZtCThreadAttr(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)
{
}/*
ZtCThreadAttr(TTypeBase& AR_CBaseType)*/
template<typename TTypeArg>
ZtCThreadAttr(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg)
{
}/*
template<typename TTypeArg>
ZtCThreadAttr(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg) */
pthread_attr_t* GetThreadAttrPtr()
{
return &mh_ThreadAttr ;
}/*
pthread_attr_t* GetThreadAttrPtr()*/
int Init()
{
/*////////////////////////////////////////
RETURN VALUES
On success, pthread_attr_init() returns 0. On error,
one of the following values is returned:
ENOMEM
Insufficient memory exists to create the thread attribute object.
EINVAL
attr does not point to writable memory.
EFAULT
attr is an invalid pointer
////////////////////////////////////////*/
return pthread_attr_init(&mh_ThreadAttr);
}/*
int Init()*/
int Fini()
{
return pthread_attr_destroy(&mh_ThreadAttr);
}/*
int Fini()*/
int GetDetachState(int& ARRI_DetachState)
{
return pthread_attr_getdetachstate(&mh_ThreadAttr, &ARRI_DetachState);
}/*
int GetDetachState(int ARRI_DetachState)*/
int SetDetachState(int AI_DetachState=PTHREAD_CREATE_DETACHED) // cf) PTHREAD_CREATE_JOINABLE
{
return pthread_attr_setdetachstate(&mh_ThreadAttr, AI_DetachState);
}/*
int SetDetachState(int AI_DetachState=PTHREAD_CREATE_DETACHED)*/
int GetSchedPolicy(int& ARRI_Policy)
{
return pthread_attr_getschedpolicy(&mh_ThreadAttr, &ARRI_Policy);
}/*
int GetSchedPolicy(int ARRI_Policy)*/
/*////////////////////////////////////////////////////////////
■ pthread_attr_setschedpolicy() 의 2 번 인수에 사용가능한 값
SCHED_FIFO
First in-first out (FIFO) scheduling policy.
SCHED_RR
Round robin scheduling policy.
SCHED_OTHER
Another scheduling policy
////////////////////////////////////////////////////////////*/
int SetSchedPolicy(int AI_Policy)
{
return pthread_attr_setschedpolicy(&mh_ThreadAttr, AI_Policy);
}/*
int SetSchedPolicy(int AI_Policy)*/
int GetSchedParam(const struct sched_param& ARR_SchedParam)
{
// struct sched_param{int sched_priority;};
return pthread_attr_getschedparam(&mh_ThreadAttr, &ARR_SchedParam);
}/*
int GetSchedParam(const struct sched_param& ARR_SchedParam)*/
int SetSchedParam(const struct sched_param& AR_SchedParam)
{
return pthread_attr_setschedparam(&mh_ThreadAttr, &AR_SchedParam);
}/*
int SetSchedParam(const struct sched_param& AR_SchedParam)*/
int GetStackSize(size_t& ARRI_StackSize)
{
return pthread_attr_getstacksize(&mh_ThreadAttr, &ARRI_StackSize);
}/*
int GetStackSize(size_t& ARRI_StackSize)*/
int SetStackSize(size_t AI_StackSize)
{
// PTHREAD_STACK_MIN 이상이어야 한다.
return pthread_attr_setstacksize(&mh_ThreadAttr, AI_StackSize);
}/*
int SetStackSize(size_t AI_StackSize)*/
/*/////////////////////////////////////////////////////////////////////////
■ 데이비드 R. 부트노프 저 'POSIX 쓰레드를 이용한 프로그래밍' 200 page 에
시스템에서 _POSIX_THREAD_ATTR_STACKSIZE 기호 상수를 정의하고 있다면,
size_t statcksize 속성을 사용할 수 있다.
고 되어 있는데, CentOS 64 bit 에서
find /usr/include/ | xargs grep 'POSIX_THREAD_ATTR_STACKSIZE'
를 실행하면,
/usr/include/bits/posix_opt.h:#define _POSIX_THREAD_ATTR_STACKSIZE 200112L
을 찾을 수 있다. -- 2011-05-23 20:44:00
■ 역시 같은 책, 같은 페이지에
POSIX 쓰레드에서는 한 쓰레드에서 요구하는 스택의 최소 크기를 나타내는
기호 상수로서 PTHREAD_STACK_MIN 을 정의한다.
이 기호를 CentOS 에서 찾으면 bits/local_lim.h 에
#define PTHREAD_STACK_MIN 16384
라고 되어 있다. -- 2011-05-23 20:55:00
/////////////////////////////////////////////////////////////////////////*/
public:
};/*
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCThreadAttr /////////////////////////*/
template< typename TTypeBase=ZNsIFace::ZCThread_BASE
>
class ZtCThread : public TTypeBase ///////////////////
{
protected:
ThreadID mh_ThreadID;
public :
ZtCThread()
{
_DEBUG_REENTRANT_CHECK_
mh_ThreadID = 0 ;
}
ZtCThread(TTypeBase& AR_CBaseType):TTypeBase(AR_CBaseType)
{
_DEBUG_REENTRANT_CHECK_
mh_ThreadID = 0 ;
}
ZtCThread(const ZtCThread& rhs):TTypeBase(rhs)
{
_DEBUG_REENTRANT_CHECK_
mh_ThreadID = 0 ;
}/*
ZtCThread(const ZtCThread& rhs):TTypeBase(rhs)*/
template<typename TTypeArg>
ZtCThread(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg)
{
_DEBUG_REENTRANT_CHECK_
mh_ThreadID = 0 ;
}/*
template<typename TTypeArg>
ZtCThread(const TTypeArg& AR_TTypeArg) : TTypeBase(AR_TTypeArg)
*/
ZtCThread& operator=(const ZtCThread& rhs)
{
*(TTypeBase*)(this) = rhs; return *this;
}/*
ZtCThread& operator=(const ZtCThread& rhs)*/
ZtCThread& operator=(const TTypeBase& AR_CBaseType)
{
*(TTypeBase*)(this) = AR_CBaseType; return *this;
}/*
ZtCThread& operator=(const TTypeBase& AR_CBaseType)*/
operator ThreadID () const
{
return mh_ThreadID;
}/*
operator ThreadID () const*/
ThreadID GetThreadID() const
{
return mh_ThreadID;
}/*
ThreadID GetThreadID() const*/
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)
{
return ::pthread_create( ///////////////
&mh_ThreadID ,
AP_ThreadAttr ,
AP_StartAddress ,
AP_Arg
/*/////////*/ ) == 0 ; /////////////////
}/*
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)*/
// 쓰레드 종료시까지 메인 함수의 실행을 지연(블럭)시킨다.
bool Wait()
{
return ::pthread_join(mh_ThreadID, NULL)==0 ;
}/*
bool Wait()*/
bool Wait(void** APP_ThreadReturn)
{
return ::pthread_join(mh_ThreadID, APP_ThreadReturn)==0 ;
}/*
bool Wait(void** APP_ThreadReturn)*/
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)
{
return ::pthread_join(AI_ThreadIDVar, APP_ThreadReturn)==0 ;
}/*
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)*/
/*////////////////////////////////////////////////////////////////////////////////////
■ pthread_detach 는 프로세스에서 fork() 후에 waitpid() 를 하지 않아도 되도록
SIGCHILD 를 무시하는 것과 동일하다.
그리고 병합(::pthread_join) 하지 않을 거면, 반드시 호출해야 하는 것으로 기억하자.
pthread_detach() 을 호출하지 않으면 쓰레드가 종료했는데도 메모리가 OS 에 반환되지
않을 수 있다. www.wordncode.com 의 파일서버를 만들어 줄 때 이 코드를 빼먹었더니만
클라이언트가 종료했는데도 계속 메모리가 줄어들지 않고 증가만 하는 것이었다. 그래서
어느 시점에 (가상)메모리를 소진하여 클라이언트 접속시에 컨넥션 쓰레드를 만들지 못
하고 있었다!
-- 2009-02-24 03:08:00
////////////////////////////////////////////////////////////////////////////////////*/
static bool Detach(pthread_t AI_ThreadID)
{
return ::pthread_detach(AI_ThreadID)==0;
}/*
static bool Detach(pthread_t AI_ThreadID)*/
bool Detach()
{
return ::pthread_detach(mh_ThreadID)==0;
}/*
bool Detach()*/
/*//////////////////////////////////////////////////////////////////////////////
■ 쓰레드를 취소(pthread_cancel)한다는 것은 다른 쓰레드로부터 취소요청을 받아서
작동을 중지하는 것을 의미합니다. 마치 kill 을 이용해서 프로세스를 중단시키는
것과 비슷합니다. 그러나 중단을 요청받은 쓰레드가 바로 중단되는가? 그것은 아닙
니다. kill 로 프로세스를 죽이는 것도 실제로는 프로세스를 죽이는 시그널을 보내
고 그 다음은 프로세스가 시그널을 처리하여 죽어줘야 하는데 가끔 죽지 않을 수도
있거나 혹은 지연(deferred) 되어 죽는 경우도 발생합니다. 쓰레드의 취소도 이와
같이 바로 죽어주지는 않습니다. 물론 즉시 취소되도록 설정할 수도 있읍니다만,
쓰레드 취소의 기본 세팅은 지연된(deferred) 취소를 의미합니다.
리눅스 시스템 네트워크 프로그래밍 : 가메출판사 : 김선영 저 315 page
■ cf) int pthread_setcancelstate(int state,int* oldstate) : 원자적 함수
state 은 PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE 중의 한 값
//////////////////////////////////////////////////////////////////////////////*/
static bool Cancel(pthread_t AI_ThreadID)
{
return ::pthread_cancel(AI_ThreadID)==0;
}/*
static bool Cancel(pthread_t AI_ThreadID)*/
bool Cancel()
{
return ::pthread_cancel(mh_ThreadID)==0;
}/*
bool Cancel()*/
public:
};/*
template< typename TTypeBase=ZNsIFace::ZCThread_BASE
>
class ZtCThread ////////////////////////////////////*/
template<> class ZtCThread<ZNsIFace::ZCThread_BASE>
{
protected:
ThreadID mh_ThreadID;
public :
ZtCThread()
{
_DEBUG_REENTRANT_CHECK_
}/*
ZtCThread()*/
ZtCThread(const ZtCThread& rhs)
{
mh_ThreadID=rhs.mh_ThreadID;
}/*
ZtCThread(const ZtCThread& rhs)*/
ZtCThread& operator=(const ZtCThread& rhs)
{
return *this; // nothing to do
}/*
ZtCThread& operator=(const ZtCThread& rhs)*/
operator ThreadID () const
{
return mh_ThreadID;
}/*
operator ThreadID () const*/
ThreadID GetThreadID() const
{
return mh_ThreadID;
}/*
ThreadID GetThreadID() const*/
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)
{
return ::pthread_create( //////////////
&mh_ThreadID ,
AP_ThreadAttr ,
AP_StartAddress ,
AP_Arg
/*/////////*/ ) == 0 ; ////////////////
}/*
bool Make(void* AP_StartAddress(void*), void* AP_Arg=NULL, pthread_attr_t* AP_ThreadAttr=NULL)*/
bool Wait()
{
return ::pthread_join(mh_ThreadID, NULL)==0 ;
}/*
bool Wait()*/
bool Wait(void** APP_ThreadReturn)
{
return ::pthread_join(mh_ThreadID, APP_ThreadReturn)==0 ;
}/*
bool Wait(void** APP_ThreadReturn)*/
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)
{
return ::pthread_join(AI_ThreadIDVar, APP_ThreadReturn)==0 ;
}/*
static bool Wait(ThreadID AI_ThreadIDVar, void** APP_ThreadReturn)*/
static bool Detach(pthread_t AI_ThreadID)
{
return ::pthread_detach(AI_ThreadID)==0;
}/*
static bool Detach(pthread_t AI_ThreadID)*/
bool Detach()
{
return ::pthread_detach(mh_ThreadID)==0;
}/*
bool Detach()*/
static bool Cancel(pthread_t AI_ThreadID)
{
return ::pthread_cancel(AI_ThreadID)==0;
}/*
static bool Cancel(pthread_t AI_ThreadID)*/
bool Cancel()
{
return ::pthread_cancel(mh_ThreadID)==0;
}/*
bool Cancel()*/
public:
};/*
template<> class ZtCThread<ZNsIFace::ZCThread_BASE>*/
/*//////////////////////////////////////////////////////////////////////////////////////
■ class ZtCMutexCond 는 window 에는 없는 리눅스 뮤텍스 조건 변수 클래스
이것이 유용한 경우는 다수의 쓰레드가 함수 A, 함수 B, 함수 C 를 수행하는데, 반드시 함
수 A -> B -> C 이 순서로 실행해야 하고 각 함수는 동기화를 해주어야 할 때, 쓰레드 조
건변수를 사용하면 한 개의 뮤텍스로 가능하나 (조건변수는 3-1 개), 뮤텍스로만 한다면 A,
B,C 용 각각 3 개의 뮤텍스를 사용해야 한다. 즉 한 개의 뮤텍스에 여러 개의 조건 변수가
대응할 수 있다. 반대로 한 개의 조건변수에 여러개의 뮤텍스가 대응할 수는 없다.
조건 변수는 복사 사용해서는 안된다. 따라서 복사 생성자와 대입 연산자의 실행을 막는다.
조건 변수는 반드시 한번만 초기화 되어야 한다.
■ 어떤 작업 큐에 다수의 쓰레드가 접근하여 작업을 가져온다고 가정하자. 작업큐에 접근하는
쓰레드는, 큐에 접근하는 동안은 다른 쓰레드가 동시 접근하지 않도록 '작업 큐 접근 동기
화 object ' 에 lock 을 걸고 해당 큐가 비어있는지 그렇지 않은지를 조사할 것이다. 이때
큐에 등록된 작업이 하나라도 있다면 그 작업을 가져오고 '작업큐 접근 동기화 object' 를
unlock 할 것이다. 만일 큐에 등록된 작업이 없다면 '작업 큐 접근 동기화 object' 를 unlock
하고 자신은 작업큐에 작업이 등록될 때까지 블럭되어야 한다. 이때 자신이 블럭되고 있다
는 것을 알리는 표시를 하고(그래야 나중에 '깨울' 수 있으니까) 블럭되야 하는데, 이 사이
에 즉 unlock 과 블럭표시를 하고 블럭되는 사이에 다른 쓰레드가 끼어들어 '작업큐 접근
동기화 object' 에 lock 을 걸고 작업을 등록하면, 작업은 분명 있는데 블럭표시를 하려는
쓰레드는 블럭표시를 하기도 전이므로, 다른 쓰레드는 그 쓰레드가 블럭되고 있지 않다고
보고 별 처리없이 '작업큐 접근 동기화 object' 에 unlock 을 하고 다음을 진행할 것이다.
그러면 블럭표시를하고 블럭되려는 쓰레드는 블럭이 해제될 기회를 갖지 못한 채 영원히 블
럭될 수 있다. 이 사태를 방자하기 위해 '작업큐 접근 동기화 object' 에 unlock 을 하는
작업과 블럭상태로 진입하는 명령은 원자적으로 이루어져야 한다. 이러면 다른 쓰레드가 블
럭되고 있는 쓰레드를 정확하게 판별할 수 있게끔 보장해준다. 이것이 조건 변수가 존재하
는 이유이다.
조건 변수 대기는 항상 뮤텍스를 잠근 채로 리턴해야 한다.
(물론 리턴하기 전, 대기하고 있을 동안은 뮤텍스를 잠그지 않은 상태다.)
■ 뮤텍스를 잠그지 않은 채로 얼마든지 조건변수로 시그널을 보내거나 브로드캐스트 할 수 있
다. 어떤 쓰레드가 조건변수로 시그널을 보내거나 브로드캐스트한다 할지라도 대상 조건 변
수에서 대기 중인 쓰레드가 하나도 없다면 아무일도 일어나지 않는다. 잠시 후 다른 쓰레드
가 pthread_cond_wait() 함수를 호출한다면, 바로 전에 해당 조건 변수로 시그널이 보내졌
다는 것을 알지 못하고 그저 기다리는 수밖에 없다. 따라서 이 시점이 절대로 기다려서는
안되는 상황이라면 쓰레드는 결코 깨어날 수 없게 된다. 쓰레드가 조건변수에서 블럭될 때
까지 뮤텍스는 잠긴 채로 남아 있기 때문에, 술어 검사와 대기도중에는(다른 쓰레드들에 의
해) 술어가 설정될 수 없다. 즉 뮤텍스가 잠겨 있으므로 다른 쓰레드들이 술어 등의 공유
데이타들을 변경할 수 없는 것이다.
■ 항상 술어를 테스트하라! 그리고 다시 한번 테스트하라!
쓰레드가 깨어났을 때, 다시 술어를 테스트하는 것 역시 매우 중요하다. 따라서 조건 변수
대기는 항상 루프를 이용하여 수행하는 것이 좋다.
-- 데이비드 R 부트노프 저 Posix 쓰레드를 이용한 프로그래밍 123P
각 조건변수는 특정 뮤텍스 및 술어 조건과 결합되어야 한다. 또한 쓰레드가 조건 변수상
에서 대기할 때는 항상 결합된 뮤텍스를 잠그고 있어야 한다. 조건변수 대기명령을 사용하
면, 쓰레드를 블록하기 전에 뮤텍스의 잠금을 해제하고 원래 코드로 리턴하기 전에 뮤텍스
를 다시 잠근다는 사실을 기억하자
-- 데이비드 R 부트노프 저 Posix 쓰레드를 이용한 프로그래밍 123P
■ window 에서는 뮤텍스 조건 변수를 구현하기가 대단히 힘든데, 만일 조건 변수의 기능을
특정 쓰레드의 "대기"와 "깨어남" 으로 한정한다면 IOCP 와 GetQueuedCompletionStatus()
함수를 이용하여 구현할 수 있다.
window 에서 조건변수는 이벤트로 구현하는 것이 제일 무난한 것 같다.
-- 2008-12-05 15:06:00
■ 이제 생각해보니 조건변수의 중요 역할은 첫째 "대기" 와 둘째 뮤텍스를 잠근 쓰레드가 아
닌 "다른 쓰레드가 잠금을 해제하는 것" 을 허용하는 것이 아닐까 한다.
-- 2007-11-12 07:56:00
■ "작업쓰레드" 가 조건변수에서 대기하고 있고, "주쓰레드" 에서 "작업쓰레드" 에 시그널을
보내 깨우는 과정을 생각해보자. "주쓰레드" 에서 "작업쓰레드" 에 시그널을 보내면, "작업
쓰레드" 는 뮤텍스에 Lock 을 걸고 대기 함수를 빠져나올려고 할 것이다. 이때 "주쓰레드"
에서 Lock 을 걸고 시그널을 보냈다면, "작업쓰레드" 는 대기 함수를 리턴하기 전에 이미
뮤텍스가 잠겨있으므로, 뮤텍스가 풀릴 때까지 다시 대기하게 된다. 따라서 시그널을 보낸
"주쓰레드" 는 가급적 빨리 Lock 을 풀어주어야 한다. 또한 시그널로 인해 깨어난 "작업쓰
레드"도 뮤텍스에 Lock 을 걸고 대기 함수를 리턴하게 되면 뮤텍스의 Lock 을 빨리 풀어주
는 것이 좋다. -- 2007-11-16 05:51:00
//////////////////////////////////////////////////////////////////////////////////////*/
template< typename TData=ZNsMain::ZCEmpty /////////////
>
class ZtCMutexCond : public TData, ZNsMain::ZCNonCopyable
{
protected:
::pthread_cond_t mh_PThread_Cond;
public :
::pthread_cond_t GetHandle() const
{
return mh_PThread_Cond;
}/*
::pthread_cond_t GetHandle() const*/
int InitCond(pthread_condattr_t* AP_CondAttr=NULL)
{
return ::pthread_cond_init(&mh_PThread_Cond, AP_CondAttr) ;
/*/////////////////////////////////////////////////////////
■ pthread_cond_init 의 에러 반환값(성공시 0)
EAGIN (메모리이외의) 자원이 부족하다.
ENOMEM 메모리가 부족하다.
EBUSY 조건변수가 이미 초기화 되어 있다.
EINVAL AP_CondAttr 이 올바르지 않다.
가급적이면 정적초기화를 사용한다. (PTHREAD_COND_INITIALIZER)
/////////////////////////////////////////////////////////*/
}/*
int Init(pthread_condattr_t* AP_CondAttr=NULL)*/
int FiniCond()
{
/*////////////////////////////////////////////////////////////
■ pthread_cond_destroy()
RETURN VALUES
On success, pthread_cond_destroy() returns 0.
On error, one of the following values is returned:
EINVAL
cond does not refer to an initialized condition variable.
EBUSY
The condition variable is in use by another thread (for example, in a condition wait).
EFAULT
cond is an invalid pointer
■ PTHREAD_COND_INITIALIZER 메크로를 사용하여
정적으로 초기화된 조건변수는 파괴하지 않아도 된다.
////////////////////////////////////////////////////////////*/
return ::pthread_cond_destroy(&mh_PThread_Cond) ;
}/*
int FiniCond()*/
int WaitCond(pthread_mutex_t& AR_MutexArg)
{
return ::pthread_cond_wait(&mh_PThread_Cond, &AR_MutexArg) ;
/*//////////////////////////////////////////////////////////
■ pthread_cond_wait 함수가 제대로 수행되려면 뮤텍스가 초기
화되어 있고 lock 이 되어 있어야 한다. 이 함수가 실행되면
함수는 내부에 사용된 뮤텍스의 lock 을 해제한 채로 대기 작
업에 들어간다. 그리고 조건 변수에 대한 시그널이 검출되면
작업을 재개한다. 즉 해당조건 변수에 대한 시그널이 발생하
면 내부의 인수로 지정된 뮤텍스를 잠근후 pthread_cond_wait
함수의 다음 라인을 실행하게 된다. pthread_cond_timedwait
함수를 사용하면 timespec 에 지정된 시간만큼 대기를 하게
된다. 만약 지정된 시간내에 시그널이 검출되지 않으면 타임
아웃 에러코드와 함께 실행이 중지된다.
//////////////////////////////////////////////////////////*/
}/*
int WaitCond(pthread_mutex_t& AR_MutexArg)*/
int WaitCondTime(pthread_mutex_t& AR_MutexArg, struct timespec& AR_Expriation)
{
// time out 이면 ZNsEnum::ZEThread_TimeOut(ETIMEDOUT) 을 반환한다.
return ::pthread_cond_timedwait(&mh_PThread_Cond, &AR_MutexArg, &AR_Expriation) ;
/*/////////////////////////////////////////////////////
DESCRIPTION
The pthread_cond_timedwait()
function blocks on the specified condition variable,
which atomically releases the specified mutex
and causes the calling thread to block on the condition variable
The blocked thread may be awakened by a call to pthread_cond_signal()
or pthread_cond_broadcast(),
or if the time specified by abstime is reached.
This function atomically releases the mutex,
causing the calling thread to block on the condition variable Upon successful completion,
the mutex is locked and owned by the calling thread.
pthread_cond_timedwait() is the same as pthread_cond_wait(),
except an error is returned
if the system time equals or exceeds the time specified by abstime
before the condition variable is signaled or broadcast,
or if the absolute time specified by abstime has already passed at the time of the call.
When timeouts occur, pthread_cond_timedwait() releases and reacquires the mutex.
When using condition variables,
there should always be a boolean predicate involving shared variables related to each condition wait.
This predicate should become true only
when the thread should proceed.
Because the return from pthread_cond_timedwait() does not indicate anything about the value of this predicate,
the predicate should be reevaluated on return.
Unwanted wakeups from pthread_cond_timedwait() may occur
(since another thread could have obtained the mutex,
changed the state and released the mutex,
prior to this thread obtaining the mutex);
the reevaluation of the predicate ensures consistency.
The pthread_cond_timedwait() function is a cancellation point.
If a cancellation request is acted on while in a condition wait
when the cancellation type of a thread is set to deferred,
the mutex is reacquired before calling the first cancellation cleanup handler.
In other words, the thread is unblocked,
allowed to execute up to the point of returning from the call pthread_cond_timedwait(),
but instead of returning to the caller, it performs the thread cancellation.
--------------------------------------------------------------------------------
PARAMETERS
cond
Is the condition variable to wait on.
mutex
Is the mutex associated with the condition variable.
abstime
Is the absolute time at which the wait is cancelled if not signaled or broadcast.
--------------------------------------------------------------------------------
RETURN VALUES
On success, pthread_cond_timedwait() returns 0.
On error, one of the following values is returned:
EINVAL
cond does not refer to an initialized condition variable,
or mutex does not refer to an initialized mutex.
Different mutexes were specified in multiple waits on cond.
mutex is not owned by the caller.
EFAULT
cond, mutex, or abstime is an invalid pointer.
ETIMEDOUT
The specified absolute time has passed.
--------------------------------------------------------------------------------
CONFORMANCE
POSIX P1003.1 (1996)
--------------------------------------------------------------------------------
MULTITHREAD SAFETY LEVEL
MT-safe.
///////////////////////////////////////////////////////////////////////////////*/
}/*
int WaitCondTime(pthread_mutex_t& AR_MutexArg, struct timespec& AR_Expriation)*/
int WakeCond()
{
return ::pthread_cond_signal(&mh_PThread_Cond);
}/*
int WakeCond()*/
/*///////////////////////////////////////////////////////////////////////////////
■ pthread_cond_broadcast() 함수는 조건 변수에서 대기하는 모든 쓰레드에게 시그널
을 보낸다. 모든 쓰레드가 일어나게 되므로 대개 경합이 발생할 가능성이 있다. 만
일 해야 할 작업이 10 개라면 실제로 일을 가져가는 쓰레드는 1 개일 테고 나머지는
조건을 비교한 뒤에 다시 대기한다. 이렇게 쓸데없이 깨어난 쓰레드들이 다시 대기
상태로 가는 것은 필연적으로 오버헤드를 발생시키게 된다. 따라서 어떤 경우에 브
로드캐스트를 쓸 것인지를 잘 생각해서 디자인해야 한다.
-- 리눅스 시스템 네트워크 프로그래밍, 김선영 저, 가메출판사
///////////////////////////////////////////////////////////////////////////////*/
int BoradCast()
{
return ::pthread_cond_broadcast(&mh_PThread_Cond) ;
}/*
int BoradCast()*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCMutexCond //////////////////////*/
/*///////////////////////////////////////////////////////////////
■ mutex 와 조건 변수를 결합한 클래스다. ZtCMutexCond<> 템플릿과
ZCProcessMutex 등을 적절히 이용하면 되지만 좀더 최적화 하기 위
해서 별도로 구현하는 것이다.
///////////////////////////////////////////////////////////////*/
template< typename TData=ZNsMain::ZCEmpty /////////////////////
>
class ZtCMutexCondData : public TData ///////////////////////////
{
protected:
pthread_mutex_t mh_Mutex;
pthread_cond_t mh_Cond ;
public :
int InitCond(const pthread_mutexattr_t* AP_MutexAttr=0, const pthread_condattr_t* AP_CondAttr=0)
{
#if(_CODE_OLD_)
// 실험용 코드.
bool VB_IsOK1= (::pthread_mutex_init(&mh_Mutex, AP_MutexAttr)==ZNsMain::ZNsEnum::ZEThread_OK);
bool VB_IsOK2= (::pthread_cond_init (&mh_Cond, AP_CondAttr )==ZNsMain::ZNsEnum::ZEThread_OK);
return (VB_IsOK1 && VB_IsOK2) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
#else
return ( ::pthread_mutex_init(&mh_Mutex, AP_MutexAttr)==ZNsMain::ZNsEnum::ZEThread_OK &&
::pthread_cond_init (&mh_Cond, AP_CondAttr )==ZNsMain::ZNsEnum::ZEThread_OK
/*///*/ ) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
#endif
}/*
int Init(const pthread_mutexattr_t* AP_MutexAttr=0, const pthread_condattr_t* AP_CondAttr=0)*/
int Lock()
{
return ::pthread_mutex_lock(&mh_Mutex);
}/*
bool Lock()*/
int TryLock(const timespec* AP_TimeOut)
{
return ::pthread_mutex_trylock(&mh_Mutex);
}/*
int TryLock(const timespec* AP_TimeOut)*/
int UnLock()
{
return ::pthread_mutex_unlock(&mh_Mutex);
}/*
int UnLock()*/
int WaitCond() // 해당 뮤텍스에 lock 이 걸려 있어야 한다.
{
return ::pthread_cond_wait(&mh_Cond, &mh_Mutex) ;
}/*
int WaitCond()*/
#ifdef __USE_XOPEN2K
int Lock(const timespec* AP_TimeOut)
{
return ::pthread_mutex_timedlock(&mh_Mutex, AP_TimeOut);
}/*
int Lock(const timespec* AP_TimeOut)*/
int WaitCondTime(const timespec* AP_TimeOut)
{
return ::pthread_cond_timedwait(&mh_Cond, &mh_Mutex, AP_TimeOut) ;
}/*
int WaitCondTime(const timespec* AP_TimeOut)*/
int WaitCondTime(int AI_TimeOutMili)
{
timespec VO_TimeAbs; // 절대 시간
timeval VO_TimeNow;
/*///////////////////////////////////////////////////////
■ timespec
struct timespec
{
__time_t tv_sec ; // Seconds.
long int tv_nsec; // Nanoseconds. 10 억분의 1 초, 1 밀리초=1000*1000 나노초
};
■ int gettimeofday( struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv,const struct timezone *tz);
-- linux manual 에서
struct timeval
{
long tv_sec; // 초
long tv_usec; // 마이크로초
};
struct timezone
{
int tz_minuteswest; // 그리니치 서측 분차(minutes)
int tz_dsttime; // DST 보정 타입
};
timezone struct 는 사용하지 않는다;
리눅스에서 tz_dsttime 필드가 사용되지 않는다.
지금까지 그렇고 앞으로도 libc 나 glibc에서 지원되지 않을 것이다.
커널 소스에서(선언 이외에) 이 필드가 나오는 모든 경우는 버그이다.
그래서 다음 내용은 순전히 역사적인 흥미거리다.
~~~~~~
gettimeofday와 settimeofday 모두 성공하면 0을 리턴하며,
실패시에는 -1을 리턴한다.(errno는 적당한 값으로 설정된다.)
///////////////////////////////////////////////////////*/
::gettimeofday(&VO_TimeNow, NULL);
VO_TimeAbs.tv_sec = VO_TimeNow.tv_sec + (AI_TimeOutMili/1000) ;
VO_TimeAbs.tv_nsec= VO_TimeNow.tv_usec*1000 + (AI_TimeOutMili%1000)*1000*1000 ;
return ::pthread_cond_timedwait(&mh_Cond, &mh_Mutex, &VO_TimeAbs) ; // return ZEThread_TimeOut if timeout
}/*
int WaitCondTime(int AI_TimeOutMili)*/
#endif //__USE_XOPEN2K
int WakeCond()
{
return ::pthread_cond_signal(&mh_Cond);
}/*
int WakeCond()*/
int WakeAllCond()
{
return ::pthread_cond_broadcast(&mh_Cond);
}/*
int WakeAllCond()*/
int BroadCast()
{
return WakeAllCond();
}/*
int BroadCast()*/
int FiniCond()
{
#if(_CODE_OLD_)
// 실험용 코드.
bool VB_IsOK1= (::pthread_cond_destroy (&mh_Cond )==ZNsMain::ZNsEnum::ZEThread_OK);
bool VB_IsOK2= (::pthread_mutex_destroy(&mh_Mutex)==ZNsMain::ZNsEnum::ZEThread_OK);
return (VB_IsOK1 && VB_IsOK2) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
#else
return ( ::pthread_cond_destroy (&mh_Cond )==ZNsMain::ZNsEnum::ZEThread_OK &&
::pthread_mutex_destroy(&mh_Mutex)==ZNsMain::ZNsEnum::ZEThread_OK
/*///*/ ) ? ZNsMain::ZNsEnum::ZEThread_OK : ZNsMain::ZNsEnum::ZEThread_Invalid ;
#endif
}/*
int FiniCond()*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty /////////////////////
>
class ZtCMutexCondData : public TData ///////////////////*/
/*//////////////////////////////////////////////////////////////////
■ ZtCEventCond<> 템플릿은 Window 의 event 와 같이, 특정 조건에서 대
기하는 모든 쓰레드를 한꺼번에 깨우기 위해서 필요하다.
Linux 의 pthread_cond_signal() 함수는 해당 조건 변수에 대기하고
있는 쓰레드 중 하나를 깨운다. event 효과를 낼려면
pthread_cond_broadcast()
를 사용해야 한다.
단순히 WakeCond() 함수를 WakeAllCond() 함수로 가리고 있다.
-- 2009-10-25 23:05:00
■ 조건 변수에서 다수의 쓰레드가 대기하고 있는데, signal 을 2 회 연속
으로 보낸 경우에, 2 개 쓰레드가 연속적으로 깨어난다고 보장할 수 없
다. signal 1 회 발생시에 context switching 이 일어나서 깨어난 쓰레
드가 무슨 일을 한 다음에, 다시 조건 변수에서 대기한다면, 2 번째
signal 에서 그 쓰레드가 또 깨어날 수 있는 것이다.
-- 2009-10-26 13:18:00
//////////////////////////////////////////////////////////////////*/
template< typename TData=ZNsMain::ZCEmpty ///////
>
class ZtCEventCond : public ZtCMutexCondData<TData>
{
public:
typedef ZtCMutexCondData<TData> ZCMutexCondData;
public:
int WakeCond(){return this->WakeAllCond();}
public:
};/*
template< typename TData=ZNsMain::ZCEmpty ////////
>
class ZtCEventCond : public ZtCMutexCondData<TData>*/
typedef ZtCEventCond<> CEventCond;
#if defined(__USE_XOPEN2K) && !defined(__USE_MUTEX_COND_FOR_BARRIER__)
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCBarrier : public TData ///////////
{
protected:
bool mb_IsValidID;
::pthread_barrier_t mh_BarrierID; // 0 으로 초기화할 수 없다.
public :
ZtCBarrier()
{
mb_IsValidID=false;
}/*
ZtCBarrier()*/
bool IsValid() const{return mb_IsValidID;}
/*/////////////////////////////////////////////////
■ Init() 와 Fini() 의 리턴값을 다룰 때는
if(CBarrierObj.Init(3)==ZNsEnum::ZEBarrier_OK)
이런 식으로 한다.
/////////////////////////////////////////////////*/
int Init(unsigned AI_Count, const ::pthread_barrierattr_t* AP_Attr=0)
{
#if(_CODE_OLD_)
if(mb_IsValidID==true)
{
::pthread_barrier_destroy(&mh_BarrierID); mb_IsValidID=false;
}/*
if(mb_IsValidID==true)*/
#endif // _CODE_OLD_
int VI_Return = ::pthread_barrier_init(&mh_BarrierID, AP_Attr, AI_Count);
if(VI_Return==ZNsEnum::ZEBarrier_OK) mb_IsValidID=true; return VI_Return;
}/*
int Init(unsigned AI_Count, const ::pthread_barrierattr_t* AP_Attr=0)*/
int Fini()
{
if(mb_IsValidID==false) return ZNsEnum::ZEBarrier_OK;
mb_IsValidID=false; return ::pthread_barrier_destroy(&mh_BarrierID);
}/*
int Fini()*/
/*////////////////////////////////////////////////////////////////////////////
int pthread_barrier_wait(pthread_barrier_t *barrier);
RETURN VALUE
Upon successful completion, the pthread_barrier_wait() function shall
return PTHREAD_BARRIER_SERIAL_THREAD for a single (arbitrary) thread
synchronized at the barrier and zero for each of the other threads.
Otherwise, an error number shall be returned to indicate the error.
ERRORS
The pthread_barrier_wait() function may fail if:
EINVAL The value specified by barrier does not refer to an initialized
barrier object.
This function shall not return an error code of [EINTR].
The following sections are informative.
////////////////////////////////////////////////////////////////////////////*/
bool Wait()
{
return ::pthread_barrier_wait(&mh_BarrierID)!=ZNsEnum::ZEThread_Invalid;
}/*
bool Wait()*/
// 아래 2 개의 멤버는 윈도우에는 없다.
bool GetShared(::pthread_barrierattr_t AH_BarrierAttr, int& ARRI_PShared)
{
return ::pthread_barrierattr_getpshared(&AH_BarrierAttr, &ARRI_PShared)==ZNsEnum::ZEThread_OK;
}/*
bool GetShared(::pthread_barrierattr_t AH_BarrierAttr, int& ARRI_PShared)*/
/* cf) PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE
AI_PShared 을 PTHREAD_PROCESS_SHARED 로 지정하는 경우에
mh_BarrierID 은 공유 메모리에 존재해야 한다. */
bool SetShared(::pthread_barrierattr_t AH_BarrierAttr, int AI_PShared=PTHREAD_PROCESS_SHARED)
{
return ::pthread_barrierattr_setpshared(&AH_BarrierAttr, AI_PShared)==ZNsEnum::ZEThread_OK;
}/*
bool SetShared(::pthread_barrierattr_t AH_BarrierAttr, int AI_PShared=PTHREAD_PROCESS_SHARED)*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCBarrier : public TData /////////*/
#else // !defined(__USE_XOPEN2K) || defined(__USE_MUTEX_COND_FOR_BARRIER__)
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCBarrier : public TData ///////////
{
public :
typedef ZtCMutexCondData<> ZCMutexCondData;
protected:
ZCMutexCondData mo_CCondData;
bool mb_IsValidID;
int mi_WaitCount;
public :
ZtCBarrier():mi_WaitCount(0){mb_IsValidID=false;}
bool IsValid() const{return mb_IsValidID;}
/*//////////////////////////////////////////////////
■ Init() 와 Fini() 의 리턴값을 다룰 때는
if(CBarrierObj.Init(3)==ZNsEnum::ZEBarrier_OK)
의 형태로 한다.
//////////////////////////////////////////////////*/
int Init(unsigned AI_Count)
{
if(mb_IsValidID==true)
{
mo_CCondData.FiniCond(); mb_IsValidID=false;
}/*
if(mb_IsValidID==true)*/
mi_WaitCount=AI_Count;
int VI_Return = mo_CCondData.InitCond();
if(VI_Return==ZNsEnum::ZEThread_OK)
{
mb_IsValidID=true; return ZNsEnum::ZEThread_OK;
}/*
if(VI_Return==ZNsEnum::ZEThread_OK)*/
return ZNsEnum::ZEBarrier_NO;
}/*
int Init(unsigned AI_Count)*/
int Fini()
{
if(mb_IsValidID==false) return ZNsEnum::ZEThread_OK;
mb_IsValidID=false;
mi_WaitCount=0 ;
return (mo_CCondData.FiniCond()==ZNsEnum::ZEThread_OK) ?
ZNsEnum::ZEBarrier_OK : ZNsEnum::ZEBarrier_NO ;
}/*
int Fini()*/
bool Wait()
{
bool VB_IsOK=false;
mo_CCondData.Lock();
{
if(--mi_WaitCount<=0)
VB_IsOK=(mo_CCondData.WakeAllCond()==ZNsEnum::ZEThread_OK);
else
VB_IsOK=(mo_CCondData.WaitCond ()==ZNsEnum::ZEThread_OK);
//else
}
mo_CCondData.UnLock();
return VB_IsOK;
}/*
bool Wait()*/
public:
};/*
template< typename TData=ZNsMain::ZCEmpty
>
class ZtCBarrier : public TData /////////*/
#endif // !defined(__USE_XOPEN2K) || defined(__USE_MUTEX_COND_FOR_BARRIER__)
typedef ZCSpinLockEasy ZCFastLockEasy;
}/*
namespace ZNsMain*/
#ifdef __INC_GLIB_ATOMIC_EXCHANGE__
#include <glib/gatomic.h>
namespace ZNsMain
{
/*///////////////////////////////////////////////////////
■ class ZtCAtomicIntSync<>
커널이나 라이브러리의 동기화 object 를 사용하지 않고
동기화를 구현하고 싶은 경우에 사용하는 클래스 템플릿.
volatile gint 에 대한 원자적 연산을 이용하고 있다.
일종의 lock-free 다.
■ int sched_yield(void) : IN <sched.h>
A process can relinquish the processor voluntarily
without blocking by calling sched_yield().
The process will then be moved to the end of the queue
for its static priority and a new process gets to run.
Note: If the current process is the only process
in the highest priority list at that time,
this process will continue
to run after a call to sched_yield().
POSIX systems on which sched_yield() is available define _POSIX_PRIORITY_SCHEDULING in <unistd.h>.
RETURN VALUE
On success, sched_yield() returns 0.
On error, -1 is returned, and errno is set appropriately.
■ 리눅스에서 정수의 atomic 연산 함수는 아래 명령으로 조회할 수 있다.
find /usr/include/ | xargs grep 'atomic' | grep 'add' # or
find /usr/include/ | xargs grep 'atomic' | grep 'increment'
■ memory barrier 관련 코드는 아래와 같다.
[root@localhost ~]# find /usr/include/ | xargs grep -n 'MEM_BARRIER' | more
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:155: _GLIBCXX_READ_MEM_BARRIER;
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:156: _GLIBCXX_WRITE_MEM_BARRIER;
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:175: _GLIBCXX_READ_MEM_BARRIER;
/usr/include/c++/4.1.1/tr1/boost_shared_ptr.h:176: _GLIBCXX_WRITE_MEM_BARRIER;
/usr/include/c++/4.1.1/x86_64-redhat-linux/bits/atomic_word.h:42:// #define _GLIBCXX_READ_MEM_BARRIER __asm __volatile ("":::"memory")
/usr/include/c++/4.1.1/x86_64-redhat-linux/bits/atomic_word.h:46:// #define _GLIBCXX_WRITE_MEM_BARRIER __asm __volatile ("":::"memory")
/usr/include/c++/4.1.1/bits/atomicity.h:53:#ifndef _GLIBCXX_READ_MEM_BARRIER
/usr/include/c++/4.1.1/bits/atomicity.h:54:#define _GLIBCXX_READ_MEM_BARRIER __asm __volatile ("":::"memory")
/usr/include/c++/4.1.1/bits/atomicity.h:56:#ifndef _GLIBCXX_WRITE_MEM_BARRIER
/usr/include/c++/4.1.1/bits/atomicity.h:57:#define _GLIBCXX_WRITE_MEM_BARRIER __asm __volatile ("":::"memory")
■ glib 에 있는 아래 함수를 이용한다. (glib 는 gtk+ 용 라이브러리이다.)
gboolean g_atomic_int_compare_and_exchange
(
volatile gint *atomic,
gint oldval ,
gint newval
);
//gboolean g_atomic_int_compare_and_exchange
Compares oldval with the integer pointed to by atomic and if they are equal,
atomically exchanges *atomic with newval. Also acts as a memory barrier.
Returns : TRUE, if *atomic was equal oldval. FALSE otherwise
□ 아래 함수도 참고한다.
gboolean g_atomic_pointer_compare_and_exchange
(
volatile gpointer *atomic,
gpointer oldval,
gpointer newval
);
//gboolean g_atomic_pointer_compare_and_exchange
■ -- 2010-04-03 18:05:00
■ 리눅스 등에서 ZtCAtomicIntSync<> 이 동기화 object 를 사용해서
(무겁게) 구현되는 경우, ZtCAtomicIntSync<>::TypeSync 타입을 통해서
어떤 동기화 object 를 사용했는지를 나타낼 것이다.
-- 2010-04-17 21:29:00
■ 리눅스에서는 alsa/iatomic.h 에 atomic 관련 함수가 있기는 한데,
glib 의 g_atomic_int_compare_and_exchange() 에 해당하는 함수가 없다.
-- 2011-06-19 22:49:00
■ (리눅스 centos)alsa/iatomic.h 에서 atomic_add(), atomic_sub() 등의 함수를 볼 수 있다.
static __inline__ void atomic_add(int i, atomic_t *v)
{
__asm__ __volatile__(
ATOMIC_SMP_LOCK "addl %1,%0"
:"=m" (v->counter)
:"ir" (i), "m" (v->counter));
}
static __inline__ void atomic_sub(int i, atomic_t *v)
{
__asm__ __volatile__(
ATOMIC_SMP_LOCK "subl %1,%0"
:"=m" (v->counter)
:"ir" (i), "m" (v->counter));
}
-- 2013-06-09 00:16:00
///////////////////////////////////////////////////////*/
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCAtomicIntSync : public TTypeBase
{
public :
typedef ZtCAtomicIntSync TypeSync;
public :
enum{ZEUseAtomicInt=1};
public :
enum ZESync
{
ZESync_Lock =0,
ZESync_UnLock=1
};/*
enum ZESync*/
private:
volatile gint mi_SyncState;
public :
ZtCAtomicIntSync()
{
mi_SyncState=ZESync_UnLock;
}/*
ZtCAtomicIntSync()*/
ZtCAtomicIntSync(const TTypeBase& rhs) : TTypeBase(rhs)
{
mi_SyncState=ZESync_UnLock;
}/*
ZtCAtomicIntSync(const TTypeBase& rhs)*/
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs) : TTypeBase(rhs)
{
mi_SyncState=ZESync_UnLock;
}/*
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)*/
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)
{
this->TTypeBase::operator=(rhs); return *this;
}/*
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)*/
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)
{
this->TTypeBase::operator=(rhs); return *this;
}/*
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)*/
void Lock()
{
#define __INTERLOCKED_COMP_EXCHANGE__ \
::g_atomic_int_compare_and_exchange(&mi_SyncState, ZESync_UnLock, ZESync_Lock)
while(__INTERLOCKED_COMP_EXCHANGE__==FALSE)
{
// 아직 Lock 이 걸려 있는 경우이다.
::sched_yield();
}/*
while(__INTERLOCKED_COMP_EXCHANGE__==FALSE)*/
#undef __INTERLOCKED_COMP_EXCHANGE__
}/*
void Lock()*/
void UnLock()
{
mi_SyncState=ZESync_UnLock;
}/*
void UnLock()*/
public:
};/*
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCAtomicIntSync : public TTypeBase ///*/
}/*
namespace ZNsMain */
#else // !__INC_GLIB_ATOMIC_EXCHANGE__
namespace ZNsMain
{
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCAtomicIntSync : public TTypeBase
{
public :
typedef ZCThreadMutexEasy TypeSync;
public :
enum{ZEUseAtomicInt=0};
private:
TypeSync mo_CSyncEasy;
public :
ZtCAtomicIntSync()
{
}/*
ZtCAtomicIntSync()*/
ZtCAtomicIntSync(const TTypeBase& rhs) : TTypeBase(rhs)
{
}/*
ZtCAtomicIntSync(const TTypeBase& rhs)*/
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs) : TTypeBase(rhs)
{
}/*
ZtCAtomicIntSync(const ZtCAtomicIntSync& rhs)*/
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)
{
}/*
ZtCAtomicIntSync& operator=(const TTypeBase& rhs)*/
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)
{
this->TTypeBase::operator=(rhs); return *this;
}/*
ZtCAtomicIntSync& operator=(const ZtCAtomicIntSync& rhs)*/
void Lock()
{
mo_CSyncEasy.Lock();
}/*
void Lock()*/
void UnLock()
{
mo_CSyncEasy.UnLock();
}/*
void UnLock()*/
public:
};/*
template< typename TTypeBase=ZNsMain::ZCEmpty
>
class ZtCAtomicIntSync : public TTypeBase ///*/
}/*
namespace ZNsMain */
#endif //!__INC_GLIB_ATOMIC_EXCHANGE__
#endif //__ZCPPMAIN__PROCESS_LINUX_H__