2009년 2월 25일 수요일

USB 메모리를 이용한 컴퓨터 문서 동기화 방법

 

출처 : http://mycom.kr/1000

USB 메모리를 이용한 컴퓨터 문서 동기화 방법

회사 업무와 관련된 문서 파일을 USB 메모리에 저장하여 외부에서 작업을 하는 일이 잦은 분들은 컴퓨터에 저장된 원본 파일을 수시로 업데이트(파일을 복사 - 원본위치에 붙여넣기) 하는 일은 상당히 귀찮은 일입니다.
게다가, 작업하는 파일 갯수가 많거나 저장 장소도 여기저기 흩어져 있을 때는 결코 간단한 일이 아니겠지요. 
이럴 때 XP에서 사용할 수 있는 '서류가방' 폴더을 이용한 파일 동기화 방법이 있는데, 오늘은 이 '서류가방'을 이용하여 USB 메모리에 있는 파일과 컴퓨터에 저장된 파일 내용을 한 방에 같은 내용으로 변경하는 것을 알아보겠습니다.
(주 컴퓨터 사용 장소는 회사라고 가정하고 설명하겠습니다.)
* 주의하세요*
편집은 하지만 저장은 다른 이름으로 하고 원본 내용은 바뀌면 안 되는 문서는 절대, 서류가방을 이용하면 안 됩니다.

USB 메모리를 열어서 마우스 우측 버튼을 클릭한 후, 새로만들기 - 서류가방을 차례로 클릭하면 '새 서류 가방' 폴더가 이동식 디스크에 만들어집니다.

자주 사용하는 문서를(원본 파일 위치는 어디에 있든 상관 없지만 가능한 폴더 하나에 같이 저장하는 것이 관리하기 좋겠지요.) 복사해서 USB 메모리에 있는 '새 서류 가방' 폴더에 붙여넣습니다.

3문서 폴더에 있던 문서를 '새 서류 가방' 폴더에 복사했습니다. 이제 USB 메모리를 외부 컴퓨터로 이동하여 '새 서류 가방'에 있는 문서를 열어서 편집하면 됩니다.(파일 이름 변경이나 폴더 이동은 당연히 안 됩니다.)

이제 다시 회사에서 사용하는 컴퓨터에 USB 메모리를 연결하고 '새 서류 가방 폴더'를 엽니다.
파일 한 개씩 업데이트를 해도 되고, 메뉴에서 '서류 가방'을 클릭하여 '모두 업데이트'를 실행하면 자동으로 업데이트를 실행합니다. 참고로 파일을 선택하고 '원본에서 분리'를 실행하면 컴퓨터에 저장된 원본과 연결 고리가 끊어지게 됩니다. 즉, 서로 별개의 파일이 된다는 것입니다.

USB 메모리의 '새 서류 가방' 폴더에서 파일 하나가 편집을 하여 업데이트해야 하는 상태로 표시되고 있습니다.
밑에 그림은 컴퓨터에 저장된 원본 파일입니다. 업데이트 해야할 파일 날짜와 시간을 잘 보세요.

업데이트 창이 나오고 '업데이트' 버튼을 클릭하면 바로 작업이 완료됩니다.(주의: 업데이트를 하고 나면 원본 파일과 복사본 모두 변경되고 돌이킬 수 없습니다.)

이번에는 반대로 원본을 변경했을 때도 메모리에 있는 파일을 최신 내용으로 업데이트 합니다.

이제 USB 메모리에 있는 문서들도 모두 최신 상태이고, 컴퓨터에 저장된 원본들도 모두 최신 내용으로 변경이 완료되었습니다. 위에서 업데이트한 문서가 원본 파일도 날짜와 시간이 바뀐 것을 확인할 수 있습니다.

서류가방을 처음 사용하는 분들은 약간 헷갈릴 수 있으니 연습을 좀 하고나서 사용하는 것이 좋겠습니다.
정리:
내 컴퓨터 열고, USB 메모리(이동식 디스크)- 마우스 우측 버튼 클릭. 메뉴 - 새로만들기 - 서류가방 - 컴퓨터에 저장된 문서 파일을 새로 만든 USB 메모리의 서류가방 폴더에 복사.
USB 메모리를 다른 컴퓨터에 붙여 서류가방 열고 문서 편집 후 저장.
USB 메모리를 다시 원래 컴퓨터에 붙인 후 서류가방 폴더 열고, 메뉴 - 서류가방 - 모두 업데이트 - 완료.

문서 작업을 많이 하는 분들은 써먹을만한 기능입니다.
사실 노트북을 많이 사용하는 분들은 예전부터 사용하던 기능이구요.
서류가방이 좋은 것은 문서 뿐만아니라 일반 파일도 동기화를 해주기 때문에 사용하기에 따라서 상당히 유용한 기능입니다.
댓글 달아주셔서 감사합니다.

2009년 2월 19일 목요일

여행, 그림, 글

 

출처 : 다나와

[인터뷰] 여행과 오기사, 그리고...

등록일 2009.02.19 15:59:12 | 조회수 677

여행하면서 그림을 그리는 일은 누구나 한번쯤 꿈꿔 봤을 것이다. 꿈꾸지만 쉽게 하지 못하는 오랜 기간의 여행과 그림, 거기에 건축까지 즐기는 행복한 남자가 있다. 오기사라는 별명으로 유명한 오영욱(33세)씨다.
전공 이름이 멋있어 연세대 건축학과를 입학하고, 군생활과 전공을 위해 3년간 대기업 건축사에서 일한 오영욱은 건축과는 떼어놓을 수 없는 사람이다. 그런 그가 건축 일을 멈추고 여행을 떠났다. 건축은 나중에 해도 될 일이었다.
현재 그는 네가지의 타이틀을 가지고 있다. ‘깜삐돌리오 언덕에 앉아 그림을 그리다’ ‘오기사 여행을 스케치하다’ ‘오기사 바로셀로나로 떠나다’ 라는 3권의 책을 낸 여행작가, 오기사라는 캐릭터를 가진 삽화가, ‘오기사 디자인’이라는 작업실이 있는 건축가. 뿐만 아니라 ‘행복한 오기사’의 인기 블로그를 운영하는 블로거다. 하고자 하는 일에 매진했을 뿐이라는 ‘행복한 오기사’ 오영욱을 만나 여행에 대한 그의 생각을 들어봤다.

여행을 시작하게 된 계기는
긴 여행은 나의 오랜 꿈이었다. 건축회사를 다니기 전부터 ‘3년 후 여행을 가야겠다’고 마음 먹었다. 회사를 그만 두는 시점에서 고민도 했지만, 지금 아니면 할 수 없다는 생각에 바로 배낭을 멨다. 언젠가 한번 사주를 봤는데 ‘역마살’이 있다고 하더라. 그래서 여행가가 되었는지 모르겠다.(웃음)

▲  여행을 다니며 스케치하는 오영욱은 삐뚤 한 선으로 깔끔한 그림을 그려낸다.

하지만 여행에 큰 의미를 두지 않는다. 떠나는 장소에 보는 풍경, 그것을 스케치하고 그 속에서 만나는 사람들이 좋을 뿐이다. 바삐 여행을 다니면서 의미를 부여할 만큼 부지런하지 않다. 나는 ‘뒹굴뒹굴’이라는 표현이 어울릴 만큼 게으르다. 단지 즉흥적일 뿐이다. 얼마 전 설 연휴에 다녀온 베트남 여행도 즉흥적이었다. 따뜻한 공기가 마시고 싶어 인터넷에서 항공기표를 찾아봤고, 베트남 행 표가 한자리 남아 여행을 떠났다.

오기사의 단짝 여행 동반자는?

▲ 여행을 떠날 때챙기는 도화지와 펜, USB메모리, 카메라, MSIG-SU2+SEASON2 외장하드. 아이리버 클릭스(4GB)MP3플레이어는 최근 측근에게 선물 받은 것이다.

가방은 최대한 가볍게 하고 다닌다. 스케치를 위한 스케치용 도화지와 얇은 펜, 카메라, USB메모리 등 몇 가지의 물건만 챙긴다. 현지의 모습을 그때그때 스케치하고 사진과 스케치를 조합하기 위한 필요 물품들뿐 이다. 오랜 기간 체류할 경우 노트북과 외장하드를 챙기지만 보통은 가지고 다니지 않는다. 현지에서도 인터넷을 사용할 수 있기 때문이다.

64MB의 USB메모리는 파일을 담기 위한 용도보다 공인인증서 용으로 사용한다. 통통한 디자인에 메모리 사양도 지금 출시되는 것에 비해 턱없이 낮지만 손 때가 묻어 애장하고 있다. (사진: ‘SELFDISK ELEGANCE POR’ USB메모리 64MB)

카메라는 현재 2개를 가지고 있다. 파나소닉 카메라는 케이스가 예뻐 구입했는데 사용하기 불편하다. 그래서 2주 전 베트남 여행을 위해 콤팩트 형식의 캐논 카메라를 구입했다.

▲ 갈색 디자인의 케이스가 마음에 들어 구입한

‘파나소닉 DMC-FX30GD’카메라와 최근에 구입한 ‘캐논 LXUS 980IS’카메라

여행저서에 보면 독특한 사진과 삽화가 눈길을 끄는데
여러 사진의 컷을 하나로 모아 보는 일은 건축가에게 익숙한 작업이다. 건축일을 하면서 자주 사용한 이 기법을 여행사진에 이용하면 좋겠다고 생각했다. 지금은 그러한 작업을 지원하는 프로그램이 따로 나왔다. 하지만 나는 포토샵으로 여러 컷의 사진을 모아 붙일는 수 있는 현재의 작업이 더 좋다.

스케치와 캐릭터를 사용한 것도 건축 일을 하면서 구체화 됐다. 여행하면서 그려내는 스케치는 언제부턴가 나만의 스타일을 갖게 했다.
안전모를 쓴 캐릭터는 예전에 건축사로 일하면서 틈틈이 그리기 시작한 것이다. 빨간색의 안전모는 책을 출간하면서 출판 편집자들의 의견에 따라 디자인됐다. 내가 봐도 예쁘더라. 나의 스케치와 캐릭터 조합을 잘해 준 편집자 분들에게 감사할 뿐이다.

누구나 여행을 꿈꾸지만 쉽게 하지 못하는 것이 여행이다
그런가? 나는 솔직히 여행을 어렵게 생각하지 않았다. 이전부터 하고 싶었던 것이었고 지금 해야겠다고 생각했다. 물론 여행을 하면서 작가가 될 것이라는 것도 건축 일이 언제 시작하게 될 것이라는 것도 예상하지 못했다.

어떤 것을 꿈꿀 때 해야겠다고 마음 먹는 것이 중요하다고 생각한다. 내가 이룬 지금의 모든 것들도 하고 싶은 것을 해야겠다고 마음먹고 그것을 놓지 않았기 때문이다.
앞으로 이루고 싶은 것이 있다면
갖고 싶었던 커피머신기와 작업공간, 아담한 2인승 자동차 ‘스마트’까지 작년 한해 이루고 싶은 것들을 2008년 한해 다 이뤘다. 소비 욕구를 다 채웠다. 여행과 책 출간, 사무실 운영까지 바쁜 한 해를 보내서인지 당장은 지금 하는 일들을 계속해서 해나가는 것 이외에는 이루고 싶은 것이 없다. 순간 여행을 즐기고, 잘 되든 잘 되지 않든 사무실을 오래도록 즐겁게 꾸려가는 것이다.

글/ 다나와 정소라 기자 ssora7@danawa.com

편집/ 다나와 신성철 multic00@danawa.com

2009년 2월 18일 수요일

여기 병신 하나 추가요~~ ^^

 

fun_1201_769703_1

“인류와 외계의 교감”???

내셔날 지오 그래피 표지

모델은 영국 교수 T.Drink pepsitogether 의 딸이라고 하는데, 잘 모르겠다.

코스프레 같은 포즈 같지만 나름 심호한 주제를 표현 하고 있는 것이니 삐딱하게 생각 하지 말자.

그래도 … 웬지 손끝에서 “마광 광살포”가 발사 될것만 같다. ^^

혹시 에바의 “레이”를 떠올렸다면 당신은 진정한 덕후~~ ^^

 

상상이 지나친 김에 웃기는 사진 하나.

풋기옥

DC 주갤에서 퍼왔는데 볼수록 웃긴다. 풋기옥… ㅋㅋㅋㅋㅋ

2009년 2월 17일 화요일

코드 최적화와 관련한 문제

 

문제 1 : a가 3이면 "a == 3"을 출력하고, a가 3이 아니면 "a != 3"을 출력하라.

방법 1: 줄수를 줄이기
방법 2: printf()함수 호출 줄이기

일반적( 50점 ) :

if( a == 3 ) printf( "a == 3" );
else printf( "a != 3" );

줄 수 줄이기( 75점 ) :

( ( a == 3 ) ? printf( "a == 3" ) : printf( "a != 3" ) ); /* 3항 연산자 사용 */

함수 호출 줄이기( 100점 ) :

printf( ( ( a == 3 ) ? "a == 3" : "a != 3" ) ); /* printf()함수를 한 번만 사용 */

문제 2 : a가 짝수이면, "a는 짝수"을 출력하고, a가 홀수이면, "a는 홀수"를 출력하라. ( 단 printf함수는 한 번만 호출하며 되도록 3항연산자와 %s는 쓰지 말 것. if문 맘껏 쓰십시오. )

점수:

3항 연산자 사용 50점

%s 이용 75점

3항 연산자와 %s안 쓰면 100점

짝수 비교 최적화 +10점

 

나머지 이용(100점)

char *dd[] = { "짝", "홀" };
printf(dd[a%2], a);

최적화 이용(110점)

char *dd[] = {"짝","홀"};
printf(dd[a&1], a);

 

최적화 했을때 어셈 코드 수 비교

a%2로 했을때
        movl    %edx, %eax
        sarl    $31, %eax
        shrl    $31, %eax
        leal    (%eax,%edx), %eax
        sarl    %eax
        addl    %eax, %eax
        subl    %eax, %edx
        movl    %edx, %eax


a&1로 바꿨을때는
        andl    $1, %eax

참고로 최적화 이용 코드는 퀘이크 제작자가 풀었다고 하는데… 존카멕인가? 게임 엔진 제작에 천재로 불리는 사람, 그가 만든게 언리얼 엔진이고… 정말 gee(ㅎㄷㄷ) 하다. ^^

하여간 별나라 사람의 이야기고 나에겐 %, &의 쓰임새를 새롭게 보게 됬다.

관계형 연산자( a&1 )

 

# 관계형 연산자.

관계형 연산자는 ..보타 크다, ..보다 작다, ..와 같다, ..와 같지 않다

등등.. 어떤 데이터들을 비교할때 쓰이는 연산자 입니다.

주로 제어 구조, 특히 if문에서 쓰이지만 꼭 그런것은 아님니다.

우선 관계형 연산자에는 어떤것이 있는지 알아보죠.

관계형 연산자에는

------------------------------
== ..와 같다
> ..보다 크다
< ..보다 작다
>= ..보다 크거나 같다.
<= ..보다 작거나 같다.
!= ..와 다르다.
------------------------------


다음과 같은 것들이 있습니다.



그럼 각 연산자들의 이해를 돕기 위해 다음 표를 보시기 바랍니다.



-----------------------------------
a == b a와 b가 같다
a > b a가 b보다 크다
a < b a가 b보다 작다
a >= b a가 b보다 크거나 같다.
a <= b a가 b보다 작거나 같다.
a != b a와 b가 다르다.
-----------------------------------


이걸 보시면 이해가 되실겁니다.



그런데 대부분의 연산자들은 어떤 값을 돌려주죠.



예를 들어 +연산자는 두개의 값을 더해 돌려 주죠.



그러면 이 관계형 연산자는 무엇을 돌려 줄까요?



돌려주는 값은 0과 1중 한가지 입니다.



관계형 연산자가 쓰인 수식을 보면 어떤 조건을 나타내고 있음을 알게되실 겁니다.



그 수식이 참이면 1을 돌려주고, 거짓이면 0을 돌려주죠.



예를 들어



a=1==2;



이런 문장이 있다고 합시다.



그럼 a에는 어떤 값이 들어갈까요?



'1과 2는 같다' <- 이건 거짓이므로 a에는 0이 들어가겠죠?



# 논리 연산자.



논리 연산자는 아까 관계 연산자의 수식 두가지를



논리적으로 연관시키는 연산자 입니다.



쉽게 말하지면 관계형 연산자로 조건이 만들어 지는데



이런 조건 두개가 동시에 만족해야할 경우가 있다고 합시다.



그럴 경우 이 논리 연산자를 쓰면 됨니다.



논리 연산자에는 다음과 같은 것들이 있습니다.



--------------
&& AND
|| OR
! NOT
--------------


각각의 의미는 위에 쓰여진 그대로인데



이걸 보세요



그런데 여기서 a와 b는 관계형 연산자로 만든



조건이라고 합시다.



----------------------------
a && b a와 b모두 참일때
a || b a또는 b가 참일때
! a a의 반대
----------------------------


a && b의 경우 a라는 조건과 b라는 조건이 모두 참일때 1을 돌려주고.



하나라도 거짓이면 0을 돌려 주죠



a || b의 경우는 둘 중 하나만 참이면 1을 모두 거짓일때만 0을 돌려주죠.



! a의 경우 반대로 되는데



즉 참이면 0을 거짓이면 1을 돌려줌니다.



만약 다음과 같은 문장이 있다고 합시다



a=(2>1)&&(3>1);



이럴경우 a에는 어떤 값이 들어갈까요?



당연히 1이 들어가죠



하지만



a=(1>5)&&(3>1);



이럴땐 0이 들어가겠죠??



# 비트별 연산자



