반응형

출처
https://serverfault.com/questions/7689/how-do-i-get-rid-of-sockets-in-fin-wait1-state

FIN_WAIT1 상태를 소켓에서 어떻게 제거하나요?

kill이 필요한 프로세스에 의해 차단된 포트가 있습니다. (충돌한 telnet 대몬). 프로세스가 성공적으로 종료되었지만 포트는 여전히 'FIN_WAIT1' 상태입니다. 그것은 나오지 않으며, 그 시간 초과는 '십년'으로 설정된 것 같습니다.

포트를 해제할 수 있는 유일한 방법은 전체 시스템을 재부팅하는 것인데, 이는 당연히 하고 싶지 않은 일입니다.

$ netstat -tulnap | grep FIN_WAIT1 
tcp        0  13937 10.0.0.153:4000         10.0.2.46:2572 

리부팅 없이 블록되지 않도록 포트를 처리하는 방법에 대해 아시는 분 있나요?


9개의 답변 중 1개의 답변

# tcp_max_orphans의 현재 값을 기록
original_value=$(cat /proc/sys/net/ipv4/tcp_max_orphans)

#tcp_max_orphans를 임시로 0 으로 설정
echo 0 > /proc/sys/net/ipv4/tcp_max_orphans

# /var/log/messages 확인
# 그것은 "kernel: TCP: too many of orphaned sockets"를 뱉어낼 것입니다.
# 접속이 없어지는 데 오래 걸리지 않을 것입니다. 

# 이전에 있던 tcp_max_orphans 의 값을 복구합니다.
echo $original_value > /proc/sys/net/ipv4/tcp_max_orphans

# 다음으로 검증합니다.
netstat -an|grep FIN_WAIT1
반응형
반응형

출처 : https://stackoverflow.com/questions/2719017/how-to-set-timeout-on-pythons-socket-recv-method

파이썬 소켓 수신 메소드에서 timeout을 정하는 방법?

저는 파이썬의 소켓 수신 메소드에서 timeout을 정하고 싶습니다. 어떻게 할 수 있을까요?

11개의 답변 중 1개의 답변만 추려냄

일반적인 접근은 timeout이 발생할 때까지 데이터가 접근 가능할 때까지 select()를 사용하는 것입니다. 데이터가 실제로 가능할 때만 recv()를 호출합니다. 안전을 위해, 우리는 recv가 무한으로 block하지 않는 것을 보장하기 위해 non-blocking 모드로 소켓을 설정할 수 있습니다. select()는 한번에 하나 이상의 소켓을 기다리도록 하는데 사용될 수 있습니다.

import select

mysocket.setblocking(0)

ready = select.select([mysocket], [], [], timeout_in_seconds)
if ready[0]:
    data = mysocket.recv(4096)

만약 많은 열려있는 파일 디스크립터가 있다면, poll()select()보다 더 효율적인 대안입니다.

다른 옵션은 socket.settimeout()을 사용하여 소켓의 모든 연산에 timeout을 설정하는 것입니다. 하지만 당신은 다른 답변에서 해당 솔루션을 명시적으로 거부했던 것을 확인하였습니다.

반응형
반응형

출처

https://serverfault.com/questions/7689/how-do-i-get-rid-of-sockets-in-fin-wait1-state


어떻게 소켓의 FIN_WAIT1 상태를 제거할 수 있을까요?

kill하길 원하는 프로세스에 의해 막힌(blocked) 포트가 있습니다. (crash된 텔넷 대몬) 프로세스는 성공적으로 kill하였지만 포트는 아직 'FIN_WAIT1' 상태입니다. 'FIN_WAIT1' 상태에서 나오지 않으면 이 timeout은 '10년'은 갈 거 같습니다.

제가 찾은 그 포트를 비우는 유일한 방법은 전체 기계를 재부팅하는 것입니다. 당연이 이 방법을 사용하고 싶지는 않습니다.

$ netstat -tulnap | grep FIN_WAIT1 tcp 0 13937 10.0.0.153:4000 10.0.2.46:2572 FIN_WAIT1 -

리부팅 없이 어떻게 포트가 막히지 않도록(unblocked) 할 수 있는 지 아는 분 계신가요?

8개의 답변중 1개의 답변만 발췌

# 현재의 tcp_max_orphan의 값이 무엇인지 (origin_value에) 기록합니다. original_value=$(cat /proc/sys/net/ipv4/tcp_max_orphans) # tcp_max_orphans를 임시로 0으로 설정합니다. echo 0 > /proc/sys/net/ipv4/tcp_max_orphans # /var/log/messages를 확인합니다. # "kernel: TCP: too many of orphaned sockets"을 뱉으며 출력할 것입니다. # 연결이 끊어지기까지 얼마 걸리지 않을 것입니다. # 전에 있던 tcp_max_orphans의 값을 복구합니다. echo $original_value > /proc/sys/net/ipv4/tcp_max_orphans # 다음 명령으로 검증합니다. netstat -an|grep FIN_WAIT1