비트별 연산자는 비트단위 연산을 할때 쓰이는 연산자 입니다.



먼저 비트별 연산자의 종류를 알아보죠.



비트별 연산자에는 다음과 같은 것 들이 있습니다.



------------------------------
& AND (비트별 논리곱)
| OR (비트별 논리합)
^ XOR (비트별 배타 논리합)
~ 1의 보수
<< 왼쪽으로 쉬프트
>> 오른쪽으로 쉬프트
------------------------------


다음과 같은 것들이 있는데 하나씩 자세히 알아보죠.



(1) 비트별 논리곱



&는 비트 단위로 AND연산을 하는 건데,



연산하려는 두 개의 비트가 모두 1일때만 결과가 1이 되고



하나라도 0이면 결과는 0이 됨니다.



정리하면



------------------------------------
첫번째 비트 두번째 비트 결과
------------------------------------
1 & 1 1
1 & 0 0
0 & 1 0
0 & 0 0
-----------------------------------


이렇게 되죠



한가지 문제를 드리죠



0x0F & 0xFF = ?



알아맞춰 보세요.



답은 0x0F죠.



0x0F는 이진수로 00001111입니다. 0xFF는 11111111이죠.



그런데 &연산자는 모두 1일때만 1을 돌려 줌니다.



00001111과 11111111에서 모두 1인 부분은 뒤쪽 4개의 비트죠.



그러므로 결과는 00001111



이걸 16진수로 고치면 0x0F가 되죠.



다시 정리하면



-----------------------------
0x0F = 00001111
0xFF = 11111111
---------- (& 연산)
00001111 = 0x0F
-----------------------------


이해가 되시죠?



(2) 비트별 논리합



다음으로 |연산자에 대해 자세히 알아보죠.



|는 OR연산을 하는 것으로 &와는 달리 두개의 비트중 1개라도 1이면



1을 돌려주는 연산자 입니다.





------------------------------------
첫번째 비트 두번째 비트 결과
------------------------------------
1 | 1 1
1 | 0 1
0 | 1 1
0 | 0 0
------------------------------------


이런 연산을 하는 연산자 입니다.



그럼 이것두 문제를 드릴까요?



아까처럼 0x0F와 0xFF를 쓰도록 하죠



그런데 이 두개를 OR연산 하면 어떤 결과가 나올까요?



당연히 0xFF죠.



이유는 하나라도 1이면 1을 돌려 주므로.



-----------------------------
0x0F = 00001111
0xFF = 11111111
---------- (| 연산)
11111111 = 0xFF
-----------------------------


이렇게 되기 때문이죠.



(3) 비트별 배타 논리합



다음으로 ^연산자에 대해 알아보죠.



^는 비트단위 배타 OR연산 즉 비트단위 XOR연산을 하는 연산자 입니다.



이 연산자는 OR비슷하지만, 다른점은 두개의 비트가 모두 1일때는



0을 돌려준다는 것이죠.



OR에서는 1을 돌려주지만



즉..



------------------------------------
첫번째 비트 두번째 비트 결과
------------------------------------
1 ^ 1 0
1 ^ 0 1
0 ^ 1 1
0 ^ & 0
------------------------------------


다음과 같은 연산을 하게 됨니다.



그럼 이번엔 0x0F와 0xFF를 XOR연산 하면 어떤 결과가 나올까요?



이건



-----------------------------
0x0F = 00001111
0xFF = 11111111
---------- (| 연산)
11110000 = 0xF0
-----------------------------


이렇게 해서 0xF0이 나오게 됨니다.



(4) 1의 보수



다음으로 ^연산자에 대해 알아보죠.



^는 1의 보수를 구하는 연산자로 그냥 쉽게 비트들을 반대로



즉 0이면 1로 1이면 0으로 바꿔버린다고 알고계시면 됨니다.



-----------------------
비트 결과
-----------------------
~ 0 1
~ 1 0
-----------------------


이런 연산을 하는 것이죠.



1의 보수 연산을 잘 쓰면 좋은점이 많이 있습니다.



만약 여러분이 unsigned long가 가질수 있는 가장 큰 값을 써야 할 경우



어떻게 할까요?



unsigned long는 4294967296까지 넣을수 있는데 이걸 직접 써야 할까요?



그렇게 해도 되고 좀더 쉬운 방법으로 0xFFFFFFFF라고 써도 되겠지만



더 쉬운 방법은 ~0이라고 쓰는 것 입니다.



0은 모든 비트가 0인데 이걸 1의 보수 연산자로



~0이라고 쓰면 모든 비트가 1이 되므로 가장 큰값이 되는 것이죠.



(5) 쉬프트



다음으로 쉬프트 연산자인 <<와 >>에 대해 알아보죠



<<,>>는 지정된 방향으로 지정되 수 만큼 비트를 이동시키는 것 입니다.



예를 들어



?? = 0xF0 >> 4;



이렇게 하면 0xF0 즉 11110000을 >>방향으로 4만큼 이동시키는 것이죠



그래서 결과는 00001111 즉 0x0F가 됨니다.



만약 1이 오른쪽 끝까지 갔다면 그냥 없어집니다.



그리고 왼쪽은 0으로 계속 채워지구요.



<<도 이것과 똑같으나 방향만 다를 뿐 입니다.



이것으로 비트별 연산자는 마치도록 하죠.



# 기타 연산자.



다음으로 기타 연산자들을 배워 보기로 하겠습니다.



여기서 배울 것에는 조건 연산자, 증감 연산자, 그리고 sizeof연산자와,



혼합 연산자가 있습니다.



(1) 조건 연산자



조건 연산자는 주어진 조건에 따라 어떤 수식을 실행하고



그 결과를 돌려주는 연산자 입니다.



사용법은



조건 ? 수식1 : 수식2



이런 식으로 사용하죠.



여기서 조건은 관계형 연산자로 만든 조건입니다.



여기서 만약 조건이 참이면 수식1을 실행한 후 그 결과를 돌려주고,



거짓이면 수식2를 실행하는 것 입니다.



예를 들어 보죠.



a = 1>3 ? 1+3 : 2+6;



여기서 a에 들어가는 값은?



1>3이란 조건은 거짓이므로 두번째 수식인 2+6이 실행되고



결과로 8을 돌려주므로 a는 8이 되겠죠.



이 문장은



1>3 ? a=1+3 : a=2+6;



이렇게 써도 상관 없습니다. =이 들어가도 수식이므로



(2) 증감 연산자



이번엔 증감 연산자에 대해 알아보죠.



증감 연산자는 어떠 변수에 1을 더하거나 빼 주는 연산자이죠



연산자에는



---------------------
++ 1을 증가시킴
-- 1을 감소시킴
---------------------


이렇게 두 가지가 있습니다.



이 두 연산자는 변수 앞이나 뒤에 붙여 사용하면 되는데



앞에 붙일때와 뒤에 붙일때의 차이점은 일단 나중으로 넘기고



예를 들어보죠.



만약



a++;



이렇게 하면 a는 1이 증가하게 됨니다.



즉 a=a+1;가 되는 것이죠.



그리고



a--;



이렇게 하면 a=a-1;이 되는 것 이고요.



그럼 앞에 붙이느냐 뒤에 붙이느냐에 차이를 알아보죠.



이 차이가 나는 곳은 바로 이 연산자가 하나로만 쓰이지 않고



여러개의 연산자와 같이 쓰였을때 차이가 남니다.



이 연산자를 앞에 붙이면



이 연산이 가장 먼저 수행되고 다른 연산이 수행됨니다.



하지만 이 연산자를 뒤에 붙이면 다른 모든 연산을 수행하고



이 연산을 수행하게 되죠.



예를 들어



int a,b=1;
a = 6 - ++b;


이렇게 했을때는 앞에 붙었으므로



우선 b를 1 증가시켜 2로 만든 후 6에서 빼 a에는 4가 들어가지만



int a,b=1;
a = 6 - b++;


이럴경우 우선 다른 연산부터 해서 a에는 5가 들어가고



마지막으로 b가 1이 증가되어 2가 되게 됨니다.



(3) sizeof연산자



이번엔 sizeof연산자에 대해 알아보죠.



sizeof는 어떤 데이터 타입이나 변수, 또는 상수의 크기를



바이트 단위로 알아내는 연산자 입니다.



사용법은



sizeof(데이터타입) 또는
sizeof(데이터)


예를 들어



a=sizeof(char)



이렇게 할 경우 char형태는 1바이트 이므로



a는 1이 됨니다.



그리고



char c;
a=sizeof(c);


이렇게 해도 1이 되죠



a=sizeof(long);



이렇게 하면 4를 돌려 주겠죠?



그리고 만약 배열이라면 배열 크기를 바이트 수로 돌려줌니다.



예를 들어



int array[10];
a=sizeof(array);


이렇게 할 경우



int는 2바이트 이고 10개의 배열이므로



2*10=20이므로 20을 돌려줌니다.



(4) 혼합 연산자.



혼합 연산자는 말 그대로 두개의 연산자를 혼합한 것 입니다.



프로그램을 만들다 보면



a=a+3;
a=a-2;
a=a*3;
a=a/2;
a=a|0x0F;
a=a&0x0A;
a=a>>4;


이런 수식처럼 한개의 데이터 여기선 a가 겹치는 수식을 쓸 때가 있습니다.



이럴때 더 간단히 쓸 수가 있습니다.



바로 혼합 연산자를 쓰면 되는데 혼합 연산자의 종류에는



+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=



이런 것들이 있습니다.



각각의 기능을 살펴보면



------------------------------
a += b -> a = a + b
a -= b -> a = a - b
a *= b -> a = a * b
a /= b -> a = a / b
a %= b -> a = a % b
a &= b -> a = a & b
a |= b -> a = a | b
a ^= b -> a = a ^ b
a <<= b -> a = a << b
a >>= b -> a = a >> b
------------------------------


이런 기능을 합니다.



이렇게 혼합 연산자를 잘 쓰면 수식을 더 간단히 쓸수 있죠.

IOCP 참고 문서

 

출처 : 미상(잡지연재분으로 기억)

IOCP- 윈속 프로그래밍   2002년 08월 21일 | 03시 05분 

이 글은 제가 얼마 전에 프로그램 세계에 연재했던 글입니다. 다음 3회 연재 글 중에서 마지막 회에 해당합니다. 1,2회에 해당하는 글은 이 책에서 찾아보실 수 있습니다.

2002/2 - 1. 윈속이란 ? - 간단한 에코우 서버/클라이언트 프로그램 만들기
2002/4 - 2. 멀티스레드 윈속 서버 프로그램으로 업그레이드 하기
2002/5 - 3. IOCP 윈속 서버 프로그램
이번 회에는 지난 회에서 멀티스레드 윈속 서버 프로그램을 IOCP(Input Output Completion Port)를 이용하는 것으로 변경해보도록 하자. 전에 서버 프로그래밍에 관한 필자의 연재기사에서 수차례 IOCP를 언급한 바 있었는데 이제서야 설명을 하게 되었다.

지난 회에 만들어본 멀티스레드 윈속 서버 프로그램의 문제점은 사용자의 수가 많아지면 스레드의 동적 생성과 스레드간의 잦은 컨텍스트 스위칭으로 인한 오버헤드가 크다는 점이었다. 이러한 점을 극복하기 위해 도입된 것이 바로 IOCP이다. 방금 설명한 것처럼 이는 멀티스레드 프로그래밍에서 유용하게 사용할 수 있으며 그 중에서도 소켓이나 파일, 메일슬롯, 파이프와 같은 입출력 관련 프로그램에서 유용하게 사용할 수 있다.

필자는 IOCP를 파일 I/O가 많은 응용프로그램과 네트웍 I/O가 많은 윈속 프로그램에서 사용해봤는데 그냥 단순한 멀티스레드 프로그램을 작성하는 것보다 괜찮은 성능을 가짐을 알 수 있었다. 부하가 그리 크지 않다면 IOCP를 사용하나 사용하지 않으나 성능상에 큰 차이가 없다. 하지만 부하가 클 경우에는 (예를 들어 윈속 서버 프로그램이라면 현재 접속 사용자수가 많을 경우에는) 상당한 차이를 가져온다는 점을 잘 새겨두기 바란다. 하지만 파일 I/O가 아주 빈번한 응용프로그램에서는 IOCP를 사용한 멀티스레드 프로그램이나 그냥 멀티스레드 프로그램이나 성능에 있어 별 차이가 없다. 그 이유는 스레드로 인한 오버헤드보다 파일 I/O 자체로 인한 오버헤드가 더 크기 때문이었다.

단, IOCP가 무슨 마법처럼 시스템이 가진 하드웨어 제약조건 이상으로 많은 수의 사용자를 처리할 수 있도록 해주거나 하는 것은 아니란 점을 명심하기 바란다. 부하가 많은 시점에 그냥 멀티스레드 프로그래밍을 하는 것보다 더 좋은 성능을 보일 뿐이다. 획기적으로 좋은 성능을 보이거나 하는 마술과 같은 것은 아니란 것이다. 또한 IOCP는 NT 4.0, 2000, XP에서만 사용가능하다는 점도 알아두기 바란다.

먼저 IOCP라는 것이 그리 이해하기 쉬운 편은 아니고 이해해서 사용하기는 더욱 어렵다는 점을 밝히고 싶다. 겁먹으라고 하는 소리는 아니고 잘 이해가 안되어도 필자 탓을 하거나 자신의 머리탓(?)을 하지말고 한번 더 읽어보라는 의미에서 하는 말이다. 참고문헌 2>와 3>에 필자가 처음 IOCP를 공부할 때 봤던 책과 인터넷 기사를 적어두었다. 참고하기 바란다. 또, 마이크로소프트 플랫폼 SDK의 예제 프로그램 중에 보면 윈속에서 IOCP를 어떻게 사용할 수 있는지 보여주는 간단한 예가 있다. 참고문헌 4에 적었다. 사실 이번 연재에서 작성한 서버 예제 프로그램도 이 것을 바탕으로 작성되었다. 클라이언트 예제 프로그램은 사실 지난 회와 동일하다. 그렇기 때문에 클라이언트 프로그램에 대해서는 다루지 않겠다.

1. IOCP의 필요성 ?
IOCP가 왜 필요한지 알아보려면 기존 멀티스레드 프로그래밍의 제한점을 먼저 이해해야 한다.

많은 수의 스레드 생성으로 인한 오버헤드 : 확장성의 제한

동시에 여러 사용자를 처리할 수 없는 프로그램을 서버 프로그램이라고 부를 수 없을 것이다. 서버 프로그램이 되려면 동시에 여러 사용자로부터의 요구를 처리할 수 있어야 하고 그렇게 하기 위해서 스레드를 사용할 수 밖에 없다. 결론적으로 진정한 다중 사용자용 서버 프로그램을 짜본 사람이라면 동시 사용자 처리를 위해 누구나 스레드를 사용하고 있을 것이다. 대부분의 경우 지난 회에 살펴본 예제 프로그램처럼 현재 접속 중인 사용자의 수만큼 스레드를 만드는 방식을 취하게 된다. 즉 사용자마다 그 요구를 처리하기 위한 전담 스레드를 생성하는 것이다.

하지만 이 방식의 문제점 중의 하나는 바로 현재 접속 중인 사용자의 수가 늘어날 경우에 발생한다. 스레드의 생성은 당연히 자원의 사용을 가져온다. 어느 수 이상으로 스레드가 생성되면 프로그램의 성능이 오히려 전체적으로 저하된다. 이유는 너무 많은 스레드가 생성되면 아무래도 그로 인해 자원이 많이 필요하게 되고 또 그 많은 스레드들간의 컨텍스트 스위칭으로 인해 실제 CPU가 어떤 일을 하는 시간보다 컨텍스트 스위칭하는데 상당한 시간을 보내게 되기 때문이다. 예를 들어 CPU의 수보다 스레드의 수가 많다면 사실 스레드간의 컨텍스트 스위칭으로 인한 오버헤드가 있다고 볼 수 있다. (사실 CPU의 수만큼 스레드의 수가 존재하는 것이 이상적이지만 이는 사실상 불가능한 일이다. 이는 만들고자 하는 응용프로그램의 특성에 따라 굉장히 달라질 수 있다.)

IOCP는 이러한 단점을 극복하기 위해 하나의 스레드가 하나 이상의 사용자로부터의 요구를 처리할 수 있도록 해준다. 그렇다고 하나의 스레드만을 생성하는 것은 아니다. 여러 개의 스레드를 생성하지만 한 스레드가 한 사용자만을 전담하는 방식은 아니라는 것이다. 즉, 실행되는 스레드의 수를 적게 해서 이로 인한 컨텍스트 스위칭의 수를 줄이는 것이다. 이것이 가능하려면 이제 뒤에서 살펴볼 것처럼 프로그램내에서 I/O시에 비동기 방식을 사용해야 한다.

비동기 I/O는 서버 프로그래밍의 필수

서버 프로그램에서 성능 향상을 위해서 사용할 수 있는 다른 하나의 테크닉은 비동기(Asynchronous) I/O를 사용하는 것이다. 이를 이용하면 동시에 여러 개의 I/O 작업을 수행할 수 있는데 이는 어디까지나 작업의 시작만 비동기로 가능하다는 것이지 작업이 끝나는 부분은 즉, I/O 결과를 받는 부분은 동기화가 되어야 한다는 것이다. 만일 비동기 I/O의 결과를 그냥 무시해도 좋은 프로그램이라면 또다른 이야기가 되겠지만 아마 대부분의 프로그램에서는 비동기 I/O를 수행하고 그 결과를 살펴봐야 할 것이다.

비동기 I/O에는 여러가지 방식이 존재한다. 간략히 참고 1에 윈도우에서 지원되는 비동기 I/O 방식을 나열해 보았다. 당연한 이야기이지만 이러한 비동기 I/O 방식은 특히 시간이 오래 걸리는 작업을 할 때 적합한 방식이다. 이러한 비동기 I/O 방식은 IOCP와 결합되었을 때 최적의 성능과 확장성을 자랑한다. 다시 정리해서 말하자면 비동기 I/O의 성능은 I/O가 끝났을 때 그 결과를 어떻게 확인하느냐에 달려 있는데 IOCP는 이러한 비동기 I/O를 가장 효율적으로 사용할 수 있게 해준다.

--------------------------------------------------------------------------------
참고 1. 윈도우의 비동기 I/O
윈도우에서는 다양한 방식의 비동기 I/O를 제공한다 (사실 너무 다양한 방법을 제공한다.) 여기서는 간략히 언급하기로 하겠다. 다음에 기회가 닿으면 파일 I/O 관련 연재 기사를 다뤄볼 생각인데 그 때 자세히 언급하기로 하겠다.

1> 오버랩드 I/O를 사용하기.

예로 파일 I/O를 들어보자. 파일을 오픈할 때 CreateFile API를 사용하는데 이 때FILE_FLAG_OVERLAPPED를 인자로 주면 오버랩드 I/O를 수행할 수 있다. ReadFile과 WriteFile을 사용하여 I/O를 수행하게 되는데 이 함수들은 실행이 끝날 때까지 기다리지 않고 바로 리턴한다(비동기 I/O니까 당연한 이야기이지만). 이 때 마지막 인자로 OVERLAPPED 구조체를 사용하는데 여기에 이벤트(지난 회에 설명한 바 있다)를 지정하도록 되어있다. 작업이 끝나면 이 이벤트로 시그널이 가게 된다. 이벤트를 사용하는 대신에 함수의 실행이 끝났는지를 검사하기 위해 GetOverlappedResult 함수를 호출할 수도 있다. 참고로 ReadFile이나 WriteFile과 같은 함수는 꼭 파일 I/O에 사용되는 것이 아니란 점도 알아두기 바란다. 소켓에서 데이터를 읽고 쓰는데도 사용할 수 있다.

2> 콜백 함수 사용하기

기본적으로는 1<의 방식과 갖다. 다만 이벤트를 사용하는 대신에 콜백 함수를 지정해서 작업이 끝나면 그 함수를 호출하도록 하는 것이다. 이때는 ReadFile, WriteFile과 같은 함수 대신에 ReadFileEx와 WriteFileEx와 같은 함수를 사용해야 한다. 이 함수들은 인자 중의 하나로 콜백 함수의 주소를 받아들이도록 되어있다.

3> IOCP 사용하기

사실 IOCP를 비동기 I/O 작업 방식이라고도 할 수 있는데 이에 대해서는 이 기사의 뒷부분에서 자세히 살펴볼 것이다.

--------------------------------------------------------------------------------

지금까지 살펴본 것과 같은 기존의 멀티스레드 서버 프로그래밍의 문제점을 해결하기 위해 만들어진 것이 바로 IOCP이다. 기본적으로 IOCP는 비동기 I/O 작업을 지원하면서 적은 수의 스레드로 최대한의 요청을 처리하기 위한 방법이란 점이라고 이해하면 된다. 너무 많은 스레드가 동시에 동작함으로 인한 문제를 해결하면서 비동기 I/O 작업시 결과를 체크해야 하는 문제를 해결함으로써 서버 프로그램의 성능을 극대화하는 것이다.

2. IOCP란 ?
IOCP란 특정 입출력 객체(예를 들면 파일, 소켓, 메일 슬롯 등등)와 관련된 일종의 I/O 작업 결과 큐라고 생각할 수 있다. 좀더 자세히 설명하자면 먼저 IOCP 객체가 별도로 생성되어야 한다. 그 다음에 이 객체와 입출력 객체 중의 하나가 연결되어야 한다. 다음으로 이 입출력 객체에 비동기 I/O 작업이 수행되면 운영체제가 이 큐에 그 비동기I/O의 결과를 집어넣게 된다.

또한 이 큐는 하나 이상의 스레드와 연관지어지게 된다 (스레드의 수는 비동기 I/O의 특성에 따라 달라지게 된다. 만일 I/O가 오래 걸리는 것이라면 스레드의 수는 적어도 관계없다. 하지만 I/O가 시간이 아주 조금밖에 안 걸리는 것이라면 스레드의 수는 많아야 한다). 운영체제는 큐에 결과가 있고 관련 스레드들 중에서 놀고 있는 놈이 있으면 그 스레드가 결과를 받아서 다음 작업을 수행할 수 있게 해준다. 즉, IOCP와 관련되어 동작할 수 있는 스레드를 미리 여러 개 만들어 놓고 이 중에서 필요에 따라 놀고 있는 것을 가져다 큐에서 비동기 I/O 결과를 읽어가도록 하는 것이다. 참고로 한 IOCP 객체는 동시에 여러 입출력 객체와 연관지어질 수 있다.

자 이러한 과정을 코드를 통해 좀더 자세히 살펴보자. 그림 1을 참고하기 바란다. 본 기사의 서버 예제 프로그램의 코드를 바탕으로 설명하겠다.

< 그림 1. IOCP의 동작 >

1> IOCP의 생성

먼저 첫번째 절차는 IOCP를 생성하는 것이다. 이는 CreateIoCompletionPort 함수를 통해 가능하다. 이 같은 함수를 이용해 입출력 객체와 IOCP를 연관짓는데 사용할 수 있다. 다음은 IOCP를 일단 생성하는 예(CreateIoCompletionPort 함수의 첫번째 인자로 INVALID_HANDLE_VALUE를 지정해야 한다. 이 함수에 대한 보다 상세한 설명은 참고 2를 보기 바란다)이다. 생성의 결과는 HANDLE로 리턴된다.

  g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
  if (NULL == g_hIOCP)
  {
    printf("CreateIoCompletionPort failed: %d\n", GetLastError());
    CleanUp();
  }

2> IOCP 큐에서 결과를 읽어들일 스레드 생성

앞서 이야기한 것처럼 IOCP와 연관된 입출력 객체에 비동기 I/O를 수행하면 그 결과가 IOCP 큐에 쌓인다고 하였다. 이 큐에서 결과를 읽어들이는 일을 수행하는 스레드를 만들어야 한다. 다른 스레드 생성과 특별히 다를 것은 없다. 단 생성할 스레드의 수는 CPU수 X 2로 되어있다. 이는 마이크로소프트에서 권장하는 방식이다. 응용프로그램에 따라 이것이 적당할 수도 있고 훨씬 더 많은 스레드가 필요할 수도 있다. 이를 위해서 GetSystemInfo라는 함수를 이용해서 현재 시스템의 CPU수를 알아내는 코드가 들어있다.

#define MAX_WORKER_THREAD    16

DWORD g_dwThreadCount;
unsigned int g_hThreads[MAX_NUMBER_OF_THREADS];


SYSTEM_INFO         systemInfo;
DWORD dwThreadId;

GetSystemInfo(&systemInfo);
g_dwThreadCount = systemInfo.dwNumberOfProcessors * 2;
  …
for (DWORD dwThread = 0;dwThread < g_dwThreadCount; dwThread++)
{
  g_hThreads[dwThread] = _beginthreadex(NULL, 0, EchoThread,
                             g_hIOCP, 0, &dwThreadId);
  If (g_hThreads[dwThread] == NULL)
  {
    printf(“%d번째 스레드 생성에 실패했습니다.\n”, dwThread);
  }
}

위에서 볼 수 있는 것처럼 스레드의 생성에는 _beginthreadex 함수를 사용하였다. 스레드 함수는 EchoThread이며 스레드 함수의 인자로는 IOCP 핸들을 넘긴다. EchoThread 함수의 자세한 내용은 5>에서 살펴볼 것이다.

3> IOCP와 입출력 객체의 연결

다음은 이 IOCP와 입출력 객체를 연결하는 부분이다. 입출력 객체는 반드시 비동기 I/O 모드로 오픈되어야 한다. 연결된 객체에 대한 비동기 오버랩드 I/O 결과가 이 IOCP 큐에 들어간다. 예를 들어 소켓과 IOCP를 연결하는 간단한 예를 보면 다음과 같다.

SOCKET sh;

sh = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL,
             0, WSA_FLAG_OVERLAPPED);…