반응형
반응형

※ 일반적인 쓰레드기반 다중 서버...

서버쪽 쓰레드 하나가 하나의 소켓을 전담한다.

서버쪽 쓰레드가 계속해서 일하는 것이기 아니기 때문에 소켓과 쓰레드가 1:1 대응되는 것은 서버에 부담이 된다. 

1. 리소스 부담... 

2. context switching이 빈번하게 발생하기 때문...


※ 소켓의 수는 줄일 수 없다.

하지만, 하나의 쓰레드가 3개의 소켓을 한꺼번에 담당한다면 리소스 부담 감소, context switching 감소...


22-1. Completion Port 입출력 기본 원리

IOCP란?

1. 기본적으로 비동기 입출력 모델이다. Overlapped IO

2. 제한된 쓰레드의 수를 통해서 여러 소켓의 입출력을 담당하게 한다.

3. 컨텍스트 스위칭에 소비되는 시간을 줄이는 모델이다.

4. Overlapped 입출력 모델의 특징과 비동기 Notification 입출력 모델의 특징을 동시에 지닌다.

원래 비동기 Notification 입출력 모델 : 먼저 확인 후 그 다음 입출력한다.

IOCP모델 : 먼저 입출력 한 후 확인을 나중에 한다.


IOCP 구현모델


Completion Port 내부에 큐가 생성된다. 

연결된 소켓이 입출력이 완료되면 Completion Packet을 만든다.

Completion Packet에는 입출력이 완료한 소켓 정보등에 대한 데이터 묶음이다.

이 packet을 Completion Queue에 집어 넣는다.

쓰레드는 큐에서 packet을 꺼내 내용을 처리한다.


IOCP에서 적절한 쓰레드의 수

Response Session : 서버가 클라이언트로 온 요청을 처리하고 전송하기 직전까지 준비과정.

Response Session이 짧은 경우(간단한 연산 결과를 돌려줌) : CPU의 개수를 초과하지 않는다.

Response Session이 긴 경우 : CPU의 개수를 적절히 초과하여 클라이언트의 평균 만족도를 높인다.

※ 실험적으로 판단하여 평가해야 한다.

일반적인 경우 : CPU의 개수를 초과하지 않는다.


22-2. Completion Port 입출력을 위한 기본 단계

1. Completion Port 오브젝트의 생성

HANDLE CreateIoCompletionPort(

HANDLE FileHandle, // 연결 시킬 소켓 핸들

HANDLE ExistingCompletionPort, // 연결시킬 completion port 핸들

ULONG_PTR CompletionKey, // completion key

DWORD NumberOfConcurrentThreads // 동시 실행 가능한 쓰레드의 수

);

실행예)

HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

※ NumberOfConcurrentThreads가 0이면 CPU의 수만큼 허용


2. Completion Port 오브젝트와 소켓의 연결

completionKey : 입출력이 완료되었을 때 Completion Packet에 저장하는 추가적인 데이터

실행예)

CreateIoCompletionPort(소켓핸들, hCompletionPort, (DWORD)PerHandleData, 0);


3. Completion Queue에 들어 있는 패킷 정보 확인

BOOL GetQueuedCompletionStatus(

HANDLE CompletionPort,  // completion port 핸들

LPDWORD lpNumberOfBytes, // 전송된 바이트수

PULONG_PTR lpCompletionKey, // file completion key

LPOVERLAPPED *lpOverlapped, // buffer

DWORD dwMilliseconds // 선택적인 timeout 값

);

2,3,4 인자를 통해 completion packet의 내용을 얻어온다.


22-3 Completion Port 입출력 기반의 서버구현


CreateCompletionPort의 키 : 완료된 소켓의 정보


Completion Port : 완료감지, completion packet 생성 : key, overlapped, 전송 바이트 수로 구성.

WSARecv의 6번째 인자인 LPWSAOVERLAPPED에서 hEvent를 이용한다. ->

수신된 데이터가 존재하는 버퍼 정보를 담음. overlapped 구조체 뿐만 아니라 다른 구조체도 추가 가능. 



반응형
반응형

21-1. Overlapped 입출력의 의미

비중첩 데이터 입출력 모델


중첩된 데이터 입출력 모델

하나의 쓰레드 내에서 여러 개의 입출력이 진행되는 것


21-2. Overlapped 입출력을 위한 기본 단계

1. Overlapped 소켓의 생성

SOCKET WSASocket(

  __in  int af,

  __in  int type,

  __in  int protocol,

  __in  LPWSAPROTOCOL_INFO lpProtocolInfo,

  __in  GROUP g,

  __in  DWORD dwFlags

);

af : address family

type : 소켓 형태

protocol : 사용된 프로토콜(여기까지 socket과 동일)

lpProtocolInfo : 생성될 소켓의 성경을 정의하는 wSAPROTOCOL_INFO 포인터

g : 예약됨.

dwFlags : 소켓 속성을 지정하는 플래그


WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

WSA_FLAG_OVERLAPPED는 Overlapped 입출력이 가능하도록 하는 플래그.


2. 데이터 송수신

※ 다음 함수들은 버퍼에 존재하는 데이터를 모아서 한 번에 전송하고(Gather) 수신된 데이터를 여러 버퍼에 나누어 저장(Scatter)하는 Gather/Scatter 입출력을 한다.


int WSASend(

    SOCKET s,                  // 소켓 핸들

    LPWSABUF lpBuffers,    // WSABUF 구조체 배열의 포인터

    DWORD dwBufferCount, // lpBuffers가 가리키는 배열의 크기

    LPDWORD lpNumberOfBytesSent, // 전송된 바이트 수를 저장하기 위한 포인터

    DWORD dwFlags,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTING lpCompletionRoutine

);

리턴 : 성공시 0, 실패시 SOCKET_ERROR


typedef struct __WSABUF{

    u_long len;

    char FAR *buf;

}WSABUF, FAR* LPWSABUF;


int WSARecv(

    SOCKET s,                  // 소켓 핸들

    LPWSABUF lpBuffers,    // 수신 버퍼 정보를 지니는WSABUF 구조체 배열의 포인터

    DWORD dwBufferCount, // lpBuffers가 가리키는 배열의 크기

    LPDWORD lpNumberOfBytesRecvd, // 전송된 바이트 수를 저장하기 위한 포인터

    DWORD dwFlags,

    LPWSAOVERLAPPED lpOverlapped,

    LPWSAOVERLAPPED_COMPLETION_ROUTING lpCompletionRoutine

);

리턴 : 성공시 0, 실패시 SOCKET_ERROR

lpOverlapped : 중첩된 입출력을 하기 위해 사용.


3. 데이터 송수신 완료 확인

이벤트 커널 오브젝트 기반

CALLBACK 함수 기반


21-3. Event 커널 오브젝트 기반의 Overlapped I/O
이벤트 커널 오브젝트 : 데이터 송수신이 되었는지 확인하는 용도로 사용.

WSACreateEvent
typedef struct _WSAOVERLAPPED{
    DWORD Internal;
    DWORD InternalHigh;
    DWORD Offset;
    DWORD OffsetHigh;
    // 위의 4개 변수는 내부적으로 사용.
    WSAEVENT hEvent;    // 이벤트 핸들의 오브젝트를 저장한다.
}WSAOVERLAPPED, *LPWSAOVERLAPPED;

WSAWaitForMultipleEvents 함수는 이벤트가 시그널 되었는 지 확인한다.
WSAGetOverlappedResult(
    SOCKET s,
    LPWSAOVERLAPPED lpOverlapped, // WSASend 혹은 WSARecv 호출시 전달한 OVERLAPPED 구조체
    LPDWORD lpcbTransfer, // 전송된 바이트 수
    BOOL fWait,    // TRUE 전달 시 입출력 완료시까지 블로킹
    LPDWORD lpdwFlags
);
※ 송수신한 바이트수, 에러가 발생하였는가? 의 결과를 알려준다.

1. 소켓 이벤트와 WSAOVERLAPPED 구조체와 연결하는 방법
event = WSACreateEvent();
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = event;
2. WSAWaitForMultipleEvents 함수 호출
WSAWaitForMultipleEvents(1, &event, TRUE, WSA_INFINITE, FALSE);
3. WSAGetOverlappedResult 함수 호출
WSAGetOverlappedResult(hSocket, &overlapped, &sendBytes, FALSE, NULL);

21-4. Completion Routines 기반의 Overlapped I/O
콜백 함수 : 특정 상황이 되면 시스템에서 의해 호출되는 함수.

LPWSAOVERLAPPED_COMPLETION_ROUTINE : 다음 콜백함수에 대한 포인터
void CALLBACK CompletionROUTINE(
    DWORD dwError,    // 오류 정보 전달 됨.
    DWORD cbTransferred,    // 전송된 바이트 수 전달됨
    LPWSAOVERLAPPED lpOverlapped,    // WSARecv 함수 호출시 전달한 구조체 변수
    DWORD dwFlags
);
콜백함수가 있을지라도 WSAOVERLAPPED 구조체는 전달해 주어야 한다.
그래서 이벤트 커널 오브젝트는 Dummy Object가 된다.