if (sh != INVALID_SOCKET)
{
    CreateIoCompletionPort((HANDLE)sh, g_hIOCP, (ULONG_PTR)0, 0);

먼저 소켓을 생성할 때 socket 함수를 사용한 것이 아니라 WSASocket 함수를 사용하였고 마지막 인자로 WSA_FLAG_OVERLAPPED가 지정되었다. 그 다음에 앞에서와 같은 CreateIoCompletionPort 함수를 사용하고 그 첫번째 인자로 소켓의 값을 지정하면 된다. 즉, CreateIoCompletionPort 함수는 IOCP의 생성에도 사용되고 생성된 IOCP와 입출력 객체를 연결하는데도 사용된다. 참고 2에서 CreateIoCompletionPort 함수의 세 번째 인자 설명을 보면 알 수 있겠지만 세 번째 인자가 가장 중요한 역할을 한다.

앞서 잠깐 언급했던 것처럼 IOCP는 여러 개의 입출력객체와 동시에 연관지어질 수 있다. 예를 들어 여러 개의 소켓이 하나의 IOCP와 연관될 수 있다. 즉 그러한 소켓들에 대해 이루어지는 비동기 작업의 결과는 모두 그 하나의 IOCP 큐로 들어간다는 것이다. 그러기 때문에 IOCP 큐에서 비동기 작업 결과를 읽어들일 때 이 결과가 도대체 어느 입출력 객체로부터 온 것인지를 구분할 수 있는 방법이 있어야 한다. 이 때 CreateIoCompletionPort 함수의 세 번째 인자로 지정했던 값이 구분하는 역할을 담당한다. 뒤에서 살펴보겠지만 IOCP 큐에서 결과를 읽어들일 때 사용하는 함수는GetQueuedCompletionStatus라는 것이다. 이 함수의 세 번째 인자로 앞서 CreateIoCompletionPort 함수에서 지정했던 값이 넘어오게 되어 있다. 예제 프로그램에서는 입출력 객체마다 다음과 같은 구조체를 생성하고 이를 IOCP 객체와 연관지을 때 세번째 인자로 지정할 것이다.

// IOCP와 연관되는 소켓마다 할당되는 구조체
typedef struct _PER_SOCKET_CONTEXT
{
    SOCKET                 Socket;
    PPER_IO_CONTEXT       pIOContext; 
} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;

위에서 Socket은 클라이언트가 하나 연결될 때마다 부여되는 소켓이다. pIOContext는 이 소켓과의 입출력 작업에 사용되는 메모리 버퍼와 각종 구조체를 모아둔 구조체로 이 소켓내에서 벌어지는 입출력 작업의 상태를 나타낸다고 생각하면 된다. 다음과 같이 정의되어 있다.

#define MAX_BUFF_SIZE       8192
// 소켓에 대한 입출력 작업에 사용되는 구조체
typedef struct _PER_IO_CONTEXT
{
    WSAOVERLAPPED        Overlapped;
    char                     Buffer[MAX_BUFF_SIZE];
    WSABUF                 wsabuf;
    int                       nTotalBytes;
    int                       nSentBytes;
    IO_OPERATION           IOOperation;
} PER_IO_CONTEXT, *PPER_IO_CONTEXT;

먼저 첫번째 필드인 Overlapped는 사실 ReadFile, WriteFile과 같은 함수를 이용해서 수행하는 비동기 I/O에서 사용하는 OVERLAPPED 구조체와 동일한 것이다. typedef로 이름만 바꾸었을 뿐이다. 뒤에서 WSARecv와 WSASend를 이용해서 비동기 I/O를 해볼 텐데 그 때 이 필드가 사용된다. 그 함수들을 호출할 때 로컬 변수로 사용하면 안 될까 생각할 수도 있는데 이 변수는 작업이 끝날 때까지 접근이 가능해야 하기 때문에 이렇게 글로발하게 별도로 잡아두는 것이다. (로컬 변수로 잡고 그걸 인자로 비동기 함수를 호출하면 그 변수가 선언한 블럭을 벗어날 경우 그 로컬 변수는 더 이상 유효하지 않다. 이런 문제를 해결하기 위함이다)

사실 이 구조체는 의도적으로 WSAOVERLAPPED 타입의 필드로부터 시작한다. 비동기 I/O 작업에 사용되는 WSASend, WSARecv함수의 경우 인자 중에 WSAOVERLAPPED 타입의 변수를 받아들이는 인자가 있다. 또한 비동기 I/O가 끝나고 그 결과를 IOCP 큐에서 읽어들일 때 앞서 사용했던WSAOVERLAPPED 타입의 변수를 그대로 받아볼 수 있다.

사실 Overlapped가 이 구조체의 첫 번째 필드이기 때문에 이 필드의 주소나 이 구조체의 주소나 동일하다. WSASend와 WSARecv를 이용해 비동기 I/O를 개시할때 이 구조체의 Overlapped 필드의 주소를 넘기면 사실 이것이PER_IO_CONTEXT 타입 변수의 주소를 넘긴 것이나 다름없다. 그렇게 해서 비동기 I/O의 결과를 큐에서 꺼낼 때 현재 작업의 상태를 알 수 있는 것이다. 앞서 이야기한 것처럼 PER_IO_CONTEXT 구조체는 현재 비동기 I/O 작업의 상태를 나타낸다.

두 번째 필드인 Buffer는 읽기/쓰기 작업을 할때 사용할 메모리 영역이다. 세 번째 필드인 wsabuf는 읽기/쓰기 작업시 데이터의 시작 포인터와 데이터 크기를 지정하는데 사용되는 구조체이다. WSASend와 WSARecv 함수의 인자로 필요하다. 네 번째 인자인 nTotalBytes는 쓰기 작업시 전송해야할 데이터의 양을 나타낸다. 다섯 번째 인자인 nSendBytes는 지금까지 전송된 데이터의 양을 나타낸다. 마지막 인자인 IOOperation은 다음과 같이 정의된 열거자로서 현재 소켓에 대해 진행 중인 작업의 종류를 나타낸다.

typedef enum _IO_OPERATION
{
    ClientIoRead, // 읽기 작업 진행 중
    ClientIoWrite  // 쓰기 작업 진행 중
} IO_OPERATION, *PIO_OPERATION;

이제 이를 바탕으로 예제 프로그램의 코드를 살펴보자. 다음에서 볼 수 있는 것처럼 클라이언트로부터의 요청이 들어오기를 대기하다가 요청이 들어오면 그로 인해 생성되는 소켓을 인자로 위의 작업을 수행하는UpdateCompletionPort라는 함수를 별도로 만들었다.

  SOCKET                 sdAccept = INVALID_SOCKET;
  PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;

  while (g_bEndServer == FALSE)
  {
    // 클라이언트가 들어오기를 대기한다.
    sdAccept = WSAAccept(g_sdListen, NULL, NULL, NULL, 0);
    if (SOCKET_ERROR == sdAccept)
    {
      printf("WSAAccept: %d\n", WSAGetLastError());
      CleanUp();
    }
    printf("클라이언트가 하나 들어왔습니다\n.");
    // 만들어진 sdAccept 소켓에 앞서본 PER_SOCKET_CONTEXT 구조체를 할당한다.
    // 그리고나서 이를 IOCP 객체와 연결한다. 두 번째 인자로는 이제 일어날 작업의    
    // 종류를 명시한다. 에코우 서버이므로 첫 번째 할 작업은 클라이언트로부터
    // 데이터를 읽는 것이기 때문에 ClientIoRead를 명시한다.
    lpPerSocketContext = UpdateCompletionPort(sdAccept, ClientIoRead, TRUE);
    if (NULL == lpPerSocketContext)
    {
      CleanUp();
    }
    // …

UpdateCompletionPort 함수의 내용은 다음과 같다. 첫 번째 인자로 지정된 소켓을 바탕으로 앞서본 PER_SOCKET_CONTEXT 구조체를 할당한다. 이것과 소켓을IOCP 객체와 연결한다. 두 번째 인자로는 이제 이 소켓에 일어날 작업의 종류를 명시한다. 에코우 서버이므로 첫 번째 할 작업은 클라이언트로부터 데이터를 읽는 것이기 때문에 ClientIoRead를 명시한다.

// 첫번째 인자로 명시된 소켓을 IOCP에 연결짓는다.
PPER_SOCKET_CONTEXT UpdateCompletionPort(SOCKET sd, IO_OPERATION ClientIo)
{
  PPER_SOCKET_CONTEXT lpPerSocketContext;

  // PER_SOCKET_CONTEXT를 할당하는데 CtxtAllocate 함수를 사용한다.
  lpPerSocketContext = CtxtAllocate(sd, ClientIo);
  if (lpPerSocketContext == NULL)
    return NULL;

  // 할당된 구조체와 소켓을 g_hIOCP에 연결한다.
  g_hIOCP = CreateIoCompletionPort((HANDLE)sd, g_hIOCP,
         (DWORD)lpPerSocketContext, 0);
  if (NULL == g_hIOCP)
  {
    printf("CreateIoCompletionPort: %d\n", GetLastError());
    if (lpPerSocketContext->pIOContext)
      free(lpPerSocketContext->pIOContext);
    free(lpPerSocketContext);
    return(NULL);
  }

  // 이 구조체를 링크드 리스트에 보관한다.
  CtxtListAddTo(lpPerSocketContext);
  return(lpPerSocketContext);
}

위의 코드를 보면 PER_SOCKET_CONTEXT 타입의 구조체를 할당하기 위해서 CtxtAllocate라는 함수를 사용하고 있다. 이 함수에 대해서는 뒤에서 다시 설명할 텐데 구조체를 할당하고 초기화하는 일을 담당한다. 그 다음에 CreateIoCompletionPort 함수를 이용해서 이 구조체와 소켓을 IOCP에 연결한다. 마지막으로 이렇게 생성된 구조체를 전체적으로 관리하기 위해서 CtxtListAddTo 함수를 호출한다. 이 함수 역시 뒤에서 다시 설명하겠다.

--------------------------------------------------------------------------------
참고 2. CreateIoCompletionPort
이 함수의 원형은 다음과 같다.

HANDLE CreateIoCompletionPort(HANDLE FileHandle,
   HANDLE ExistingCompletionPort,
   ULONG_PTR CompletionKey,
   DWORD NumberOfConcurrentThreads);

첫 번째 인자인 FileHandle은 IOCP의 대상이 되는 입출력 객체의 핸들이어야 한다. 이 객체는 반드시 오버랩드 I/O 모드로 오픈된 것이어야 한다. 만일 이 인자의 값이 INVALID_FILE_HANDLE로 주어지고 두 번째 인자의 값이 NULL이 되면 이 함수의 리턴값은 새롭게 생성된 IOCP의 핸들이 된다. 이 때 세번째 인자의 값은 무시된다.

두 번째 인자인 ExistingCompletionPort는 IOCP에 대한 핸들을 지정하기 위해 사용된다. 이 경우 첫번째 인자의 값은 입출력 객체의 핸들이 되어야 하며 이 둘은 연결되게 된다. 그런 경우 이 함수는 두번째 인자로 지정된 IOCP 핸들을 그대로 다시 리턴한다.

세 번째 인자인 CompletionKey는 IOCP와 연결된 입출력 객체에 특정한 포인터라고 할 수 있다. 한 IOCP에는 여러 개의 입출력 객체가 동시에 연관될 수 있기 때문에 이 값을 통해 어느 객체로부터의 I/O 결과인지를 구분할 수 있다. 따라서 여러 개의 입출력 객체를 사용할 경우 이 인자는 아주 중요한 역할을 하게 된다.

마지막 인자인NumberOfConcurrentThreads는 이 IOCP에 연관지어지는 스레드의 최대 수를 지정하는데 사용된다. 0을 주면 시스템의 자원이 허용하는 한 스레드가 계속 만들어지게 된다.

--------------------------------------------------------------------------------

4> 비동기 I/O의 수행

앞 절차에서 소켓이 제대로 IOCP에 연결이 되고 나면 이제 그 소켓에 대해 비동기 I/O 작업을 수행해야 한다. 소켓의 경우, WSASend와 WSARead를 호출하면 그 결과는 g_hIOCP라는 것이 가리키는 큐안에 쌓이게 된다. 다음과 같은 함수들이 비동기 I/O 결과를 IOCP큐에 넣는다.

ReadFile, WriteFile
WSASend, WSARecv
ConnectNamedPipe
DeviceIoControl
LockFileEx
ReadDirectoryChanges
TransactNamedPipe
WaitCommEvent
예제 프로그램에서는 UpdateCompletionPort 함수의 호출이 성공적으로 끝난 후에 클라이언트에서 보내는 데이터를 받기 위해서 WSARead 함수를 한번 호출한다. 참고로 다시 한번 이야기하자면 이 서버 프로그램은 에코우 서버이기 때문에 클라이언트가 보낸 데이터를 그대로 다시 클라이언트로 전송한다.

    lpPerSocketContext = UpdateCompletionPort(sdAccept, ClientIoRead);
    if (NULL == lpPerSocketContext)
    {
      CleanUp();
      return 1;
    }

    // 소켓에 비동기 읽기를 수행한다.
    nRet = WSARecv(sdAccept, &(lpPerSocketContext->pIOContext->wsabuf), 1,
                &dwRecvNumBytes, &dwFlags,
                &(lpPerSocketContext->pIOContext->Overlapped), NULL);
    if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
    {
      printf("WSARecv Failed: %d\n", WSAGetLastError());
      CloseClient(lpPerSocketContext);
    }
  } //while

위의 WSARecv 함수 호출에서 6번째 인자를 눈여겨 보기 바란다. WSAOVERLAPPED 구조체의 변수를 지정하는데 PER_IO_CONTEXT의 Overlapped 필드를 넘기고 있다. 3>에서 설명한 것처럼 이는 사실 pIOContext의 주소를 넘기는 것과 동일한 효과를 갖는다.

아무튼 WSARecv로 인한 읽기 작업이 완료되면 이는 IOCP 큐에 들어간다. 이를 읽어들이는 작업은 앞에서 만든 스레드들에서 수행한다. 이 함수는 비동기 함수이기 때문에 바로 리턴하고 그리고나서 코드는 다시 while 루프로 선두로 가서 다른 클라이언트로부터의 연결을 대기한다.

  while (g_bEndServer == FALSE)
  {
    // 클라이언트가 들어오기를 대기한다.
    sdAccept = WSAAccept(g_sdListen, NULL, NULL, NULL, 0);
    …

즉, main 함수는 초기화 작업을 하고 난 뒤부터는 클라이언트로부터의 소켓연결이 맺어지기를 기다렸다가 만들어지면 이를 IOCP와 연결한 뒤에 WSARecv를 한번 호출하는 일만 한다. 실제 작업은 모두 스레드에서 이루어진다.

참고로 WSASend와 WSARecv의 함수 원형을 살펴보자.

int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
       LPDWORD  lpNumberOfBytesRecvd, LPDWORD lpFlags,
       LPWSAOVERLAPPED lpOverlapped,
       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

int WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
       LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
       LPWSAOVERLAPPED lpOverlapped,
       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

이 두 함수는 비슷한 인자를 많이 갖고 있다. 먼저 모두 첫번째 인자는 소켓 핸들이다. 두 번째 인자는 WSABUF라는 구조체에 대한 포인터로 보낼 데이터에 대한 정보이거나 데이터를 받을 버퍼에 대한 정보이다. WSABUF는 다음과 같이 버퍼의 시작 주소와 버퍼의 크기를 지정하는 두개의 필드로 구성되어 있다.

Typedef struct __WSABUF
{
u_long len; // 버퍼 크기
  char FAR *buf; // 버퍼 시작 주소
} WSABUF, FAR *LPWASBUF;

이 두 번째 인자로는 WSABUF 배열의 주소를 지정할 수도 있다. 그 경우 차례로 여러 버퍼의 데이터를 전송하거나 (WSASend의 경우) 받은 데이터를 여러 버퍼로 옮기는 역할(WSARecv의 경우)을 한다. 세 번째 인자는 이 두 번째 인자가 가리키는 WSABUF 변수의 수를 나타낸다. 배열을 지정했을 경우에는 그 크기를 이 인자로 지정해주면 된다. 배열이 아니라면 그냥 1을 지정하면 된다. 여기서 한가지 알아야 할 점은 이 두 함수 모두 지정한 크기만큼 입출력이 종료된 다음에 리턴되는 것이 아니란 점이다. WSARecv 같은 경우에는 읽어올 데이터가 생기면 지정된 크기와 관계없이 바로 작업을 종료한다. WSASend의 경우에는 소켓 버퍼가 꽉 차서 데이터를 지정된 크기만큼 보낼 수 없으면 일단 보낼 수 있는 만큼 보내고 만다.

네 번째 인자는 각기 실제로 전송된 데이터(WSASend의 경우)와 실제로 읽어들인 데이터(WSARecv의 경우)의 크기가 들어간다. 그런데 이 함수들을 예제 프로그램에서처럼 비동기 모드로 사용할 경우에는 이 인자로 리턴되는 값은 함수 자체의 리턴값이 0인 경우에만 의미가 있다. 0인 경우는 바로 작업이 끝난 경우이다. 함수가 바로 끝나지 않을 경우에는 SOCKET_ERROR가 리턴되고 이 때 GetLastError 함수를 호출해보면 그 값이 WSA_IO_PENDING일 것이다.

다섯 번째 인자는 약간 복잡한데 일단 대부분 0이 리턴되거나 (WSARecv의 경우) 0이 지정(WSASend의 경우)된다고 알아두기 바란다. 여섯 번째 인자는 WSAOVERLAPPED 구조체에 대한 포인터를 지정하는 부분이다. IOCP를 사용하는 경우에는 hEvent 필드의 값은 반드시 NULL이 지정되어야 한다. 마지막 인자는 콜백함수를 지정하는데 사용된다. 이 콜백함수의 원형은 다음과 같다.

void CALLBACK CompletionROUTINE(DWORD dwError, DWORD cbTransferred,
    LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);

만일 여섯 번째 인자와 마지막 인자가 모두 NULL이면 이 함수들은 동기 모드로 동작한다. 여섯 번째 인자와 마지막 인자가 모두 지정되면 작업이 종료되었을 때 마지막 인자로 지정된 함수가 호출된다. 여섯 번째 인자만 지정되고 첫 번째 인자로 지정된 소켓이 IOCP와 연결되어 있으면 이 함수의 결과는 IOCP 큐에 들어간다. 사실 이 두 함수의 인자들을 제대로 이해할 수 있다면 윈도우 운영체제의 입출력 함수는 다 이해했다고 봐도 무방하다.

5> 비동기 I/O 결과 읽기

앞서 수행된 비동기 I/O의 결과를 읽어들이려면 GetQueuedCompletionPort라는 함수를 이용해야 한다. 이 함수 원형에 대한 설명은 참고 3에 있다. 이 함수는 IOCP 큐안에 읽어들일 비동기 I/O 결과가 있으면 이를 읽어가지고 리턴한다. 읽어올 것이 없으면 읽어올 것이 생길 때까지 리턴하지 않는다. 다음 코드처럼 이 함수는 무한루프안에서 계속적으로 호출되는 것이 일반적이다.

While (1)
{
  GetQueuedCompletionStatus(…);
  // 읽어들인 결과를 바탕으로 다음 일을 수행한다.
  …
}

예제 프로그램과 같은 에코우 서버에서는 특정 소켓에 대해 읽기 작업이 완료된 결과를 읽어들였으면 이를 비동기로 쓰는 작업을 하고, 쓰기 작업이 완료된 결과를 읽어들였으면 다시 비동기로 읽기 작업을 수행한다. 앞서 이야기한 것처럼 GetQueuedCompletionPort 함수의 세 번째 인자로는 현재 이 소켓에 대해 따로 할당된PER_SOCKET_CONTEXT 구조체의 포인터가 리턴되고 이 것의 pIOContext 필드를 보면 현재 진행중인 작업의 상태를 알 수 있다. pIOContext의IOOperation 필드의 값이ClientIoRead이면 지금 큐에서 읽어온 작업이 읽기 작업의 결과인 것이고 ClientIoWrite이면 쓰기 작업인 것이다.

위의 코드를 좀더 예제 프로그램에 맞게 고쳐보면 다음과 같은 식이다.

While (1)
{
  GetQueuedCompletionStatus(…);
  // 읽어들인 결과를 바탕으로 다음 일을 수행한다.
  만일 읽어들인 결과가 읽기 작업이면
    읽어들인 데이터를 그대로 다시 서버로 보낸다 (물론 비동기 I/O)
  만일 읽어들인 결과가 쓰기 작업이면
    만일 앞서 쓰기 요청한 것이 다 전송되지 않았으면
      전송안 된 부분만 다시 전송한다
    다 전송되었으면
      읽기 비동기 작업을 소켓에 수행한다.
}

--------------------------------------------------------------------------------
참고 3. GetQueuedCompletionStatus
이 함수의 원형은 다음과 같다.

BOOL GetQueuedCompletionStatus(
    HANDLE CompletionPort,      
    LPDWORD lpNumberOfBytes,
    PULONG_PTR lpCompletionKey,
    LPOVERLAPPED *lpOverlapped,
    DWORD dwMilliseconds);

첫 번째 인자인 CompletionPort로는 앞서 생성된 IOCP 객체의 핸들을 지정한다.

두 번째 인자로는 지금 읽어온 I/O 작업의 결과로 읽거나 쓴 데이터의 크기가 바이트 단위로 지정된다. 즉 이 인자의 값은 운영체제에서 지정한다.

세 번째 인자인 lpCompletionKey역시 운영체제에 의해 채워져 리턴되는 값이다. CreateIoCompletionPort 함수로 IOCP 객체를 생성할 때 세 번째 인자로 지정한 값이 여기로 리턴된다. 앞서 이야기한 것처럼 한 IOCP 객체로 둘 이상의 입출력 디바이스를 처리할 수 있기 때문에 이를 구분하는 값이 여기로 지정된다고 생각하면 된다.

네 번째 인자인 lpOverlapped 역시 운영체제에 의해 값이 지정되는데 이는 한 입출력 디바이스내에서 각각의 입출력 작업을 구별하는 역할을 한다. 이 값은 사실 앞서 비동기 작업에서 사용된 OVERLAPPED 구조체의 주소가 그대로 들어온다. 그렇기 때문에 비동기 I/O 작업시에 OVERLAPPED 구조체를 스택에 있는 것을 사용하면 안 되고 각 작업마다 서로 다른 OVERLAPPED 구조체가 사용되어야 하는 것이다.

마지막 인자인dwMilliseconds는 IOCP 큐에 결과가 없을 경우 얼마나 더 대기하다가 리턴할 것인지를 밀리세컨드 단위로 지정한다. 만일 타임아웃이 나서 리턴할 경우에는 GetQueuedCompletionStatus 함수의 리턴값은 FALSE가 되고 네 번째인자로는 NULL이 지정된다. 읽어올 것이 생길 때까지 대기하도록 하고 싶으면 이 인자로 INFINITE를 지정하면 된다.

위의 플로우를 염두에 두고 이제 예제 프로그램의 스레드 코드를 실제로 살펴보자. 주석을 자세히 달아놓았으므로 주석과 함께 코드를 살펴보기 바란다.

DWORD WINAPI EchoThread (LPVOID WorkThreadContext)
{
  // 앞서 스레드 생성시 스레드 함수의 인자로 IOCP 핸들을 지정했었다.
  // 인자를 IOCP 핸들로 캐스팅한다.
  HANDLE hIOCP = (HANDLE)WorkThreadContext;
  BOOL   bSuccess = FALSE;
  int      nRet;
  LPOVERLAPPED    lpOverlapped = NULL;
  PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;
  PPER_IO_CONTEXT     lpIOContext = NULL;
  WSABUF buffRecv;
  WSABUF buffSend;
  DWORD  dwRecvNumBytes = 0;
  DWORD  dwSendNumBytes = 0;
  DWORD  dwFlags = 0;
  DWORD  dwIoSize;
  while (TRUE)
  {
    // IOCP 큐에서 비동기 I/O 결과를 하나 읽어온다.
    bSuccess = GetQueuedCompletionStatus(hIOCP, &dwIoSize,
             (LPDWORD)&lpPerSocketContext, &lpOverlapped,INFINITE);
    if (!bSuccess)
      printf("GetQueuedCompletionStatus: %d\n", GetLastError());

    // CleanUp 함수에 의해서 스레드의 강제 종료 명령이 내려지면..
    if (lpPerSocketContext == NULL)  return 0;
    if (g_bEndServer) return 0;
    // 클라이언트와의 소켓 연결이 끊어졌으면…
    if (!bSuccess || (bSuccess && (0 == dwIoSize)))
    {
      // lpPerSocketContext를 메모리에서 제거한다.
      CloseClient(lpPerSocketContext);
      continue;
    }

    /* 앞서 WSASend와 WSARecv에 의해 I/O 작업을 할 때 넘겼던 WSAOVERLAPPED   
타입의 변수가 사실은 PER_IO_CONTEXT 타입의 시작이기도 하므로 이를 캐스팅하
여 사용가능하다. */
    lpIOContext = (PPER_IO_CONTEXT)lpOverlapped;
    switch (lpIOContext->IOOperation) // 끝난 작업 종류가 무엇인가 ?
    {
        case ClientIoRead: // 읽기 작업인가 ?
        // --------------------------------------------
        // 받은 것을 그대로 보낸다. 즉, 다음 작업은 쓰기 작업이다.
        // --------------------------------------------
        printf("%s를 받았고 이를 재전송합니다.\n.", lpIOContext->wsabuf.buf);
          lpIOContext->IOOperation = ClientIoWrite; // 이제 쓰기 작업이 진행됨을 표시
          // 얼마큼 전송할 것인지 명시한다. 받은 만큼 보낸다. 이는 상태를 기록하기
          // 위함이지 WSASend 함수와는 관련없다.
          lpIOContext->nTotalBytes = dwIoSize;
          // 전송된 데이터 크기. 아직 보낸 것이 없으므로 0
          lpIOContext->nSentBytes  = 0;
        // WSASend에게 보낼 데이터의 포인터와 크기를 지정한다.
        // 받은 데이터가 이미 lpIOContext->wsabuf.buf에 있다.
        lpIOContext->wsabuf.len  = dwIoSize; // 크기 지정
        dwFlags = 0;
        nRet = WSASend(lpPerSocketContext->Socket,
              &lpIOContext->wsabuf, 1, &dwSendNumBytes,
              dwFlags, &(lpIOContext->Overlapped), NULL);
        if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
        {
          printf("WSASend: %d\n", WSAGetLastError());
          CloseClient(lpPerSocketContext);
        }
        break;

      case ClientIoWrite: // 쓰기 작업인가 ?
        // ----------------------------------------------------
        // 전송이 다 되었는지 확인한다. 다 전송되지 않았으면 아직 전송되지
        // 않은 데이터를 다시 보낸다. 다 전송되었으면 WSARecv를 호출해서
        // 다시 받기 모드로 진입한다. 
        // --------------------------------------------
        lpIOContext->nSentBytes  += dwIoSize; // 전송된 데이터 크기 업데이트
        dwFlags = 0;
        if (lpIOContext->nSentBytesnTotalBytes) // 다 전송되지 않았으면
        {
          // 마저 전송해야 하므로 아직 보내기모드
          lpIOContext->IOOperation = ClientIoWrite;
          // -----------------------
          // 전송되지 않은 부분을 보낸다.
          // -----------------------
          // 버퍼 포인터를 업데이트하고
          buffSend.buf = lpIOContext->Buffer + lpIOContext->nSentBytes;
          // 보내야할 데이터의 크기를 남은 데이터의 크기만큼으로 줄인다.
          buffSend.len = lpIOContext->nTotalBytes - lpIOContext->nSentBytes;
          nRet = WSASend (lpPerSocketContext->Socket,
                     &buffSend, 1, &dwSendNumBytes,
                     dwFlags, &(lpIOContext->Overlapped), NULL);
          // SOCKET_ERROR가 리턴된 경우에는 반드시 WSAGetLastError의 리턴값이
          // ERROR_IO_PENDING이어야 한다.
          if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
          {
            printf ("WSASend: %d\n", WSAGetLastError());
            CloseClient(lpPerSocketContext);
          }
        }
        else // 데이터가 전부 전송된 경우
        {
          // 다시 이 소켓으로부터 데이터를 받기 위해 WSARecv를 호출한다.
          lpIOContext->IOOperation = ClientIoRead;
          dwRecvNumBytes = 0;
          dwFlags = 0;
          buffRecv.buf = lpIOContext->Buffer; // 수신버퍼 지정
          // 읽어들일 데이터 크기 지정. 사실 이 크기만큼 데이터를 읽어들여야
          // 그 결과가 IOCP큐에 들어가는 것은 아니다.  이 크기 이상 안
          // 읽어들일 뿐이고 데이터가 이용가능한 만큼 IOCP큐에 넣는다.
          buffRecv.len = MAX_BUFF_SIZE;
          nRet = WSARecv(lpPerSocketContext->Socket,
                        &buffRecv, 1, &dwRecvNumBytes,
                        &dwFlags, &(lpIOContext->Overlapped), NULL);
          // SOCKET_ERROR가 리턴된 경우에는 반드시 WSAGetLastError의 리턴값이
          // ERROR_IO_PENDING이어야 한다.
          if (SOCKET_ERROR == nRet && (ERROR_IO_PENDING != WSAGetLastError()))
          {
            printf ("WSARecv: %d\n", WSAGetLastError());
            CloseClient(lpPerSocketContext);
          }
        }
        break;
      } //switch
    } //while
    return(0);
}

자 이상으로 IOCP가 어떤 식으로 동작하는지 알아보았다. 단계별로 설명과 코드를 잘 살펴보면 어떻게 동작하는지 더 쉽게 이해할 수 있을 것이다.

3. 예제 프로그램의 기타 코드 설명
예제 프로그램에서 설명이 안 된 코드는 서버와 연결된 클라이언트의 리스트를 관리하는 함수들(CtxtAllocate, CtxtListFree, CtxtListAddTo, CtxtListDeleteFrom)과 청소 함수(CleanUp, CloseClient), 대기 소켓 생성함수(CreateListenSocket)등이다. 대기 소켓 생성 함수는 이미 지난 연재에서 살펴본 내용(사실 socket 대신에 WSASocket을 호출하는 부분만 다르다)이기 때문에 여기서는 다른 함수들에 대해서만 알아보겠다.

클라이언트 리스트 관리 함수들

접속하는 클라이언트가 생길 때마다 이는g_CtxtList에 기록된다. 이는CptrList 타입의 링크드 리스트 클래스이고 이 변수로의 접근은 모두g_CriticalSection이란 크리티컬 섹션에 의해 한번에 한 스레드로 제한된다.

CtxtAllocate는 인자로 지정된 소켓에 PER_SOCKET_CONTEXT 구조체를 하나 할당하고 그 구조체를 초기화한 다음에 이를 리턴한다. 할당에 실패하면 NULL을 리턴한다. PER_SOCKET_CONTEXT 구조체의 IO_PER_CONTEXT 타입의 필드인 pIOContext의 필드를 초기화하는 부분을 눈여겨 봐두기 바란다.

PPER_SOCKET_CONTEXT CtxtAllocate(SOCKET sd, IO_OPERATION ClientIO)
{
  PPER_SOCKET_CONTEXT lpPerSocCon;

  lpPerSocCon = (PPER_SOCKET_CONTEXT)malloc(sizeof(PER_SOCKET_CONTEXT));
  if (lpPerSocCon)
  {
    lpPerSocCon->pIOContext = (PPER_IO_CONTEXT)
        malloc(sizeof(PER_IO_CONTEXT));
    if (lpPerSocCon->pIOContext)
    {
      lpPerSocCon->Socket = sd;
      memset(&lpPerSocCon->pIOContext->Overlapped,
         0, sizeof(OVERLAPPED));
      lpPerSocCon->pIOContext->IOOperation = ClientIO;
      lpPerSocCon->pIOContext->nTotalBytes = 0;
      lpPerSocCon->pIOContext->nSentBytes = 0;
      lpPerSocCon->pIOContext->wsabuf.buf = lpPerSocCon->pIOContext->Buffer;
      lpPerSocCon->pIOContext->wsabuf.len = MAX_BUFF_SIZE;
    }
    else
    {
      free(lpPerSocCon);
      lpPerSocCon = NULL;
    }
  }
  return(lpPerSocCon);
}

나머지 세 함수들은 간단하다. CptrList 클래스를 사용해본 이라면 이 함수들의 소스를 이해하기가 아주 쉬울 것이다. 여기서는 CtxtListAddTo와 CtxtListDeleteFrom 함수만 살펴보겠다.

// g_CtxtList에 lpPerSocketContext가 가리키는 항목을 추가한다
VOID CtxtListAddTo (PPER_SOCKET_CONTEXT lpPerSocketContext)
{
  EnterCriticalSection(&g_CriticalSection);
  g_CtxtList.AddTail(lpPerSocketContext); // 리스트의 끝에 붙인다.
  LeaveCriticalSection(&g_CriticalSection);
  return;
}

// g_CtxtList에서 lpPerSocketContext가 가리키는 항목을 제거한다.
VOID CtxtListDeleteFrom(PPER_SOCKET_CONTEXT lpPerSocketContext)
{
  EnterCriticalSection(&g_CriticalSection);
  if (lpPerSocketContext)
  {
    POSITION pos = g_CtxtList.Find(lpPerSocketContext);
    if (pos)
    {
      g_CtxtList.RemoveAt(pos);
      if (lpPerSocketContext->pIOContext)
        free(lpPerSocketContext->pIOContext);
      free(lpPerSocketContext);
    }
  }
  LeaveCriticalSection(&g_CriticalSection);
  return;
}

청소 함수들

여기서는 CleanUp 함수의 코드를 보기로 하겠다. 이 함수를 프로그램이 종료될 때 호출되는 함수로 모든 스레드가 종료되기를 기다렸다가 클라이언트 리스트에 할당되었던 자료구조들을 제거하고 최종적으로 IOCP와 대기 소켓을 제거하는 일을 수행한다.

void CleanUp()
{
    if (g_hIOCP)       
    {
        // 스레드를 강제 종료하도록 한다.
        // 참고 4와 EchoThread의 if (lpPerSocketContext == NULL)를 같이 보기 바란다. 
        for (DWORD i = 0; i < g_dwThreadCount; i++)
            PostQueuedCompletionStatus(g_hIOCP, 0, 0, NULL);
    }

    // 모든 스레드가 실행을 중지했는지 확인한다.
    if (WAIT_OBJECT_0 != WaitForMultipleObjects( g_dwThreadCount,  g_hThreads,
                     TRUE, 1000))
        printf("WaitForMultipleObjects failed: %d\n", GetLastError());
    else
        for (DWORD i = 0; i < g_dwThreadCount; i++) // 스레드 핸들을 모두 닫는다.
        {
            if (g_hThreads[i] != INVALID_HANDLE_VALUE) CloseHandle(g_hThreads[i]);
                g_hThreads[i] = INVALID_HANDLE_VALUE;
        }
    // g_CtxtList에 들어있는 클라이언트들을 모두 제거한다.
    CtxtListFree();
    // IOCP를 제거한다. 
    if (g_hIOCP)   
    {
        CloseHandle(g_hIOCP);
        g_hIOCP = NULL;
    }
    // 대기 소켓을 제거한다.
    if (g_sdListen != INVALID_SOCKET)
    {
        closesocket(g_sdListen);
        g_sdListen = INVALID_SOCKET;
    }

    DeleteCriticalSection(&g_CriticalSection); // 크리티컬 섹션을 제거한다.
    WSACleanup(); // 윈속 라이브러리를 해제한다.
}

--------------------------------------------------------------------------------
참고 4. PostQueuedCompletionPort
앞에서 설명한 것처럼 이 함수는 IOCP 큐에 마치 비동기 작업이 끝나서 그 결과가 큐에 들어가는 것처럼 흉내내는 기능을 한다. 그렇기 때문에 이 함수의 인자들을 보면 GetQueuedCompletionStatus 함수에 있는 것과 동일하다. 이 함수의 원형은 다음과 같다.

BOOL PostQueuedCompletionStatus(
HANDLE CompletionPort,
DWORD dwNumberOfBytesTransferred,
  ULONG_PTR dwCompletionKey,
LPOVERLAPPED lpOverlapped);

첫 번째 인자인 CompletionPort로는 지금 만들어내는 I/O 작업의 결과가 들어갈 IOCP 객체의 핸들을 지정한다.

두 번째 인자인 dwNumberOfBytesTransferred는 GetQueuedCompletionStatus 함수의 두 번째 인자로 넘어갈 값을 지정한다.

세 번째 인자인 dwCompletionKey는 두 번째 인자와 마찬가지로 GetQueuedCompletionStatus 함수의lpCompletionKey 인자로 들어갈 값을 지정하는데 사용된다.

네 번째 인자인 lpOverlapped는 앞서 인자들과 마찬가지로 GetQueuedCompletionStatus 함수의 네 번째 인자로 들어갈 OVERLAPPED 구조체의 값을 넘긴다.

이 함수가 성공적으로 인자로 지정된 값들을 IOCP 큐에 넣으면 0이 아닌 값이 리턴된다. 실패시에는 0이 리턴되며 이 때는 GetLastError 함수를 호출해서 에러의 원인을 찾아볼 수 있다.

예제 프로그램의 실행 화면은 그림 2와 같다.

< 그림 2. 예제 프로그램의 실행화면 >

이 것으로 IOCP에 대한 장황한 설명을 마치겠다. 아마 이해하기가 그리 쉽지 않을 것이다. 필자의 경우에도 이를 이해하는데 상당한 시간을 소모했으며 위의 예제 프로그램을 바탕으로 실제 환경하에서 동작하는 프로그램을 만드는데도 상당한 시간을 보냈다. 이해하기는 어렵지만 IOCP는 스레드을 최대한으로 활용할 수 있도록 해주는 메커니즘이다. 특히 소켓으로 다중 사용자의 요구를 처리해야 하는 프로그램을 만들어야 한다면 IOCP는 최적의 솔루션이 아닌가 싶다.

참고문헌
1. INFO: Design Issues When Using IOCP in a Winsock Server (Q192800) - http://support.microsoft.com/default.aspx?scid=kb;EN-US;q192800
2. Programming Server-Side Applications for Microsoft Windows 2000, Chapter 2 Devico I/O and Interthreaded Communication
3. Writing Windows NT Server Applications in MFC Using I/O Completion Ports - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpic/html/msdn_servrapp.asp
4. UNBUFCPY, SOCKSRV ? Microsoft Platform SDK IOCP 윈속 예제 프로그램
5. Windows Sockets 2.0: Write Scalable Winsock Apps Using Completion Ports - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/Winsock.asp

김치 콩나물국

 

출처 : http://kr.blog.yahoo.com/yaggo_21/1145520?c=9

★ 국민의 해장국, 김치 콩나물국

아마도 김치 콩나물국 많이들 끓여 드실거예요.
맛짱도 마땅한 국거리가 없다 할때 제일 만만하게 발리 준비하는것이 김치 콩나물 국이예요.

그렇지만.. 쉬워보여도 초보님들이 어렵다고 생각하는 것이 국이라고들 질문을 주시어
아래 맛내기 포인트 몇가지 적고 지나갑니다.

[재료] 멸치다시물 1.5리터 (멸치 10마리, 다시마 사방 8센치 크기 1개)
          콩나물 150그램, 배추김치 100그램, 김치국물 5숟가락, 다진마늘 3분의1 숟가락, 대파, 굵은소금

1. 콩나물을 씻어서 체에 건져두고, 멸치육수를 만든다.
2. 김치는 대충 털어내고 채를 썬다
3. 멸치육수가 만들어지면 1의 콩나물과 김치를 넣고 끓여준다.
4. 3에 다진마늘은 넣고 한소큼 끓여준다.
5. 4가 끓으면 굵은 소금으로 간을 맞춘뒤에 어슷썰은 대파를 넣는다.

* 미리 만들어 놓은 육수라면 콩나물과 김치를 넣고 두껑을 닫은후에 끓여주고,
즉석에서 만들어 끓이는 육수라면 육수를 만들어 콩나물을 넣은 뒤에 뚜껑을 닫지 않고 콩나물 국을 끓여야
콩나물 비린내가 나지 않고 국맛이 깔끔하고 시원하다.

* 김치의 양이 너무 많거나 김치 국물이 많이 들어가면 국물에서 쓴 맛이 날 수도 있으니 적당량 조절한다.

이 김치 콩나물국은 재료도 착하고 만들기도 쉬운 국이예요.
그렇게 간단하고 착한 재료이면서도.. 술 드신후에 속풀이 해장국으로도 아주 좋은 '국민의 해장국'이지요.

김치 콩나물 국은 위에 *표시한 두가지만 주의를 하면 시원하고 얼큰한 국이 만들어 진답니다.

시원한 콩나물국 끓이기, 두가지 방법

 

출처 : http://kr.blog.yahoo.com/yaggo_21/1113197

시원한 콩나물국 끓이기, 두가지 방법

오랜만에 여고동창 긴통화로 수다를 떨었네요.
다른 이야기가 아니라, 친구는 요즘 요리를 배운다고 하더군요.

친구의 가족이 전부미식가라 그 입맛에 부흥하여 음식을 만들어야 한다고...ㅎ
그럼, 무엇을 배웠냐고 했더니, 여러가지를 배웠는데, 나물이 전혀 원하는 맛이 아니라고 하네요.

그래서 또, 제가 그랬지요. 그럼.. 내가 가르쳐 주는 대로 한번해봐라.

"콩나물이 가장 구하기 쉬운 재료 임에도 불구하고,
콩나물 고유의 맛을 살리면 시원하고 맛있게 끓이기가 어렵다고
울카페 회원님들의 질문이 많이 들어오니..
니가 요리학원에서 배운 콩나물국 말고, 맛짱식으로 그대로 끓여 보라구..."ㅋ

"글구 맛 없으면 앞으로는 전화하지 말고(ㅋ, 농담~) 맛있으면 학원가지 말고 나한테 배우라고..ㅎ"

친구는 낼 아침에 끓여 먹고 전화를 한다고 하고 끓었답니다.
친구의 입 맛에 잘 맛을지 저도 궁굼하네요.

아래는 친구에게 가르쳐준 콩나물국 입니다.
혹시 여러분도  콩나물국 끓이는 방법이 저와 다르다면, 아래방법으로 끓여서 맛이 어떤가 말씀해 주세요~^^*

◈  시원한 콩나물국 끓이기, 두가지 방법 ◈

##  방법1: 시원한 콩나물국 끓이기-생수,멸치액젓,소금,마늘,대파  ##

콩나물(한대접 약간 넘는양)을 깨끗이 씻어서,

물(생수, 수도물)은 5컵을 넣어주고,

뚜껑을 덮고 끓이기 시작을 합니다.

팔팔 끓기 시작을 하면 불을 줄이고 약간 불에서 조금 더 끓이다가,

마늘을 넣고, 멸치액젓을 한수저~한수저 반을 넣은 뒤에 간을 보아 싱거우면
소금으로 간을 해 줍니다.

대파를 썰어서 넣어주고 우르륵 끓인뒤에 불을 꺼주고,
(파가 떨어져서 냉동실에 있는 육수용 대파 잎사귀를 넣어 주었습니다.
대파줄기 부분을 썰어서 넣어주세요)

개운하고  깔끔한 맛의 콩나물국 입니다.
( 기호에 맞게 고추가루를 풀어 드셔도 됩니다. )

## 방법2: 시원한 콩나물국-육수,새우젓,청·홍고추,대파,소금,마늘 ##

멸치, 마른표고,다시마를 넣어 육수를 넣어 줍니다.
( 육수용 멸치가 떨어져 작은 멸치를 대신 하였습니다. )

이긍~ 사진이 넘, 안나왔네요..^^;;
( 콩나물 한대접 약간 넘게, 대파, 매운맛을 내기 위한 고추 )

육수에 콩나물을 내어 콩나물을 넣고, 뚜껑을 열고 끓여 줍니다.
(육수가 식은 것이라면, 뚜껑을 닫고 끓이셔도 됩니다.)

콩나물국이 끓으면 청홍고추를 넣어 끓여주고,

다진마늘을 넣고, 새우젓을 반수저(새우젓의 간에 따라 가감, 조금씩 넣으면 간을 보세요.)
넣은뒤에 간을보아 싱거우면 소금으로 간을 맞추고,
대파를 넣어 우르륵 끓이면 완성~
(사진이 잘 안나왔지만, 그냥 올렸습니다.^^;;)

새우젓으로 간을하여 국물 빛은 덜 맑지만, 시원한 콩나물 국이랍니다.
(청홍고추를 넣어 매콤, 시원하지만, 기호에 따라 고추가루를 더 넣으셔도 됩니다.
다시마는 육수를 내고 나온 다시마를 채썰어 올려서 드셔도 좋답니다.)

볼린저 밴드 활용편-기초

 



보조지표를 쓰려면 어떤 원리로 값이 산출되는지 돌아가는 매커니즘은 알고 써야 된다.
원리도 모른채 단순히 선 찍찍 그여지는대로 따라하기만 한다면 그것 또한 뇌동매매에 불과하다.
원리를 이해한다면 왜 기본값 그대로 쓰지 않고 2단으로 개조를 하는가에 대한 합당한 이유를 느낄 수 있으리라 본다.
그래서 잠깐 짚고 넘어가자면..
기본적으로 볼린저밴드는 중간선, 상한선, 하한선 3개의 선으로 이루어져 있는데
여기서 중간선은 이평선과 똑같다. 기본값이 20이므로 이평선의 20선과 같다.
상한선과 하한선은 표준편차를 통해 얻어진 값인데 볼린저밴드의 두번째 파라메터에 의해 정해진다.
기본값이 2 이므로 표준편차 범위를 +-2로 잡아서 나온선이 바로 상한선과 하한선이다.
즉 볼린저밴드는 단순하게 평균을 구한 이평선+표준편차로 구한 상한선,하한선 총 3개의 선으로 이루어져 있다.
우선 다음의 그림을 보자.  (선지 5분봉)
 
바깥쪽에 위치한 2개의 선이 표준편차2의 상,하한선이고 안쪽에 위치한 2개의 선이 표준편차1의 상,하한선이Bollinger_a다.
초록색으로 표시 된 부분이 표준편차1의 밴드폭이고 초록색+똥색으로 표시 된 부분이 표준편차2의 밴드폭이다.
표준편차2의 밴드폭 내에서 주가가 놀 확률은 95.45%이며 반대로 범위를 벗어날 확률은 4.55%에 불과하다.
또, 표준편차1의 밴드폭 내에서 주가가 놀 확률은 68.27%이며 반대로 범위를 벗어날 확률은 31.73%이다.
(전에 누가 이 확률은 어떻게 구한것이냐고 묻던데 나보고 묻지말고 고등학생때 보던 정석 펴고 표준편차 보자-_-)
볼린저밴드를 기본값으로 쓰다보면, 상,하한선 그리고 중간선 그 어디에도 걸치지 않았는데 어중간한 부분에서
지지와 저항을 받는것을 자주 확인 할 수 있다. 그래서 볼린저밴드가 형편없는 보조지표라 생각해보지 않았나?
그러나 그전에 도대체 왜 엄한데서 지지/저항을 받을까?하고 의문을 품어보긴 했나?
내가 2단 볼린저밴드를 쓰게 된 경위는 바로 여기서 부터 출발한다.
파라메터를 이리저리 바꾸어 보며 시뮬레이션 해보고 테스트 하다 보니 2단 볼린저밴드라는 결론을 얻은것인데
왜 그런가를 한 번 보자.
볼린저밴드의 상한선은 저항선으로, 하한선은 지지선으로 작용하게 되는데 이론적으로 보면
표준편차2의 상한선이 저항선으로 그리고 하한선이 지지선으로 작용 할 확률이 95.45%이며
주가가 밴드폭을 벗어나서 반대로 상한선이 지지선으로 그리고 하한선이 저항선으로 작용 할 확률은 4.55%에 불과하다.
따라서 노란 동그라미로 표시해둔 부분 처럼 일순간 벗어나더라도 4.55%의 확률로 지지, 저항 받는 경우는 거의 없고
평균회귀 현상으로 인해 이내 곧 밴드로 재진입하게 된다.
그럼 표준편차1의 경우는 어떨까?
표준편차1의 밴드폭을 벗어날 확률은 31.73%나 되기 때문에 밴드폭을 벗어난 뒤에
상한선이 저항선으로 하한선이 지지선으로 확률도 31.73% 라고 볼 수 있다.
한마디로 똥색으로 표시한 부분에서 주가가 놀 확률이 31.73%라고 볼 수 있다.
그래서 평균회귀 현상을 보이기는 하지만 표준편차2의 경우 처럼 확실하고 즉각적이진 않다.
특히 볼밴 특유의 선을 타고 한 방향으로 내지르는 경우는 특히 더 그렇다.
하지만 확률은 어디까지나 이론적인것이기 때문에 경험적으로 보자면
표준편차1은 밴드내에서 놀 확률 : 벗어날 확률을 7:3으로 보는것이 아니라 5:5로 보는것이 좋다
즉, 볼린저밴드를 기본값으로 쓸 때 안보이던 어중간한 부분의 지지, 저항은 바로 이 표준편차1의 상,하한선이라고 보면 된다.
표준편차를 1이 아니라 1.5일 수도 있고 0.5으로 할수도 있지만 내가 얻어 낸 결론은 1이 최적값이다.
원래대로라면 표준편차2의 상한선에 닿았을때 매도(풋)를 하고 중간선에서 지지가 실패하면 청산을 하는 식이지만
2단 볼린저밴드를 쓰므로써 주가가 중간선까지 가는것을 기다리지 않고 표준편차1의 상한선에서 지지 실패를 확인하고
청산을 한다면 기존보다 한박자 빠른 대응이 가능해진다.
Bollinger_aa_1
노랗게 표시 된 부분에서 표준편차1의 상,하한선이 지지,저항선으로 작용하는 것을 확인 할 수 있으며
표준편차1의 상,하한선이 지지,저항에 실패한다면 대부분은 중간선까지 그냥 뚫리는 것도 확인이 된다.
한박자 빠른 대응이 가능함을 확인 할 수 있는 대목이다.
그러나 개인적으로 볼린저밴드를 장의 흐름을 읽을 수 있는 보조지표로 사용하지
직접적인 매매 신호를 주는 보조지표로 사용하지는 않는다.
정확한 매매 타이밍은 다른 보조지표로 잡되 볼린저밴드는 참고만 하자.
중간선이 평행하다고 가정 했을때 상단의 똥색 범위에 주가가 있을때는 다른 보조지표의 매도(풋) 시그널
하단의 똥색 범위에 주가가 있을때는 매수(콜) 시그널의 신뢰성을 더하는 식으로 말이다.
(하지만 볼린저밴드가 횡보하다가 밴드폭이 넓어지며 주가가 밴드를 타는 시그널은 백방이다.
이 시그널은 무조건 순응해야 되며 절대로 무시하고 반대로 가면 안된다.)
한마디로 2단 볼린저밴드는 볼린저밴드 2개를 겹친것이다.
단지 하나의 보조지표로 만들어 간편하게 쓸 수 있다.
설정법은 의외로 간단하다.
먼저 키움 기준.
1) 차트에서 우클릭->수식관리자
2) 사용자지표 새로만들기
3) 지표명 : 2단 볼린저밴드
4) 수식
수식1 이름 : 2단 볼린저밴드 중심선 %Period%
수식1 수식 : BBandsC(Period,D1)
수식2 이름 : 상단%D1%
수식2 수식 : BBandsUp(Period,D1)
수식3 이름 : 하단%D1%
수식3 수식 : BBandsDown(Period,D1)
수식4 이름 : 상단%D2%
수식4 수식 : BBandsUp(Period,D2)
수식5 이름 : 하단%D2%
수식5 수식 : BBandsDown(Period,D2)
5) 지표조건설정
Period : 20
D1 : 1
D2 : 2
6) 라인설정은 알아서
7) 스케일 : 가격
다음은 이트레이드 기준. 키움과 과정은 같고 수식만 다르다.
1) 차트에서 우클릭->수식관리자
2) 사용자지표 새로만들기
3) 지표명 : 2단 볼린저밴드
4) 수식
수식1 이름 : 2단 볼린저밴드 %적용기간%
수식1 수식 : BBandsMid(가격,적용기간,이평방법)
수식2 이름 : 상단%승수1%
수식2 수식 : BBandsUp(가격,적용기간,승수1,이평방법)
수식3 이름 : 하단%승수1%
수식3 수식 : BBandsDown(가격,적용기간,승수1,이평방법)
수식4 이름 : 상단%승수2%
수식4 수식 : BBandsUp(가격,적용기간,승수2,이평방법)
수식5 이름 : 하단%승수2%
수식5 수식 : BBandsDown(가격,적용기간,승수2,이평방법)
5) 지표조건설정
적용기간 : 20
승수1 : 1
승수2 : 2
가격 : (고가+저가+종가)/3
이평방법 : 단순
6) 라인설정은 알아서
7) 스케일 : 가격

 