index = WSAWaitForMultipleEvents(1, &event, FALSE, WSA_INFINITE, TRUE);
index가 WAIT_IO_COMPLETION이면 중첩된 입출력이 완료되었다는 뜻.
다른 의미는 오류가 발생함.
반응형
반응형

1. 비동기 Notification의 의미

인터넷 계층

Link계층 : 물리 + 데이터링크

IP계층

TCP/UDP계층 : 송수신 버퍼가 생성된다.

응용프로그램 계층


동기화된 입력 및 출력 함수의 호출


동기화 : 함수의 호출과 행위의 시작, 함수의 리턴과 행위의 종료가 일치함.

데이터 전송 종료 :  Application -> TCP계층으로 데이터를 다 보낸 상황. send함수를 호출하면 출력버퍼로 보내고자 하는 데이터가 다 들어갔을 때의 상황. 

데이터 수신 종료 : TCP -> Application으로 데이터를 다 읽은 상황.


동기화된 데이터 입출력 과정상의 문제점


※ Host B의 버퍼가 꽉 찼을 경우에 (패킷 전송 일시 정지 요청에 의해) Host A는 패킷 전송을 하지 않고 blocking상태에 있게 된다.

※ send함수에서 패킷을 다 보냈을 경우 send함수가 리턴되어 종료된다.


비동기화 Notification

동기화 Notification

1. select 함수를 기반으로 한 서버의 구현

2. 핸들에 변화가 발생해야 리턴한다. select 함수의 리턴 시기와 핸들 변화 시기가 일치(동기)

비동기화 Notification

1. WSAEventSelect 함수를 기반으로 한 서버의 구현

2. 함수의 호출과 동시에 리턴한다. 따라서 핸들의 변화를 확인하기 위한 별도의 과정이 필요하다.


20.2 WSAEventSelect 모델 기반 서버 구현을 위한 API

구현순서


※ 소켓과 소켓을 위한 Event Object(소켓의 상태를 반영)가 쌍을 이룬다. 소켓의 변화가 다양하기 때문에 Event 객체가 따로 필요하다.

1. WSACreateEvent : Event를 편하게 이용할 수 있도록 돕는다.

2. WSAEventSelect

select함수 : 관심을 두고자 하는 핸들을 설정(설정) + 소켓의 변화(알림), 동기화 알림 함수.

WSAEventSelect : 관심을 두고자 하는 하나의 핸들을 설정만 할 수 있음.

3. WSAWaitForMultipleEvents : 소켓의 변화를 확인. nonsignaled에서 signaled로 변화할 때 리턴됨.

4. WSAEnumNetworkEvents : 소켓이 변화된 이유를 확인.


필요한 함수

WSAEVENT WSACreateEvent(void);

리턴 : manual-reset 모드, non-signaled 상태의 이벤트


int WSAEventSelect(

    SOCKET s,                       // 감시 대상이 되는 소켓

    WSAEVENT hEventObject, // 소켓의 변화를 확인하기 위한 이벤트 오브젝트의 핸들.

    long lNetowrkEvents         // 감시하고자 하는 이벤트 종류를 Bit-wise OR(|)로 묶어서 전달

);

리턴 : 성공시 0, 실패시 SOCKET_ERROR


감시하고자 하는 이벤트 종류

 이벤트 의미 
 FD_READ  수신된 데이터가 존재할 경우 
 FD_WRITE 블로킹되지 않고 데이터를 전송할 수 있는 경우 
 FD_OOB OOB 메세지 수신에 대한 이벤트
 FD_ACCEPT 연결 요청이 수신되었을 경우
 FD_CLOSE 연결 종료 요청이 수신되었을 경우


DWORD WSAWaitForMultipleEvents(

    DWORD cEvents,                           // 검사 대상의 핸들 수     

    const WSAEVENT FAR *lphEvents, // 검사 대상의 배열

    BOOL fWaitAll,                              // FALSE일 경우 하나의 배열만 변화가 있어도 리턴. TRUE면 모든 배열의 변화가 있어야 함.

    DWORD dwTimeout,                     // 타임아웃.

    BOOL fAlertable

);

여러 개의 이벤트 핸들(포인터로 배열 전달)이 signaled 상태가 되었는지 감시하는 함수.

리턴 : 실패시 WAIT_FAILED 리턴, 성공시 이벤트 발생 오브젝트 정보 리턴


int WSAEnumNetworkEvents(

    SOCKET s,                                                   // 소켓

    WSAEVENT hEventObject,                             // 이벤트가 발생한 소켓과 연결되어 있는 이벤트 오브젝트 핸들

    LPWSANETWORKEVENTS lpNetworkEvents    // 발생한 이벤트 정보와 오류 정보로 채워진 WSANETWORKEVENTS 구조체 포인터

);

이벤트 핸들이 발생한 이유를 알아내는 데 사용한다. signaled 상태의 이벤트 핸들을 nonsignaled상태로 바꿔주는 기능도 수행한다.

리턴 : 성공시 0, 실패시 SOCKET_ERROR 리턴


발생한 이벤트 정보와 오류 정보로 채워진 WSANETWORKEVENTS 구조체

typedef struct _WSANETWORKEVENTS{

    long lNetworkEvents;

    int    iErrorCode[FD_MAX_EVENTS];

} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;


※ bind와 listen 사이의 소켓에 대해 이벤트를 만든다.

index = WSAWaitForMultipleEvents(sockTotal, hEventArray, FALSE, WSA_INFINITE, FALSE);

index = index - WSA_WAIT_EVENT_0;

여러 개의 소켓에서 변화가 일어났는 지 확인하고 소켓의 index값을 얻는다. 리턴된 index값은 가장 작은 소켓 인덱스 값이다. 

변화가 일어난 소켓이 여러개일 수 있기 때문에 각각을 for문을 통해 확인한다.


반응형
반응형

※ 커널 오브젝트가 생성될 때는 non-signaled 상태에 있다가 종료하는 경우에 signaled상태로 바뀐다.

※ nonsignaled상태이면 WaitForSingleObject가 멈추고 그렇지 않으면 통과(리턴)할 수 있다.


19-1. 쓰레드 동기화 기법의 분류

프로세스 실행의 두 가지 모드

사용자 모드(User Mode)와 커널 모드(Kernel Mode)


user mode : 시스템 리소스를 제외하고 원하는대로 사용가능. 

접근할 수 있는 메모리 공간이 제한되어 있음.

물리적 영역으로 직접 접근이 허용되지 않는다.

사용자 모드 동기화 기법 : CRITICAL_SECTION 오브젝트 사용

장점 : 프로그래밍하기 수월, 속도가 빠르다.

단점 : 커널 모드 동기화 기법에 비해 제한된 기능만 가진다.


kernel mode : 시스템 리소스를 접근할 수 있지만 시스템 콜을 통해서만 운영체제에서 직접 처리한다

시스템의 모든 메모리 영역으로 접근 가능.

시스템 리소스 : 파일, 쓰레드

시스템의 안전성을 보장하기 위해서 제공

커널 모드 동기화 기법 : Event, Semaphore, Mutex (다른 프로세스에서 접근 가능)

장점 : Deadlock 문제를 막을 수 있다. 둘 이상의 프로세스 내에 존재하는 쓰레드 간의 동기화 가능. 

단점 : 실행 속도의 저하가 발생.


19-2. CRITICAL_SECTION

동기화 관련 함수

※ lpCriticalSection : CRITICAL_SECTION 변수의 포인터

CS 초기화 : void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

CS 소유(임계영역에 들어갈 때) : void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

CS 반환(임계영역을 나올 때) : void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

CS 소멸 : void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);


커널 오브젝트의 상태

뮤텍스

signaled 상태 -> 뮤텍스 소유가 가능한 상태

non-signaled 상태 -> 뮤텍스가 이미 가지고 있는(소유되어진) 상태

세마포어

signaled 상태 -> 세마포어 카운트가 0이 아닌 경우.

non-signaled 상태 -> 세마포어 카운트가 0인 경우.


BOOL CloseHandle(HANDLE hObject);

커널 오브젝트 객체를 소멸한다. 참조카운트 1 감소.


19-3. Mutex(Mutual Exclusion)

HANDLE CreateMutex(

    LPSECURITY_ATTRIBUTES lpMutexAttributes,

    BOOL bInitialOwner,

    LPCTSTR lpName

);

뮤텍스 객체를 생성하고 참조카운트 1 증가.

리턴 : 성공시 생성된 Mutex 오브젝트 핸들, 실패시

lpMutexAttributes : 보안설정

bInitialOwner : false는 signal, true는 non-signal상태로

lpName : 객체 이름


※ WaitSingleObject함수는 뮤텍스를 가져오는(lock) 함수

BOOL ReleaseMutex(

    HANDLE hMutex

);

Mutex를 돌려주는(unlock)하는 함수.

리턴 : 성공시 TRUE 실패시 FALSE

non-signaled -> signaled


Mutex를 통한 쓰레드 동기화