문덕큰입베쓰
나도 밴드 저렇게 2단으로 놓고 보는데.. 매매에 적용하는 법이 전혀 다르긴 하지만

말보로
뭐 사람마다 다르긴 하지만 장중매매시에는 20이평만 봐도 충분하다고 봄. 난 장중매매용 차트엔 이평선 버렸지

볼린저 밴드 활용편

 

출처 : DC

By 말보로

 Bollinger
내가 갤로그를 만들고 처음 썼던 글이 볼린저 밴드를 2단으로 개조하는 글이었는데
(http://gallog.dcinside.com/marlboros/112363106373901000)
볼린저 밴드의 세가지 특성을 요약해보라고 하면,
1) 상단, 하단선은 각각 지지, 저항선으로 작용한다.
2) 상하단 밴드폭이 벌어지며 주가가 붙을때는 그쪽 방향으로 나아간다.
3) 주가가 밴드폭을 벗어 났을 경우 밴드내로 재진입 할 확률은 95%에 이른다. (표준편차 2를 사용 할 경우)
그런데 저기서 '주가' 대신에 RSI 를 대입해보자.
뭔가 번뜩하지 않나?
그렇다.
주가 대신에 RSI든지 CCI든지 Stochastic이든지 무엇이든 집어 넣어서
볼린저밴드가 가지는 세가지 특성을 모조리 이용 할 수 있다.
그렇게 해서 얻는 결과는 무엇일까?
일단 죽은 놈 부랄 만져보자. 
Bollinger_1
오늘자 선지 5분봉이다.
아래쪽 보조지표중 아래가 기본 RSI이며 위가 RSI에 볼린저 밴드를 적용한 BB-RSI (Bollinger Bands RSI)다.
주가에서 사용할때와 마찬가지로 RSI값이 볼린저 밴드폭 내에서 놀고 있다.
위 차트에서 부분적으로 떼어내서 다시 한 번 보자. 
Bollinger_3
주가 차트에선 그 어떤 추가 상승의 단서도 보이지 않고 있는데, 아래쪽 BB-RSI를 가만히 보면
RSI가 상승하며 밴드가 벌어지고 있다. 바로 볼린저 밴드의 특성을 그대로 나타내며 추가 상승에 대한 단서를 알려준다. 
Bollinger_4
예상대로 주가가 추가 상승을 했지만 여기서 이상 징후가 포착 된다.
RSI 값이 과매수 구간에 도달하기도 전에 밴드폭을 벗어나기 시작한 것이다.
매번 강조하듯이 밴드폭을 벗어났을때 밴드 내로 회귀 할 확률은 '수학적으로' 95% 나 된다.
추가 상승을 할 지 어떨지는 몰라도 밴드 내로 회귀하며 하락하리라 예상 해 볼 수 있는 모양이다. 
Bollinger_5
RSI가 밴드내로 회귀하며 주가가 하락했는데, 밴드가 거의 평행을 이뤄가는 시점에서 밴드 위치를 확인해보자.
과매도 구간은 밴드 내에 포함 되어 있으나 과매수 구간은 밴드로 부터 한참이나 떨어져 있다.
주가가 큰 폭으로 상승을 하여 과매수 구간까지 이르기에는 대단히 큰 상방 추진력이 필요함을 짐작 해 볼 수 있다.
이런 식으로 볼린저 밴드의 특성을 활용하면 기존 보조지표를 조금 더 면밀히 분석 해 볼 수 있다.
꼭 볼린저 밴드가 아니더라도 이런 식으로 각자 자기만의 다양한 아이디어를 십분 활용하면 보조지표를 더 잘 쓸 수 있을 것이다.
키움용 수식은 다음과 같다.
1. 차트에서 우클릭 - > 수식관리자
2. 새로만들기
3. 지표명 : BB RSI
4. 수식1
수식 이름 : BB-RSI %RSI_Period%
수식 : RSI(RSI_Period)
수식2
수식 이름 : up
수식 : avg(RSI(RSI_Period),BB_Period) + D1 * stdev(RSI(RSI_Period),BB_Period)
수식3
수식 이름 : down
수식 : avg(RSI(RSI_Period),BB_Period) - D1 * stdev(RSI(RSI_Period),BB_Period)
5. 지표조건설정
RSI_Period : 8 (자신이 쓰는 값으로)
BB_Period : 20
D1 : 2
6. 기준선설정
70
30
7. 수식검증 후 이상없으면 작업저장.
* RSI 대신 다른 지표를 위와 같이 쓰고자 한다면 RSI(RSI_Period)라고 되어 있는 부분을 원하는 것으로 바꾸면 된다.
예) CCI의 경우 CCI(CCI_Period)로 바꾸고 지표조건설정중 RSI_Period를 CCI_Period로 변경

 