19-4. Semaphore

세마포어가 0이 되어야 세마포어 오브젝트 non-signaled 상태가 된다.

HANDLE CreateSemaphore(

    LPSECURITY_ATTRIBTE lpSemaphoreAttributes,

    LONG lInitialCount,

    LONG lMaximumCount,

    LPCTSTR lpName

);

세마포어 객체를 생성하고 참조카운트 1 증가.

리턴 : 성공시 생성된 Mutex 오브젝트 핸들, 실패시

lpMutexAttributes : 보안설정

lInitialCount : 초기 세마포어값.

lMaximumCount : 세마포어의 최대값.

lpName : 객체 이름


※ WaitForSingleObject는 세마포어 값을 감소시킨다. signaled상태 -> nonsignaled상태로

BOOL ReleaseSemaphore(

    HANDLE hSemaphore,

    LONG lReleaseCount,

    LPLONG lpPreviousCount

);

리턴 : 성공시 TRUE, 실패시 FALSE

hSemaphore : 세마포어의 핸들.

lReleaseCount : 세마포어 값을 증가시킬 크기

lpPreviousCount : 이 함수를 호출하기 전에 카운트 값을 저장할 수 있다.


19-5. Event

Event 특징 : auto & manual-reset 모드의 커널 오브젝트의 생성이 가능하다.

※ 대기 중에 있는 쓰레드들을 한꺼번에 깨울 수 있다. (장점)

HANDLE CreateEvent(

    LPSECURITY_ATTRIBUTES lpEventAttributes,

    BOOL bManualReset,

    BOOL bInitialState,

    LPCTSTR lpName

);

이벤트를 생성한다.

리턴 : 성공시 생성된 Event 오브젝트 핸들. 실패시 NULL

lpEventAttributes : 보안설정

bManualReset : TRUE면 manual-reset 모드의 이벤트 생성. 

bInitialState : TRUE면 signaled 상태의 이벤트 생성, FALSE면 non-signaled 상태의 Evenet가 생성

lpName : Event에 이름을 준다.


manual-reset 모드 Event 동기화 원리

1. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); -> 쓰레드 생성하면 대기상태로 생성.

2. SetEvent(hEvent);

3. NumberOfA, NumberOfOthers가 수행됨.

반응형
반응형

18-1 커널 오브젝트(Kernel Objects)

커널 오브젝트 

시스템 리소스의 정보를 담고 있는 데이터 블록(윈도우즈 기반에서만)

시스템(운영체제)이 관리한다.


18-2. 윈도우즈 기반의 쓰레드 생성


#include<process.h>

unsigned long _beginthreadex(

    void *security,

    unsigned stack_size,

    unsigned (*start_address)(void*),

    void *arglist,

    unsigned initflag,

    unsigned *thrdaddr

);


security : 보안에 관련된 설정

stack_size : 쓰레드 생성시 요구되는 스택의 크기. 0이면 default

start_address : 쓰레드에 의해 호출되는 함수의 포인터

arglist : start_address가 가리키는 함수 호출시, 전달할 인자를 지정해 준다.

initflag : 바로 실행상태(0)가 되느냐, 아니면 대기 상태로 들어가느냐 결정하는 요소. 

thrdaddr : 쓰레드 생성시 쓰레드 ID


쓰레드 생성을 위한 라이브러리 설정


※ 윈도우즈도 main thread가 먼저 끝나면 다른 thread들이 실행중이라도 프로그램이 종료가 된다.

※ 윈도우즈는 기본 실행 단위가 thread다. 프로그램을 만들면 main thread(main함수 실행하는 용도)가 생성된다.


커널 오브젝트, 리소스, 핸들과의 관계


※ 프로그램은 파일을 직접 조작하는 것이 아니라 커널 오브젝트에게 원하는 조작을 요청하고 시스템이 파일을 조작하게 된다.


18-3. Signaled & Non-Signaled 커널 오브젝트

커널 오브젝트의 상태

쓰레드 커널 오브젝트의 상태

생성시 : non-signaled 상태로 초기화.

종료시 : signaled 상태로 변경.


커널 오브젝트 상태 확인 함수

#include<windows.h>

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);

리턴 : signaled 상태가 되어 리턴하는 경우 WAIT_OBJECT_0, 타임아웃에서 리턴될 경우 WAIT_TIMEOUT. 오류시 WAIT_FAILED.

hHandle : 상태를 확인할 커널 오브젝트의 핸들을 전달.

dwMilliseconds : 타임아웃을 1/1000초 단위로 설정한다. INFINITE를 전달할 경우 signaled 상태가 되기 전에 절대 리턴하지 않는다.