주식초보
결국은 정규분포와 중심극한정리에서 아이디어를 잘 찾으라는 것이군요 ^^ 말보로횽님 잘 봤스빈다 ^^

밍키
분봉에 ADX 합쳐서....이평선에 같이 맞추는건 못하고 이뜸...혹시 방법좀...

2009년 2월 16일 월요일

중기전략 종목 검색식

 

출처 : DC

중기전략 종목 검색식

제가 중기로 가져가는 종목 검색법입니다.

뭐 중기급으로 보면 보통 적게는 일주일에서 많게는 추세가 허용하는 선까지 보유하는 종목을 선택하는 방법입니다.
실제로 제가 여러분께 추천드리는 종목 외에 제가 제대로 거래하는 종목들을 검색하는 방법이라고도 볼 수 있겠지요.
그냥 미친 척 하고 공개하겠습니다. (그럼 앞으로 이대로 검색해서 매매하시고 전 단기던 장기던 추천 안드릴께요. 으흐흐~)
HTS는 한투의 eFriend를 사용하는 모습입니다.

scanraw

일단 제 개인적으로는 한투 HTS가 제일 좋아요. :-) 각자 본인들이 사용하시는 HTS에 맞춰서 사용하세요.
(근데 키움 HTS인 영웅문과 e-Trade의 X-ing에서 검색한 결과는 제가 보는 결과물과 다르네요. 이 단순한 검색식으로도 이렇게 신뢰할 수 없다니 좀 난감..)
이동평균 기초를 4일 평균선과 33일 평균선을 사용하는 모습을 보실 수 있습니다.
나중에 4일선과 33일선에 대해서 이야기 할 기회가 있길 바라며..
참고로 개인적으로는 최근에는 차트를 볼때도 4일선과 33일선 이외에 다른 이평선은 모두 제거를 하고 봅니다.
실제로 이 방법대로 검색시 하루에 걸려봐야 한두종목에서 많게는 세 종목정도가 탐색이 되고, 없는 날이 있기도 합니다. 종목고르는 것 하나는 확실하게 범위를 좁혀주는 효과를 가져오지요 :-) 무슨 보조지표가 어떻고 저떻고 그딴거 다 필요없습니다. 그냥단순하게 이평선과 거래량, 외국인 매수세가 있는가, 그리고 나중에 차트 보실때 추가로 볼린저밴드, 이정도만 보면 다른것은 볼필요가 없습니다. 지표를 쓸데없이 많이 봐봤자 흔히 주식시장에서 이야기하는 과최적화의 오류를 일으키게 되는것이지요. 개인적으로 단기용으로 심심풀이로 거래하는것 외에 제가 중장기로 가져가는 종목은 모두 이 방법으로 종목 선정을 하고 있으며 이 방법 사용시 지금까지 실패한적? 성공 대비 실패 비율이 10% 남짓정도 였던것 같습니다. 하다못해 상승 추세가 꺾여버리면 일단 매도함으로써 이익실현을 하면 그걸 실패라고 볼 것도 없지요.
자 일단 검색을 하셨으면 실제로 검색되었다고 대책없이 매매하지 마시고 자신만의 매매기법을 이용을 하셔서 매매를 하시는것이 좋습니다. 매매기법에 대해서는 언제 한번 이야기를 할 일이 있겠지요.
그럼 다음주도 성투하시길 바랍니다. :-)
p.s
이건 제가 이 방법으로 종목을 검색하고 거래한다는거지 이거 말고 다른건 다 구리다 이건 아닙니다. 개인마다 본인의 매매기법이 있기 마련이고, 본인의 매매기법에 알맞는 종목 선정방법이 있기 마련입니다. 저같은 경우는 전에도 이야기했지만 분명한 추세매매를 하는 사람이기 때문에 이에 따르는 거래를 할 뿐입니다. 기업의 재무분석과 가치는 고려하지 않느냐구요? 외국인이 3일씩이나 연속 매수할 정도면 매수할 가치는 충분해요 :-) 저는 제 거래기법에 맞는 종목이 이 종목일 뿐입니다. 제가 좋다고 한 종목이 여러분들께 항상 좋지는 않습니다. 그건 분명 감안하세요.
혹시 뭐 급등주 매매기법이라거나 박스권 돌파 매매법, 신고가 돌파 매매법을 원하시는 분들은 말씀을 주시면 해당 내용에 대한 종목 검색방법과 매매기법을 올려드릴 생각은 있습니다만 제 전문종목은 아닙니다. 실은 단기도 그다지 주종목은 아니예요 ^^ 그냥 일봉 대신 60분봉을 대상으로 하는 중기투자라고 생각하고 있을 뿐인거죠. 하루를 6일처럼. :-) 거래가 잦아서 세금이 후덜덜한거죠. 흐흐! 세금을 세금으로 생각하면 아깝지만, 매매기법 향상을 위한 약간의 수업료 정도로 생각하면 아깝지는 않아요. 돈을 안버는것도 아니고 잘만 하면 하루에 몇백 떨어지는 날도 많으니까 말이죠 :-)