※ signaled 상태가 될 때까지 main thread가 기다려주면 된다. 리눅스의 JOIN과 비슷한 효과.


커널 오브젝트의 종류

auto-reset mode 커널 오브젝트 : 리턴이 되면서 커널 오브젝트가 non-signaled 상태가 됨.

manual-reset mode 커널 오브젝트 : 리턴이 되어도 커널 오브젝트는 자동으로 상태가 변경되지 않음.

반응형
반응형

17-1. 프로세스(Process)와 쓰레드(Thread)

쓰레드란 무엇인가?

경량화된 프로세스 

동시 실행 가능.


프로세스와 차이점

스택, PC를 포함한 레지스터 복사본, 쓰레드 지역 보관영역을 제외한 나머지 메모리 영역을 공유.

※ fork를 통해 프로세스 복사했다면 부모와 자식 프로세스는 서로 독립적으로 작동한다.

data : 전역변수가 저장되는 공간.

heap : 동적할당을 하는 메모리 공간.

stack : 지역변수를 위해 사용하는 공간.

보다 간단한 컨텍스트 스위칭.

일부 메모리를 공유하므로 쓰레드간 통신이 편리.

(프로그래밍 하는데 부담이 될 수 있음.)


17-2. 쓰레드 생성하기

쓰레드란 무엇인가?

프로세스(LINUX) : 실행되고 있는 일의 단위

쓰레드 : 프로세스 내에서 다시 나누어지는 일의 단위. 프로세스가 존재해야 쓰레드 생성이 가능.


#include<pthread.h>

int pthread_create(pthread_t * thread, pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

쓰레드를 생성한다. nonblocking함수.

리턴 : 성공시 0, 실패시 이외 값.

thread : 생성된 쓰레드의 ID를 저장할 변수의 포인터.

attr : 생성하고자 하는 쓰레드의 특성. 일반적으로 NULL을 전달.

start_routine : 리턴타임이 void*이고 인자도 void*인 함수를 가리키는 포인터

arg : 쓰레드에 의해 호출되는 함수에(start_routine 포인터가 가리키는 함수) 전달하고자 하는 인자 값.


※ main쓰레드가 먼저 죽으면 자식 쓰레드는 끝까지 실행되지 않고 종료될 수 있다.


#include<pthread.h>

int pthread_join(pthread_t th, void **thread_return);

리턴 : 성공시 0, 실패시 이외 값.

th : 인자로 들어오는 ID의 쓰레드가 종료할 때까지 실행을 지연시킨다. blocking 함수.

thread_return : 쓰레드가 종료 시 반환하는 값에 접근할 수 있는 2차원 포인터.


임계영역과 쓰레드에 안전한 함수

임계영역 : 두 개 이상의 쓰레드에 의해서 동시에 실행되면 안 되는 영역.

쓰레드 안전한 함수(Thread safe function) : 임계영역에서 호출이 가능한 함수.

예) gethostbyname(쓰레드 불안전한 함수) vs gethostbyname_r(쓰레드 안전한 함수)


gcc -D_REENTRANT thread2.c -o thread2 -lpthread

-D_REENTRANT : #define _REENTRANT의 뜻. 쓰레드 불안전한 함수를 쓰레드 안전한 함수로 바꿔주는 매크로.

-lpthread : 기본적으로 쓰레드와 링크되어 있지 않기 때문에 링크시키기 위한 옵션.


17-3. 임계영역 & 쓰레드의 문제점
컴퓨터가 덧셈을 하는 기본 원리

※ 임시메모리 = accumulator

두 개의 쓰레드에 의한 덧셈 연산
int i = 10;    // 전역 변수
....
i+=10;

※ A Thread가 끝나기 전에 B Thread가 수행될 수 있다.

※ 3단계에서 i=30; 이 되어야 하지만 i=20;이 되어 버렸다.


17-4. 동기화(Synchronization)

임계영역 : 둘 이상의 쓰레드에 의해서 공유되는 메모리 공간에 접근하는 코드 영역.

동시에 접근 가능한 메모리(전역변수)에 접근해서 연산하는 코드 영역

동기화

1. 공유된 메모리에 둘 이상의 쓰레드가 동시 접근하는 것을 막는 행위.

2. 둘 이상의 쓰레드 실행순서를 컨트롤(control)하는 행위.

동기화 기법 : 뮤텍스, 세마포어


17-5. 뮤텍스(Mutex)

뮤텍스의 정의

Mutual Exclusion의 줄이말로 쓰레드 서로 간에 동시 접근을 허용하지 않겠다는 의미.

pthread_mutext_t 타입의 변수를 가리켜 뮤텍스라고 한다.

일종의 (화장실) 문고리에 해당.