출처 : http://ydhoney.egloos.com/3128105

주식특기생검색식참고

 

출처 : DC

키움 영웅문 HTS 기준

A 주가범위:0일전 종가가 1000 이상 50000 이하
B 이평이격도[일]1봉전(종가 5, 종가 20) : 5%이내 근접 1회이상
D 주가등락률:[일]1봉전 시가대비 1봉전 종가등락률 -4%이상 4%이하
E 가격-이동평균 비교:[일]0봉전 (종가 12)이평 <= 종가
F 주가등락률:[일]1봉전 고가대비 0봉전 종가등락률 0%이상
G 3일 평균거래량 100000이상 999999999이하 (<금일포함>)
H 주가등락률:[일]0봉전 시가대비 0봉전 종가등락률 0%이상
I 주가등락률:[일]0봉전 종가대비 0봉전 고가등락률 6%이하
J 기간내 주가변동폭:[일]1봉전까지 10봉간 종가변동폭 8%이하
K 거래량비율:[일]2봉 전거래량대비 동일주기 1봉전 150%이하

A and B and D and E and F and G and H and I and J and K 
이건 내가 만든 건 아니고 시중에 도는 특정패턴과 기법의 검색식이다.
한동안 사용 안 하다가 최근에 알게 된 원리와 이 기법이 통하는 바 있어
최근에 내 기준으로 좀더 보완수정해서 테스트 중이다.
실시간으로 돌려봐야 하고, 잡히는 종목 전부 해당하는 게 아니라
특정 이평선과 주가 위치에 따라 골라내야 하는 거다.
감 잡으려면, 일단 실시간이나 장 마감후 돌려서 잡힌 놈들을
관종에 넣어두고 당일(실시간 검색시) 움직임을 보거나 익일(장마감후 검색시) 움직임을 관찰해봐라.
그러면 매수해야 할 대상/패턴을 가려낼 수 있게 될 테니까.
근데 가장 중요한 건 이 검색식으로 잡히는 종목들의 패턴을 보며 그 원리의 맥을 짚어내는 거다.
그 원리를 깨닫고 나면 어느 종목에서나 통하니까.
난 놀러 나가야 해서 이만....
휴일 잘 보내라.

현물과 선물의 관계 및 시장 분석법

 

출처 : 불분명

현물과 선물의 관계 및 시장 분석법

2008.07.30 19:47

1. 현물과 선물의 관계

선물은 현물의 가격을 기초로 하여 가격이 형성됩니다.

즉, 선물의 가격은 금리, 배당도 영향을 미치지만 가장 많은 영향을 주는 것은 현물의 가격입니다.

향후 현물의 가격이 상승할 것으로 예상되는 경우 선물의 가격은 이론적으로 계산되는 선물가격에 비해 고평가되며,

반대로 현물의 가격이 하락할 것으로 예상되는 경우 선물의 가격은 이론적으로 계산되는 선물가격에 비해 저평가되는 것이 일반적입니다.

2. 베이시스와 괴리율

베이시스는 선물의 가격과 현물의 가격간 차이를 의미합니다.

예를 들어 코스피200의 가격이 201.82 선물의 가격이 203.70이라고 하면

두 가격의 차이는  1.88이며, 이것을 베이시스라 합니다.

여기서 이론적으로 산출되는 선물가격, 즉 이론선물가격과 현재의 선물가격간의 차이를 괴리율이라 하는데

이론 선물가격이 203.20이라면, 괴리율은 다음과 같이 계산됩니다.

(203.70 - 203.20) / 203.20 = 0.25%

아래 그림을 참고하시기 바랍니다.

18226950_1217414837

베이시스가 플러스이고, 괴리율 역시 플러스 상태일 경우 실질적인 콘탱고라고 하며,

이 경우 프로그램 차익 매수(현물 매수 + 선물 매도)가 유입되며, 지수가 상승하는 경향이 있습니다.

다만 지수가 상승하기 위한 조건이 되자면,

프로그램 매수가 유입되면 베이시스와 괴리율이 축소되어 선물 가격이 하락할 수 있는데,

선물 매수세가 유입되면서 재차 베이시스와 괴리율이 확대되는 것이 지속적으로 나타나야 합니다.

3. 선물 미결제약정

선물은 매수와 매도 어느 것을 먼저 해도 상관이 없습니다.

선물을 매수한 상태에서 매도를 하는 것 또는 매도한 상태에서 매수를 하는 것을 청산이라고 하는데,

미결제약정이란 청산되지 않은 상태의 계약을 말합니다.

선물 지수와 선물 미결제약정은 다음과 같은 상관관계가 있습니다.

- 선물 지수 상승과 미결제약정 증가 : 지수 상승 강화

- 선물 지수 상승과 미결제약정 증가에서 감소로 전환 : 향후 지수 하락 가능

- 선물 지수 하락과 미결제약정 증가 : 지수 하락 강화

- 선물 지수 하락과 미결제약정 증가에서 감소로 전환 : 향후 지수 반등 가능

4. 현물 거래대금과 선물 거래대금

현물과 선물의 거래대금은 현재의 시장이 강세장인지 약세장인지를 판단하는 근거로 활용됩니다.

하루 선물의 거래대금은 모든 매매자들의 매수 거래와 매도 거래를 합하여 산출하며

일평균 약 40조 가량이 됩니다.

이에 비해 현물의 거래대금은 25% 정도인 10조 정도가 됩니다.

시장은 현물 시장이 활성화되어야 강세장이라 할 수 있는데

통계적으로 볼 때, 현물과 선물의 거래대금비율,

즉 현선비율은 25% 이상이 유지될 경우 강세장에 진입했다 할 수 있으며, 20% 이하일 경우는 약세장입니다.

선물거래에 대해 알아 보자-콘댕고에 대한 검색

 

주식 시황/분석 : 선물거래에 대하여 알아보자

2008.06.08 12:53

 

출처 :  http://cafe.naver.com/fnsalon/131

 

선물시장은 다양하게 존재합니다.

주가지수선물, 원유선물, 금선물, 통화선물, 옥수수선물, 금리선물, 등등 .....

선물은 말 그대로 미리 사고파는 거래를 말합니다.

배추값이 오르면 밭떼기로 사니 파니 하는 입도선매가 바로 선물거래와 거의 똑 같은 형태입니다.

선물거래는 원래 현물가격의 미래가격변동 위험을 회피하기 위해 만들어진 제도입니다.

하지만 위험을 회피하기 위한 제도가 실제적으로는 고위험 고수익 투자수단으로  거래되고 있습니다.

선물 옵션 거래를 원 현물거래에서 파생되어 나온 거래라 하여 파생거래,

파생상품이라 일컫고 있습니다.

 

주가지수 선물거래에 대하여 알아보겠습니다.

홈트레이딩이나 주가전광판 혹은 신문의 주가란에 보면 종합주가지수 외에

코스피200지수라는 항목을 보실 수 있습니다.

코스피 200은 각 업종별 대표종목 200개를 선택하여

200개 종목의 평균 주가지수를 표시하는 지수입니다.

(시가총액 상위 200 종목이 아닙니다)

주가선물은 코스피200종목을 기준으로 하여 이뤄집니다.

현재 코스피 200종목의 지수가 230이라 가정하겠습니다.

그러면 선물은 0.05를 한틱으로 하여 232에 거래된다고 가정합니다.

이말은 현재 실제 코스피200은 230원이지만 향후에 232원 이상 갈 것이라고 예상하는 사람들이

미리 232원을 주고 산다는 말입니다. 232원을 주고 선물 한계약을 매수한 사람은 선물 만기 이전에는

선물지수가 232원 이상 (수수료는 따로 계상) 가야 수익이 나는 구조이며 만기일에는 코스피 200이

최소232이상 가야 수익이 나는 구조입니다 . – 매수거래

 

반면에 선물지수를 232원에 파는 사람들은 현재 선물지수가 232원 밑으로 형성되리라 예상하는

사람들이 232원을 받고 미리 파는 형태입니다.

232원을 받고 선물지수를 매도한 사람들은 만기일 이전 까지는

선물지수가 232원이하가 되어야 수익이 나는 구조이고

만기일에는 코스피 200이 232원 이하가 되어야

수익이 나는 구조입니다. – 매도거래

 

코스피 200과 선물지수와의 차이를 베이시스라고 하는데

베이시스가 양일때 콘댕고, 음일때를 백워데이션 이라고 합니다.

현재 코스피 200이 230이고 선물지수가 232이면 베이시스가 2이며 콘댕고 상태라 하고

코스피 200이 230인데 선물지수가 229이면 베이시스가 -1이며 백워데이션이라 합니다.

이론적으로는 콘댕고 상태는 향후 주가 지수가 상승쪽으로 보는 것이며

백워데이션인 상태는 주가지수가 하락쪽으로 본다는 것이지만

실제로는 시장은 콘댕고와 백워(그냥 줄입니다)가 수시로 교차하기도 합니다.

 

여러분들이 잘 알고 계시는 프로그램 매도 프로그램 매수라고 하는 거래가 바로

선물거래로 인하여 발생하는 것입니다.

코스피 200지수와 선물거래와의 차이 즉 베이시스를 이용하여 선물이 현물보다 비쌀때는 선물을 팔고

현물을 사고 현물이 선물보다 비쌀때는 현물을 팔고 선물을 사는 거래가 차익거래(무위험 거래)입니다.

이 차익거래와 옵션매도 거래의 수익이 우리나라 증권사들의 주 수익원이 되고 있습니다.

차익거래는 이론적으로는 위험이 없는 거래로 100%수익만 나는 땅짚고 헤엄치는 수익 구조입니다.

예를 들어 선물이 100이고 코스피 98이면 선물을 100원에 팔고 현물을 98원에 산다고 가정할 때

선물이 주가가 내려 코스피가 97원이 되었다고 가정하면 현물은 1원 손해지만

선물은 3원의 수익을 내었기

때문에 결과적으로 2원의 수익을 올리는 구조가 차익거래입니다.

반대의 경우도 마찬가지이고요.....

그러나 아무위험이 없이 오직 수익을 올릴 수 있는 차익거래가

잘못하면 치명적인 손실을 발생시킬 수도 있는데

그예가 98년 발생한 LTCM(Long Term Capital Manegerment)사태인데

이것은 다음에 또 말씀드리겠습니다.

대선때 동영상으로 아무위험도 없는 거래가 차익거래이며 마치 자기들이 만들어 낸 거래처럼

말하던 무식하기 짝이 없는 어느 사람이 생각나는 군요.......

 

선물거래는 3개월 마다 만기일을 정하여 정산을 합니다.

매 분기월 둘째 주 목요일이 만기일입니다.

이 날이 되면 각 거래 주체들이 취하고 있던 포지션이 무조건 정리됩니다.

주식거래에 있어 미수거래를 하면 2영업일인가 3영업일 날 돈이 입급되지 않으면

강제로 반대매매가 이뤄지듯이

선물 마감일날은 만기 마감시깐까지 거래를 하지 않으면 해당월물은 무조건 정리 되어 버립니다.

즉 선물 1계약을 232에 매수하여 만기일 까지 홀딩 하고 있었다고 가정하면

코스피 200 만기지수가 233이 되었다면  1포인트의 수익이 발생하고

코스피 200 만기지수가 231이 되었다면  1포인트의 손실이 발생합니다.

 

선물은 옵션거래에 비해서는 위험도가 작습니다.

하지만 현물 주식거래에 비하면 엄청난 위험과 수익이 오가는 곳입니다.

매일 상한가 하한가로만 가격이 결정되는 주식에 투자하는 결과와 비슷한 구조입니다.

선물 한계약을 거래한다고 가정할때

선물 일포인트당 50만원의 수익이 발생합니다.

한틱이 0.05이니 0.05당25,000원의 수익 발생하는 거래입니다.

상승을 예상하고 선물 1계약을 230에 매수하였는데 장중 231에 매도하였으면

50만원의 수익이 발생하고 반대이면 50만원의 손실이 발생합니다.

 

선물거래를 하기 위해서는 주식계좌와는 달리 선물옵션계좌를 개설하여야 가능하며

1계약 최소 개설 보증금을 납입해야 됩니다.

최소 개설 보증금은

현재  선물지수 x 50만원 X 15% 입니다

현재 선물지수가 230이라면 230 X 50만원 X 15% = 17,250,000원이 있어야

1계약을 거래 할 수 있습니다.

 

요즘 우리 시장의 선물 변동폭이 평균 2~3포인트 됩니다.

이론적으로 따지자면 상승때 2포인트 하락때 2포인트면

4포인트 즉 200만원의 수익을 올릴 수 있습니다.

1700만원으로 하루에 200만원의 수익을 낼 수 있다는 뜻입니다.

 

그러나 그 반대면 어찌 되겠습니까?

선물거래는 손실이 나면 매일 매일 금액이 자동으로 빠져나가고 입금이 됩니다.

선물매수거래를 하여 1계약을 홀딩하고 있더라도 매일 손실금액은 빠져나가고 이익금액은

자동으로 계좌에 입금됩니다. 이 과정에서 손실이 자꾸 발생하여 유지증거금을 유지하지 못하면

마진 콜(증권회사에서 계좌에 돈넣으라고 전화옵니다)을 당하게 되어 만기일 이전에라도

자동으로 정리 매매되어 버립니다.

 

선뜻 정리가 되시지는 않으시리라 생각됩니다.

대강 이런 것이 있다라고 알아두시는 정도면 됩니다.

선물옵션은 알아만 두셔야지 직접투자하시면 백전백패 최단시간내 계좌깡통이 됩니다.

선물거래는 어쩌다가 한번쯤 거래할 기회도 오지만

옵션거래는 아예 쳐다보지도 말아야 합니다.

 

ELD. ELS. ELF 상품들은 바로 선물옵션의 수익구조에 의해 발행되는 상품들입니다.

원금의 90%이상을 채권에 투자하고 채권투자에 의해 발생하는 이자만으로

선물옵션에 투자하여 수익구조를 취하는 ELD. ELS. ELF 상품은 시장의 상황에 따라

적절한 투자 수단이 되기도 하지만 원금의 70% 90%를 파생상품에 투자하는

ELD. ELS. ELF 상품은 고수익을 올릴 수도 있지만 신문에 난 것처럼

1억이 80만원 될 수도 있는 것입니다.

다음에는 옵션거래에 대하여 알아보기로 하겠습니다.

옵션 거래에 대하여 알아보자

 

옵션 거래에 대하여 알아보자

주식/시황분석

2008.06.08 13:44

출처 : http://cafe.naver.com/fnsalon/132

 

옵션은 선물에 대한 프리미엄 거래입니다.

예를 들면 배추밭을 한떼기에 100원을 주고 산 사람이 있다 가정하겠습니다.

그런데 배추값이 오르고 배추가 풍년이라 100원을 주고 배추밭 한떼기를  산사람은

큰 수익을 올리게 되었습니다. 아직 배추는 자라고 있는 중이라 가정합니다.

그런데 100원을 주고 배추밭 한떼기를 산 사람이 그냥 110원이나 120원 정도 받고

팔려고 하면 돈이 너무 크니 예상되는 수익분 10원 혹은 20원만 팔려고하면

9원에 사려는 사람 11원에 사려는 사람들이 나타 날 수 있고

9원의 프리미엄을 주고 산사람은 나중에 배추가 다 자라서 시장에 출하되어

110원이나 120원에 가격이 이루어지면 1원이나 11원을 수익을 올릴 수 있습니다.

그러나 배추값이 그동안 변동이 되어 도로 100원이 되어 버리면

9원을 주고 프리미엄수익을 기대한 사람은 꽝이 되어버립니다.

9원의 프리미엄을 받고 판사람은 배추값이 오르던 내리던 그냥 9원의 수익으로 고정되는 것이지요

 

옵션거래는 선물거래와 코스피200을 근간으로 하여 이루어지는 거래입니다.

옵션은 매수거래(콜옵션 매수, 풋옵션매수)와 매도거래(콜옵션매도, 풋옵션매도)로 이루어 집니다.

콜옵션매수와 풋옵션 매도는 주가 상승시 수익구조를 취할 수 있으며

풋옵션매수와 콜옵션 매도는 주가 하락시 수익구조를 취할 수 있는 구조입니다.

그런데 매도거래는 엄청난 증거금을 요구하기 때문에 개인들이 거래하기 힘들며

매도자가 절대적으로 유리한 옵션거래에서 개인들이 수익내지 못하는 이유가 여기에 있는 것입니다.

옵션거래는 방향성(오르고 내리고)을 맞춰야 하는것 + 시간가치 감소라는 리스크가 있습니다.

즉 정해진 기간안에 그 지수에 도착해야하고 도착한다 하더라도 시간가치 감소라는

리스크 때문에 방향성을 맞추었다 하더라도 타이밍이 정확하지 않으면 많은 손실이 발생합니다.

 

옵션의 스프레드는 2.5이며 옵션가격은 230 232.5 235 227.5 225등의 가격으로 이루어 집니다.

지금현재의 지수와 가장 근접한 옵션가격을 등가격이라 하고

등가격 이내의 가격을 내가격 등가격 외의 가격을 외가격이라하며

무슨일이 있어 주가가 급등 급락을 하면 콜옵션 풋옵션 외가격에서는

두배 세배 심지어는 열배 스무배의 가격 변동이 있게 됩니다.