뮤텍스의 기본 원리

임계영역에 들어갈 때 뮤텍스(문고리)를 잠그고 들어간다.

임계영역에서 나올 때 뮤텍스를 풀고 나간다.

뮤텍스를 조작하는 함수들

#include<pthread.h>

초기화 : pthread_mutex_init

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

뮤텍스 소멸 : pthread_mutex_destroy

int pthread_mutex_destroy(pthread_mutex_t *mutex);

뮤텍스 잠금 : pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

뮤텍스 풀기 : pthrea_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex);


뮤텍스 동기화 원리


17-6. 세마포어(Semaphore)

세마포어의 정의

sem_t 타입의 변수를 가리켜 흔히 세마포어라 표현한다.

세마포어의 기본 원리

세마포어는 정수를 지닌다. 정수의 값이 0이면 실행 불가를 의미한다.

세마포어가 1이상이 되면 실행 가능을 의미한다.

세마포어는 0미만이 될 수 없지만 1이상은 될 수 있다.

바이너리 세마포어 : 0과 1의 상태만을 지닌다.

세마포어를 조작하는 함수들

#include<semaphore.h>

세마포어 초기화 : sem_init

int sem_init(sem_t *sem, int pshared, unsigned int value);

value : 초기화하고자 하는 값.

세마포어 소멸 : sem_destroy

int sem_destroy(sem_t * sem);

세마포어 감소 : sem_wait

int sem_wait(sem_t * sem);

세마포어 증가 : sem_post

int sem_post(sem_t * sem);


세마포어 동기화 원리

Thread A는 새로운 데이터를 주고 Thread B는 그 데이터를 받는다.

a->b->a->b 순서가 반복된다.


17-7. 쓰레드 기반 서버 구현하기

채팅 서버의 임무 : 전체 클라이언트에게 메세지 내용을 다 보내야 되기 때문에 클라이언트 소켓을 갱신해야 한다.


pthread_mutex_lock(&mutx);

for(i=0;i<clnt_number;i++)

    write(clnt_socks[i], message, len);

pthread_mutex_unlock(&mutx);

위 소스에서 clnt_socks[i]에서 클라이언트 소켓 파일 디스크립터가 존재한다고 생각하고 write함수를 호출하지만 파일 디스크립터가 사라질 위험이 있기 때문에 mutex를 통해 잠가 놓는다.



반응형
반응형

16-1. 스트림의 분리

입출력 스트림의 분리 

입력을 위한 파일포인터와 출력을 위한 파일 포인터를 독립적으로 유지.

스트림을 분리하는 이유

프로그래밍을 단순하게 한다.

입출력 겸용 스트림의 경우 입출력 작업 전환시 버퍼를 비워야 한다.

일반적인 프로그래밍 방식.

주의할 사항

출력 스트림 종료시 EOF 전송이 된다.

문제점 : fclose함수를 통해 출력 스트림 뿐만 아니라 입력 스트림까지 완전 종료가 발생할 수 있다.

발생 상황 : 디스크립터가 하나고 그걸 통해 입출력(2개)용 FILE포인터를 만들 때 발생.


16-2. 파일 디스트립터의 복사와 스트림의 분리

파일 포인터(FILE*)를 이용한 소켓의 종료과정

fclose 함수의 호출은 파일 디스크립터의 종료로 이어지고, 이는 소켓의 완전종료를 의미한다.


※ 하나의 파일 디스크립터를 기반으로 입출력 파일 포인터 생성시 하나의 파일 포인터만 종료되어도 완전 종료된다.

※ 시스템 리소스(여기서는 소켓)는 가리키는 포인터(여기서는 파일 디스크립터)가 없을 때 종료된다.


하나의 파일 디스크립터를 기반으로 하나의 파일포인터만 생성

파일 디스크립터가 두 개일 경우에는 두 개의 파일 디스크립터가 종료되어야 완전 종료된다.


파일 디스크립터 복사

하나의 파일(혹은 소켓)에 접근할 수 있는 파일 디스크립터를 하나 더 만들어 내는 것.


#include<unistd.h>

int dup(int fildes);

int dup2(int fildes, int fildes2);

리턴 : 성공시 복사된 파일 디스크립터(번호), 실패시 -1

fildes : 복사하고자 원하는 파일 디스크립터

fildes2 : 복사되는 파일 디스크립터의 값을 명시적으로 지정하고 싶을 때 사용.


※ 쓰기 파일 디스크립터를 half close 함으로서 EOF가 전송됨.

fflush(sock);

shutdown(fileno(wstrm), SHUT_WR);    //  1차 종료, EOF 메세지 전송

fclose(wstrm);

반응형

+ Recent posts