911사태 때 풋옵션 외가격에서 하루만에 미국 옵션시장에서

무려 900배의 수익이 나왔다는 것은 잘 알고 계실 것입니다.

옵션은 만기(옵션은 매달 둘째 주 목요일이 만기일입니다)전 까지는 선물지수에 따라

가격이 춤을 추지만 만기일은 코스피 200을 기준으로 정산합니다.

 

예를 들어 보겠습니다.

현재 선물지수가 230 코스피 200이 228이라 가정하고

콜옵션 232.5를 5만원의 프리미엄을 주고 삿다고 가정합니다.

옵션 만기일 이전에는 선물지수에 따라 콜오션 232.5의 프리미엄이

6만원 또는 10만원이 될 수도 있고 이 과정에서 적절하게 콜옵션을 청산하면

1계약당 만원 또는 5만원의 수익을 올릴 수 있지만

주가가 하락하면 50% 또는 프리미엄의 최소값인 천원에 청산하여야 할 경우가 생깁니다.

장이 상승하리라 예측하고 5만원짜리 콜옵션을 매수하였는데

장이 다행히 상승하여 콜옵션 232.5가 십만원이 되었다면 하루만에 100%의 수익을 내는 것이고

반대일 경우 옵션 프리미엄의 최저가인 1,000원에 오만원짜리 콜옵션을 청산하여야 하는 경우가

이론적으로 발생하는 것이 옵션 거래입니다.

그러나 만기일의 경우는 다릅니다.

콜옵션 232.5를 오만원에 매수하였다고 할때 만기일날 코스피200(선물지수가 아닙니다)이

232.49가 되어도 콜옵션 232.5의 정산가격은 0입니다.

코스피 200지수가 232.6이 되었을 경우 수익은 -49,000원입니다.

왜냐하면 오만원의 프리미엄을 주고 삿는데 실제 정산수익은

232.6-232.5 = 1,000원이기 때문입니다. 최소한 코스피 200이 233은 되어야 본전이 됩니다.

 

이해가 선뜻 되시지 않습니까?

이해 안하셔도 됩니다.

다만 옵션거래는 잘못하면 계좌의 전금액이 하루만에 0이 될수도 있고

이론적으로는 몇백배의 수익을 올릴 수도 있는 거래입니다.

신문에 난 것과 같이 1억이 80만원이 되는 이유가 바로 선물옵션을 수익구조로하는

포트폴리오를 구성한 펀드였기 때문에 일어난 일입니다.

 

옵션거래는 절대적으로 매도자가 유리한 게임입니다.

매수거래를 할 수 밖에 없는 개미들은 어쩌다 대박의 기회를 잡을 수 있지만

결과적으로 계좌가 0이 되는 시간은 얼마 걸리지 않습니다.

수 많은 천재들이 옵션시장에서 대박을 바라고 투자하지만

결국은 소리없이 사라집니다.

 

옵션거래는 관심을 가질 필요도 없고 그냥 상식수준으로 그런것이 있다 정도로만

이해하시기 바랍니다.

옵션 거래를 하시는 순간 가정파탄자 신용불량자 파산자 노숙자가 되는 길로

들어선다는 것만 명심하시면 됩니다.

                                                                             원이아빠

현물과 선물이란?

 

출처 : DC

대표적인 선물 상품으로는 코스피200 선물과

코스닥50 선물 지수 두가지가 있습니다.

만기에 따라 코스피200 07년 3월물, 6월물 , 9월물, 12월물

이런 식으로 부르게 됩니다.

코스피200 선물은 1계약 단위가 50만원입니다.

1계약을 거래하기 위해서는 증거금 1500만원이 필요하구요.

2계약을 거래하기 위해서는 3000만원이 필요하겠죠.

코스닥50 선물은 1계약 단위가 10만원이고

증거금은 아마 300만원인가 500만원인가 그럴껍니다.

현물처럼 미수, 당일매수, 당일매도 모두 가능합니다.

그러면 중요한 손익률에 대해 설명드리겠습니다.

금일 코스피200 07년 3월 선물 지수가 186.95 이군요.

그러면 이에 대해서 두가지 상품을 사고 팔 수가 있는데

하나는 '매수' 상품이고, 하나는 '매도' 상품입니다.

'매수'를 1계약 샀는데 코스피200 07년 3월 선물 지수가

186.95에서 187.95로 1포인트가 올랐다...

그러면 50만원 x 1배를 벌게 되는 것입니다.

또한 '매도'를 1계약 샀는데

코스피200 07년 3월 선물 지수가 186.95에서 184.95로 2포인트가 떨어졌다...

그러면 50만원 x 2배인 100만원을 벌게 되는 것입니다.

즉 방향성만 맞으면 대박 혹은 쪽박을 찰 수 있는게 바로 주식 선물 시장입니다.

1억을 현물에 투자했는데

1500P -> 1650P 로 올랐다면 10% 수익을 올려 1천만원의 수익을 올리지만

코스피선물이 150 -> 165로 15포인트 올랐다면

1억의 증거금으로 구매 가능한 선물이 약 7계약이므로

50만원 x 7계약 x 15포인트 = 5250만원의 수익을 올릴 수 있습니다.

그러나 반대로 선물에서 매수 포지션이 아닌 매도 포지션으로 구매를 했다면

5250만원을 잃는 게임입니다.

선물 지수는 현물 지수와 동일한 비율로 오르고 내려야 하는 것이 원칙이나

수급에 따라 만기일 전까지는 실제로는 그렇지가 않습니다.

현물은 3포인트쯤 올랐는데, 선물은 0.05포인트 빠지고 이런 경우가 많죠.

이를 베이시스가 콘댕고 혹은 백워데이션이라고 하는데...

어쨋든 선물 지수가 만기일에 가서는 현물과 일치해야 합니다.

총알이 풍부한 기관이나 외국인들은

이러한 차이를 이용해 차익거래를 실현하기도 합니다.

또한 같은 선물인데도 07년 3월물과 07년 6월물의 가격이 다르게 되는데

이를 스프레드라고 하며, 이 차익을 이용해 거래를 하기도 합니다.

그리고 옵션은 더욱 복잡한 또다른 세계입니다...

거래를 하시려거든 은행에 가셔서 선물계좌를 개설해달라고 하시면 됩니다.

도움이 되었는지요?

잃지 않는법

 

출처 : DC

음봉에서 사라.
그게 진리다.
그렇다고 떨어지는 칼날 잡으면 병신이고.
그럼 어떤 음봉에서 사느냐.
꼭 음봉이 아니더라도, 현재 급속히 매물 쏟아지는 때 사란 거다.
올라가는 놈 따라 사는 버릇 고치고,
떨어지는 놈 기다렸다가 사라. 오케이?
특히 장 후반에 특정 이평선 돌파 하고 나서
그거 보고 달려든 하루살이들 떨어내려고
하락시키는데, 그때를 노려서 중요한 이평가격 2호가 정도 위에서 기다리고 사면 된다.
세세한 설명도 해야 알아먹겠지만,
어느 정도 매매경험과 기본 있는 애라면 알아듣겠지.
오르는 놈 따라 사지 말고,
올랐던 놈 급속도로 떨굴 때를 기다려서 멈출 때 사라.
오케이?
파동에 대한 공부도 해야 하고.
추가하락이 있을 경우도 있으니까.

코스피 비정상? 2009/02/12

 

출처 : DC

By 주식특기생


딱히 알려줄 게 없어. 이유가 뭐냐면, 기존에 널리 알려진 기법으로 하는 거거든. 눌림목, 전고점 돌파, 상승파동, 이평선매매, 거래량과 매수주체 분석... 다만, 나는 좀 매수할 자리를 끈기있게 기다리는 정도라고나... 양봉 보고 올라타지 말고 기다렸다가 사면 버는 확률이 더 높아. 사는 데 급급해 말고, 종목 하나 골라서 그냥 계속 구경해봐.

 

 

 

 

지금 코스피가 얼마나 비 정상적이냐 하면 말이지...

 

올초하고 비교했을때...
다우년이 10.3% 정도 빠졌지...
숙희년은 반대로 6% 상승했고....
올 1월동안의 갭이 무려 16% 야....
그럼 지난 12월로 가볼까...
다우년이 11% 정도 빠졌었어...
숙희년은 10.6% 정도 상승했고....
12월과 비교시 갭이 22%에 달해...
원래 그럼 숙희년과 다우년이 디커플링 된거냐고.... 혹은 하락기에 숙희년이 다우년보다 더 많이 빠져서 낙폭을 줄인거라고 볼수도 있다고??
그런데 이전에는 그러지 않았거든....
작년 9-11월 사이를 보면
숙희년이 -27%, 다우년이 -25%로 거의 비슷했어....
그동안 우리 경제가 전세계 증시를 모두 쌩깔 정도로 좋진 않았잖아.. 나쁘면 나빴지...
12월 기준으로 키 맞추기를 하면....930
1월 기준으로 키 맞추기를 하면 1002야....
숙희년의 오버슈팅 감안해도 최소한 1100 아래에서 놀고 있어야 하는데....
참, 지금의 오버슈팅이 나중에 어떻게 부메랑으로 돌아올지 걱정이다....

 

원광법사
너는 너고 나는 나다

화로구이
주식을 머리로 하면 이해 안되는거 투성이라...

원광법사
머 이런 정도

흐컁
나도 그런 생각으로 훗을 들고 있지....하지만 현실은 깡통 계좌 ㅠㅠ

경방
한방 터진다 고로 난 조만간 또 풋 대량 매입한다

ㅇㅇㅇ
내말은 장기적으로 보면 이란 말이야.... 당장에 일어나고 있는 일들은 더 미시적인 요소가 작용을 하겠지... 예를 들면 메져의 의도 등과 같은...

의미없는
분석이다 중국장 오르는것도 고려해야지 미국만 보냐

ㅇㅇㅇ
중국이랑 어떻게 비교하냐.... 저 나라는 그래도 올해 6% 이상의 성장을 보고 있는 나란데...

ㅋㅋㅋ
다우년이 비정상?

비교가 아니..
우리니라 장은 다우뿐만이 아니라 상해나 홍콩의 영향도 많이받는단 예기다. 조선철강 그런 중국주들. 미국이 다가 아니다

ㅇㅇㅇ
전세계 증시에서 중궈와 브라질 빼면... 다우년과 비슷할걸... 홍콩도 마찬가지고....

ㅇㅇㅇ
그래서 몇몇 특수한 나라를 제외하면 해외 펀드 의미 없다.....


나도 대충 다우뇬하고 비교해보는데 울나라 증시가 미쳤다고 볼 수 밖에 ..난 요즘 장이 미쳤다고 밖에 볼 수 없는거 같다. 점점 주식에 자신이 없어져

절대지존
08년 1월이랑 10월엔 한국이 미국보다 10% 더 폭락했다. 결국 셈셈

[보조지표] 키움용 HMA (Hull Moving Average)

 

출처 : http://gall.dcinside.com/list.php?id=stock&no=2320098

By 말보로

제 목
[보조지표] 키움용 HMA (Hull Moving Average)

차트를 이용하는 분석중에 가장 많이 쓰이는 것이 아마도 가격이동평균 즉, 이평선일텐데
무슨 몇일 이평에 닿으면 어쩌고 저쩌고 몇일 이평을 넘으면 어쩌고 저쩌고 하는등
차티스트들이 이평선에 대단히 중요하고 많은 의미를 두는 것을 자주 목격하게 되는데
문제는 기존에 우리가 이용하는 이평선이 메커니즘 측면에서 결함을 갖고 있다는 점이다.
대표적인 것이 가격변화를 재빠르게 반영하지 못해 가격과 이평선의 갭으로 인해 발생하는 후행성과
여기에서 파생되어 나온 가격에 비한 오버슈팅 또는 언더슈팅 문제등 여러가지 문제가 많다.
그 동안 한마디로 절름발이 이평선을 갖고 갖은 의미를 부여해왔던 것이라고 할 수 있다.
물론 기존 이평선에 아주 의미가 없는 것은 아니지만 태생 자체가 결함이 있는데 그러한 부분을 감안하지 않고
무조건 절대적인 의미를 부여하는 우를 범하고 있었다는 뜻이다.
앞으로 기회가 될 때 마다 기존 이평선의 이러한 문제점들을 개선하려고 노력한 다양한 이평선들을 올릴 예정인데
내가 아는 목록만 해도 5~10개는 되는 것 같다.
오늘 올리는 이평선은 Hull Moving Average라는 것으로 기존의 가중 이동 평균과 비슷하나 아래 그림에서 볼 수 있듯이
주가에 훨씬 더 민감하고 재빠르게 반응한다.
기존 이평선에 대한 사고의 전환이 되는 계기가 되었으면 한다. 
키움용 HMA
오늘장 5분봉
주황색선 : 기존 가중 이동 평균 14
노란색선 : Hull Moving Average 14
보조지표 만드는 방법
1. 차트에서 우클릭 - > 수식관리자
2. 지표명 : HMA (Hull Moving Average)
3. 수식
수식 이름 : HMA %Period%
수식 : MA(2*MA(가격, Period/2, 가중) - MA(가격, Period, 가중), Sqrt(Period), 가중)
(위와 같은 방법으로 다른 수식탭에서 Period1, Period2등으로 변수명을 다르게 하여 이평선을 여러개 만들어도 된다.)
4. 지표조건설정
가격 : 종가
Period : 14
5. 스케일 설정 : 가격
6. 수식검증 후 이상 없으면 작업저장.

추세를 따라가보자 - 5014 추세 매매 시스템

 

출처 : DC

By 말보로

 수식_malboro
CCI (Commodity Channel Index)는 우리가 늘상 보는 S&P 따위의 해외 선물 트레이더들에게서
유독 인기가 좋은 보조지표 중 하나인데, 선물 트레이더질만 20 년 이상한 Woody라는 영감이 고안한
Woodies CCI 라는 매매 시스템이 유명한것을 비롯 CCI를 이용하는 각종 매매 시스템이 있다.
오늘 소개하는 것은 내가 고안한 것은 아니고 Dr. Bob 이라는 사람이 고안한 5014 시스템을 베이스로 하여
내가 애용하는 ADX를 첨가하여 설명 해볼까 한다.
5014란 CCI의 기본 파라메터 값 14와 파라메터 50의 CCI를 조합하여 매매 타이밍을 포착하는 시스템이다.
일단 ADX와 CCI의 보조지표 셋팅을 먼저 아래와 같이 한다.
1. ADX
지표조건설정 : 기본값 (14)
라인설정 : 침체 25
기준선설정 :  25
2. CCI 50
지표조건설정 : 50
라인설정 : 과열 100 / 침체 -100
기준선설정 : 100 / -100
3. CCI 14
지표조건설정 : 14
라인설정 : 과열 0 / 침체 0
이렇게 보조지표를 장착하고 차트를 그림으로 한 번 보자. 
CCI
상당히 복잡해 보이지만 알고보면 매우 명료한 시스템이다.
1. 가장 중요한 룰
첫번째 룰은 빨간박스에서 보이듯이 ADX가 25 이하 일때는 모든 시그널을 무시하는것이다.
짤방에서 보다시피 ADX가 25 이하 구간에 머물때는 주가가 횡보 할 확률이 높고 횡보한다는 것은
추세추종자가 먹을만한 추세가 발생되지 않는다는 뜻이기 때문이다.
2. CCI 50 + CCI 14 의 콜 진입 시그널 해석
CCI(14)가 0선을 상향돌파 했을때 그 시그널을 따를려면 CCI(50)과 싱크가 맞아야 한다.
짤방에서 보이는것 처럼 CCI(14)가 먼저 0선을 돌파하고 ADX가 침체구간인 25 에서 빠져 나오더라도
CCI(50)이 100 선을 상향돌파 할 때 까지 기다려 콜 매수를 한다.
3. CCI 50 + CCI 14 의 풋 진입 시그널 해석
짤방에서 처럼 CCI(50)은 100선을 하향돌파하고 CCI(14)가 0선을 하향돌파하며 동시에 시그널을 보내고 있다.
이런 극적인 추세반전이 자주 일어나는것은 아니지만 ADX가 높은 값을 가진 상태에 주로 발생한다.
그리고 조금 더 가다보면 CCI(50)이 -100선까지 하향돌파 해버리는데 추세매매를 하다보면 시그널을 보고도 놓쳐서
추격 매수하기 불안 할 때가 있다. 그럴때 한 번 더 들어오라고 손짓을 하는 신호가 되겠다.
정리하자면,
콜이든 풋이든 ADX는 항상 25 보다 위에 있어야 한다.
그리고 콜과 풋의 진입 신호를 명확히 하자면 아래와 같다.
[콜 진입] (풋 매도)
1) CCI(50)이 100선을 상향 돌파 또는 -100선을 상향 돌파.
2) CCI(14)가 0선을 상향 돌파
[풋 진입] (콜 매도)
1) CCI(50)이 100선을 하향 돌파 또는 -100선을 하향 돌파.
2) CCI(14)가 0선을 하향 돌파.
그외 CCI(14)를 통해 주가와의 다이버전스를 체크하는것도 종종 도움이 된다.
추세추종 매매는 추세장에서만 효과적임을 명심하고
연습(지난 차트 복기)을 많이 해 본 뒤에 시행착오 과정에서 손실이 많이 생기지 않도록
실제 매매에 소액부터 적용하여 차츰 차츰 연습을 해나가자.
마지막으로 이 시스템을 소개하는 이유는 CCI에 50이라는 파라메터를 사용하는 것에서 힌트를 얻으라는 뜻임.
보조지표는 파라메터를 어떻게 바꾸냐에 따라 굉장히 다른 결과를 가져다 줄 수 있음.
아래는 복습용 다른 날의 차트.
CCI1