블로그 이미지
流れ星みたいな人生。 RosaGigantea

카테고리

로사기간티아의 프로.. (407)
프로그램 설계 (7)
알고리즘 (2)
리눅스 서버 (87)
윈도우 서버 (29)
DataBase (21)
모바일 프로그래밍 (3)
콘솔 게임 프로그래밍 (19)
임베이드 장비 (4)
윈도우 프로그래밍 (5)
C/C++언어 (82)
Util 프로그램 (7)
대학생 레벨 (117)
기타 언어 프로그래밍 (8)
리뷰 (16)
Total454,668
Today159
Yesterday167

'로사기간티아의 프로그래밍 관련'에 해당되는 글 407건

  1. 2013/09/20 파이선 간단한 captchr 소스
  2. 2013/09/20 파이선 PIL 간단 예제
  3. 2013/09/18 파이썬의 PIL 이미지 라이브러리 사용 강좌
  4. 2013/09/18 captcha 우회하기
  5. 2013/09/17 XQuery를 사용한 XML 데이터 업데이트
  6. 2013/09/15 Centos 에서 Objective-c 와 gcc 설치 법...
  7. 2013/09/09 윈도우 버젼, 컴파일러 버젼 macro
  8. 2013/09/02 프로시저 내 특정 단어 검색하기
  9. 2013/08/30 Ubuntu에서 Valgrind로 Memory Leak 검사하기 (기본편)
  10. 2013/08/29 [MSSQL - GROUP BY HAVING 을 이용한 중복 데이타 체크]
  11. 2013/08/22 TCMalloc 구글 성능 도구를 이용한 메모리 누수 디버깅
  12. 2013/08/21 Google CPU Profiler
  13. 2013/08/10 [Linux] CPU, I/O, Memory 사용량 측정
  14. 2013/08/01 실행 중인 쿼리의 처리 상태 확인 - sys.dm_exec_requests
  15. 2013/08/01 MSSQL Lock 정보 확인 및 해제
  16. 2013/07/27 VisualStudio 2012 자동주석 Macro
  17. 2013/07/20 [Synology] VideoStation(DS Video) Frame Rate 올리기+AirVideo 부럽지 않은 동영상 스트리밍 환경 구축하기
  18. 2013/07/20 멀티 쓰레드 환경에서 Memory Allocator
  19. 2013/07/07 빠르고 안전한 통신 프로토콜을 만들어 보자 - XOR 가지고는 문제가 해결되지 않는다.
  20. 2013/07/07 게임 해킹을 막는 보안 전략
  21. 2013/06/25 리눅스 이더넷 모델명 확인
  22. 2013/06/24 리눅스 하드웨어 정보
  23. 2013/06/18 OpenQuery를 이용한 Select, Insert, Update, Delete Database
  24. 2013/05/25 인천에서 일본 위성 방송 보기 위한 여러 노력... orz
  25. 2013/05/15 TIME_WAIT를 남기지 않는 세션종료 (Graceful Shutdown)
  26. 2013/05/15 멀티스레드 프로그래밍(Multithread Programming)에 관한 고찰 (2)
  27. 2013/05/15 멀티스레드 프로그래밍(Multithread Programming)에 관한 고찰 (1)
  28. 2013/05/06 솔라리스 장비 관련
  29. 2013/05/06 [Network] Link 상태 확인 후 IP 셋팅
  30. 2013/05/03 하드웨어 호환 목록(HCL) - 윈디하나의 솔라나라

http://www.emoticode.net/python/create-an-image-using-pil-python-imaging-library.html

 

#!/usr/bin/python

import Image
import ImageDraw
import ImageFont
import ImageFilter
import cStringIO
import random

def gen_captcha_img(txt):
	#Create the image
	im = Image.new("RGB", (140,40), "#444")
	#Create an object that can be used to draw in the given image
	draw = ImageDraw.Draw(im)
	#Get image size
	x, y = im.size
	pnt = random.randint
	#Draw points (individual pixels) inside the image area
	for i in range(300):
		draw.point((pnt(0,x),pnt(0,y),pnt(0,x),pnt(0,y)), fill="#666")
	#Set the font and its size
	font = ImageFont.truetype("../font/Cashier.ttf", 30)
	#Draw the text
	draw.text((5,3), txt, font=font, fill="#fff")
	#Apply a filter to the image
	im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
	#Create a string buffer
	f = cStringIO.StringIO()
	#Save the image in the sting buffer (instead of in a file)
	im.save(f, "JPEG")
	#Get the string buffer content
	content = f.getvalue()
	f.close()
	#Base64 encode for data URI scheme
	data_uri = content.encode("base64").replace("\n", "")
	#Show the image according to data URI scheme
	img = '<img src="data:image/jpeg;base64,%s" />' % (data_uri)
	print img

	
print "Content-type: text/html\n"
gen_captcha_img("emoticode")

 

 

이걸 응용하면 좋을듯

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea

-- 선언부 --

 

import Image

 

global ext
ext = ".jpg"

 

imageFile = "test.jpeg"
im1 = Image.open(imageFile)

 

 

-- 이미지 조작 --

 

Resize

def imgResize(im):

    div = 2
    width = im.size[0] / div
    height = im.size[1] / div

    im2 = im.resize((width, height), Image.NEAREST) # use nearest neighbour
    im3 = im.resize((width, height), Image.BILINEAR) # linear interpolation in a 2x2 environment
    im4 = im.resize((width, height), Image.BICUBIC) # cubic spline interpolation in a 4x4 environment
    im5 = im.resize((width, height), Image.ANTIALIAS) # best down-sizing filter
    
    im2.save("NEAREST" + ext)
    im3.save("BILINEAR" + ext)
    im4.save("BICUBIC" + ext)
    im5.save("ANTIALIAS" + ext)

imgResize(im1)


Crop

def imgCrop(im):
    
    box = (50, 50, 200, 300)
    region = im.crop(box)
    region.save("CROPPED" + ext)

imgCrop(im1)


Transpose

def imgTranspose(im):
    
    box = (50, 50, 200, 300)
    region = im.crop(box)
    
    region = region.transpose(Image.ROTATE_180)
    im.paste(region, box)
    
    im.save("TRANSPOSE" + ext)

imgTranspose(im1)


Band merge

    
def bandMerge(im):
    
    r, g, b = im.split()
    im = Image.merge("RGB", (g,g,g))
    
    im.save("MERGE" + ext)

bandMerge(im1)


Blur

import ImageFilter

def filterBlur(im):
    
    im1 = im.filter(ImageFilter.BLUR)
    
    im1.save("BLUR" + ext)

filterBlur(im1)


Find contours

    
def filterContour(im):
    
    im1 = im.filter(ImageFilter.CONTOUR)
    
    im1.save("CONTOUR" + ext)

filterContour(im1)


Find edges

def filterFindEdges(im):
    
    im1 = im.filter(ImageFilter.FIND_EDGES)
    
    im1.save("EDGES" + ext)

filterFindEdges(im1)
    

 

 

-- 이미지 만들어서 글자 쓰기 --

 

image = Image.new("RGBA", (600,150), (255,255,255))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("resources/HelveticaNeueLight.ttf", fontsize)

draw.text((10, 0), txt, (0,0,0), font=font)
img_resized = image.resize((188,45), Image.ANTIALIAS)

 

 

http://en.wikibooks.org/wiki/Python_Imaging_Library/Editing_Pixels

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG Python

PIL 설치...

http://itekblog.com/centos-6-x-install-pil-python-imaging-library-tutorial/

 

 

yum install python-imaging

 

 

이후 강좌

http://kylog.tistory.com/17

윈도우용 파이썬 같은 느낌이긴 한데;;;

 

http://code.google.com/p/gonando-project/wiki/HowToUsePIL

Introduction

PIL 라이브러리를 가지고 이미지 관리 프로그램의 기능들을 어떻게 프로그래밍해야하는지 정리합니다.

참고 문서

기본 기능

크기 변환

resize 메소드 이용

  • im.resize(size) => image
  • im.resize(size, filter) => image
  • 크기를 바꾼 이미지 객체를 리턴한다.
  • size에는 픽셀단위로 터플에 담아서 넣는다. (width, height)
  • filter에는 NEAREAST, BILINEAR, BICUBIC, ANTIALTIAS 중 하나를 지정한다.
    • NEAREAST: 조사중
    • BILINEAR:
    • BICUBIC:
    • ANTIALTIAS:
  • 필터인자 값이 생략되거나 이미지가 모드 1이나 모드 P라면 NEAREST로 지정된다.

 

예제코드 )

>>> im.resize((200,300))

<PIL.Image.Image image mode=RGB size=200x300 at 0x27DFCB0>

>>> im.resize((200,300)).show()

포맷 변환

save 메소드 이용

  • im.save(outfile, options...)
  • im.save(outfile, format, options...)
  • outfile에 넣은 문자열을 파일 이름으로 하여 저장한다.
  • format을 생략한 경우 format은 파일이름의 확장자로 설정된다.
  • 이 메소드는 None을 리턴
  • option은 추가적인 지시를 제공한다.
  • option을 인식하지 못할 경우 조용히 무시된다
  • 가능한 옵션들은 필요하다면 추가 조사
  • 파일 이름 대신 객체를 넣을 수 있다는데 이에 대한 내용도 추가조사
  • 저장에 실패할 경우 그 이유를 예외로 띄워준다 .
  • 주로 IOError 예외가 발생하는데 이 경우 파일을 만들고 데이터를 이미 넣었을 지도 모른다. 이 불완전한 파일을 제거하는것이 필요하다면 그것은 당신의 몫!

 

예제코드 )

>>> print im.save("tail.bmp","bmp")

None

>>> im.save("tail.bmp")

하나의 픽셀의 값 읽기/설정

픽셀 값 읽기

getpixel 메소드 이용

  • im.getpixel(xy) => value or tuple
  • 매개변수로 두 좌표를 담은 터플(x,y)를 넣어주어야 한다.
  • 다중레이어 이미지라면 각 레이어의 해당 픽셀을 터플에 담아 반환한다.
  • 보다 빠르게 수행하려면 픽셀접근object(load)를 이용하거나 getdata메소드를 이용하는것이 좋다.

 

예제코드 )

>>> from PIL import Image

>>> im = Image.open("c:\\py_t\\2.jpg")

>>> im.getpixel((1,2))

(33, 180, 187)

픽셀 값 설정

putpixel 메소드 이용

  • im.putpixel(xy, colour)
  • 단일 밴드인 경우 colour에 숫자를, 멀티밴드인 경우 coulour에 터플로 숫자를 담아서 주어야 한다.
    • 밴드가 뭘까? => 레이어랑 비슷한 개념인것같은데, 그 차이점을 추가조사해보겠습니다.

예제코드 )

 

>>> im.putpixel((1,2),33)

>>> im.getpixel((1,2))

(33, 0, 0)

미러링

  • FLIP_LEFT_RIGHT를 인자값으로 넣으면 수직선에 대해 대칭시킨 이미지를 리턴한다.
  • FLIP_TOP_BOTTOM은 소평선에 대해 대칭시킨 이미지를 리턴한다.

예제코드 )

>>> im.transpose(Image.FLIP_LEFT_RIGHT).show()

>>> im.transpose(Image.FLIP_TOP_BOTT)M).show()

일부 영역 선택/복사

crop 메소드 사용.(직사각형 선택 및 복사)

  • im.crop(box)
  • box 인자로 네개의 원소를 가진 터플이 들어간다.
  • ( left, upper, right, lower)
  • 직사각형의 왼쪽, 위쪽, 오른쪽, 아래쪽의 픽셀좌표를 의미
  • 잘라낸 영역의 복사본을 리턴한다. (원본에 손상없음)

예제코드 )

>>> im.crop((10,0,100,100)).show()

alpha 값 변환(투명도)

이미지를 투명하게 만들 수 있다.

putalpha

  • im.putalpha(band)

 

회전

rotate 메소드 이용

  • im.rotate(angle) => image
  • im.rotate(angle, filter=NEAREST, expand=0) => image
  • 주어진 각도만큼 시계방향으로 회전한 이미지의 복사본을 리턴한다
  • 필터 인자는 NEAREST, BILINEAR, BICUBIC 중 하나가 될 수 있다.
  • 생략 된 경우나 이미지의 모드가 1이나 P인 경우 NEAREST로 지정한다.
  • 확장인자 expand가 true로 들어오면 리턴하는 이미지가 회전이미지를 포함할 만큼 크게 조절해준다. 그러나 생략된 경우나 false인 경우 리턴된 이미지는 입력이미지와 같은 크기를 유지한다.
  • 오버로딩 관계에 있는 인자하나인 메소드는 이미지크기를 조절해서 회전이미지를 포함한다.

 

예제코드 )

>>> im.rotate(90).show()

"""이 경우 회전이미지를 포함한다. """

>>> im.rotate(45,0).show()

"""이 경우는 회전이미지를 포함하지 못하고 부분적으로 약간 짤리게 된다 """

!!NEAREST 등의 상수가 정의되지 않은 같다. 어떤 모듈을 임포트해야할지 모르겠다..;

이미지에 텍스트 쓰기

ImageDraw 모듈의 text 메소드 이용

  • import ImageDraw
  • draw = ImageDraw.Draw(im)
  • 여기서 im은 위에서 사용하였던 Image.open의 결과
  • draw.text(position, string, options)
  • 주어진 좌표에 문자열을 삽입한다. 좌표는 텍스트의 왼쪽 위를 가리킨다.
  • 폰트옵션으로 사용하고자 하는 폰트를 설정한다.
  • 복사본에 효과를 적용하여 리턴하는 것이 아니라 바로 대상 객체에 적용한다.

 

예제코드 )

>>> import ImageDraw

>>> draw = ImageDraw.Draw(im)

>>> draw.text((44,55),"YES WE CAN DO IT! ")

>>> im.show()

폰트의 종류, 크기와 색상을 지정하고 싶은 경우

예제코드 )

import ImageFont, ImageDraw

draw
= ImageDraw.Draw(image)

use a truetype font
font
= ImageFont.truetype("arial.ttf", 15)

draw
.text((10, 25), "world", (0,0,0),font)
  • PIL에서는 비트맵폰트와 트루타입폰트를 다룰 수 있는데 비트맵폰트는 크기를
  • 지정할수 없으므로 적지않음.
  • 트루타입 폰트 : 글자의 외곽선만을 저장한 폰트타입.
  • font = ImageFont.truetype("arial.ttf", 15)
  • truetype메소드를 통해 폰트의 종류와 폰트크기를 지정하여 폰트객체를 생성.
  • draw.text((10, 25), "world", (0,0,0),font)
  • 세번째 인자로 rgb색상값을 넣고 네번째 인자로 위에서 지정한 폰트를 넣는다.

썸네일생성

thumbnail 메소드 이용

  • im.thumbnail(size)
  • im.thumbnail(size, filter)
  • 내부적으로 draft메소드가 이용되는것 같다.
  • 필터 인자로는 NEAREST, BILINEAR, BICUBIC, ANTIALIAS 중 하나가 가능하다.
  • 생략된 경우 NEAREST로 설정된다
  • 필터 인자는 중 하나가 될수있다
  • 이미지 object를 바로 수정하기때문에 원본자료를 또 이용할 일이 있는 경우 원본의 복사본을 이용하는 것이 좋다.
  • None을 리턴한다.

 

예제코드 )

>>> print im.thumbnail((200,300))

None

>>> im.show()

또 무슨 기능??

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG Python

간단한 captcha 는 기계적으로 풀어버리는 소스.

http://kthan.tistory.com/176

 

음....

 

파이선 말고 php 로는 구글에 이런것도 있음..

http://code.google.com/p/cool-php-captcha/

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG CAPTCHA

 

XQuery를 사용한 XML 데이터 업데이트

 

-      Version :  SQL Server 2005,2008, 2008R2, 2012

 

XML의 값을 수정하는 방법에는 무엇이 있을까? XML 편집기 등을 이용하여 사용자가 직접 수정 할 수도 있지만 XML노드와값을 정확하게 수정하려면 XQuery를 사용하여 수정 할 수 있다.

XQuery는 구조화 되었거나 반구조화된 XML 데이터를 쿼리할 수 있는 언어이다. XQuery는 기존의XPath 쿼리 언어를 기반으로 더 나은 반복 성능 및 정렬 결과를 위한 지원이 추가 되었다.

자세한 내용은 MSDN을 참고한다.

 

[노드삽입]

삽입 키워드는 다음과 같은 구조를 사용한다.

Insert Expression1 ({as first | as last}into | after | before Expression2)

삽입 키워드를 통하여 샘플 테이블을 생성 한다.

CREATE TABLE HR_XML (ID INT IDENTITY, SALARIES XML)

GO

INSERT HR_XML VALUES(

    '<SALARIES>

      <MARKETING>

        <EMPLOYEE ID="1" TIER="4">

          <SALARY>42000</SALARY>

        </EMPLOYEE>

        <EMPLOYEE ID="2" TIER="1">

          <SALARY>52000</SALARY>

        </EMPLOYEE>

        <EMPLOYEE ID="3" TIER="4">

          <SALARY>48000</SALARY>

        </EMPLOYEE>

      </MARKETING>

    </SALARIES>

    '

)

GO

 

SELECT * FROM HR_XML

GO

 

     

 

 

<Account / >라는 새로운 노드를 생성하려면 INSERT 구문을 이용하여 수행 할 수 있다.

UPDATE HR_XML

SET Salaries.modify('insert <Accounting /> into (/Salaries)[1]')

GO

 

SELECT * FROM HR_XML

GO

 

 

 

위의 쿼리에서 보면 Salaries [1] 노드에 대해 Singleton Designation 값이 지정된 것을 확인 할 수 있다.Singleton Designation 없이 쿼리를 실행하면 오류가 발생 한다.

UPDATE HR_XML

SET Salaries.modify('insert <Accounting /> into (/Salaries)')

GO

 

 

 

[원하는위치 노드 추가]

노드를 추가하면 기존의 노드 이후에 삽입되는 것을 확인 할 수 있다. (위그림 참조). 원하는 위치에 노드를 삽입하는 방법을 알아 보자실습에서는 <Salaries> 아래 첫번째 노드에 삽입을 하여 보자. Firstinto 명령어를 이용한다.

UPDATE HR_XML

SET Salaries.modify('insert <Accounting /> as first into (/Salaries)[1]')

GO

 

select * from HR_XML

GO

 

 

 

첫번째 노드 <Account /> Employee 노드(ID, tier)를 입력 해보자노드를 입력 할 때 속성을 포함해야 한다. SingletonDesignation [2] 지정으로 <Account>노드 바로 아래 <Employee>가 삽입 되는 것을 확인 할 수 있다.

UPDATE HR_XML

SET Salaries.modify('insert <Employee ID="4" tier="4" /> into (/Salaries/Accounting)[1]')

GO

 

select * from HR_XML

GO

 

 

 

[특정노드를 식별하여 삽입]

특정 Employee의 노드에 새 노드를 삽입하여 보자. @ID 인수를 사용하여 Employee를 식별 할 수 있다실습에서는 Employee ID = 2인 노드를 찾아서 새로운 노드를삽입하였다.

UPDATE HR_XML

SET Salaries.modify('insert <Projects /> into (/Salaries/Marketing/Employee[@ID=("2")])[1]')

GO

 

select * from HR_XML

GO

 

 

 

[노드삽입과 값 삽입]

노드 삽입과 컬력션 삽입을 할 수 있다. Employee id =2의노드에 새로운 노드(Project ID = 1)과 값을 삽입한다.

UPDATE HR_XML

SET Salaries.modify('insert <Project ID="1"><Description>Organize new

    strategies</Description></Project> into

    (/Salaries/Marketing/Employee[@ID=("2")]/Projects)[1]')

GO

 

select * from HR_XML

GO

 

 

 

[다른노드 값을 참조하여 노드 삽입]

Employee id = 1의 값을 참조하여 Employee ID = 4 <Salary>노드를 추가하여보자중괄호를 사용하여 명시적으로 노드를 지정한 것을 확인 할 수 있다.

UPDATE HR_XML

SET Salaries.modify('insert

<Salary>{(/Salaries/Marketing/Employee[@ID=("1")]/Salary/text())}</Salary>

into (/Salaries/Accounting/Employee[@ID=("4")])[1]')

GO

 

select * from HR_XML

GO

 

 

 

[노드삭제]

노드 삭제는 노드 삽입보다 간단하다표현식은

Delete Expression 이다노드 마지막의 <Account />노드를 삭제 하여 보자.

UPDATE HR_XML

SET Salaries.modify('delete (/Salaries/Accounting)[2]')

GO

 

select * from HR_XML

GO

 

 

 

[값삭제]

값 삭제는 텍스트() 함수를 사용하여 삭제 할 수 있다.

UPDATE HR_XML

SET Salaries.modify('delete

(/Salaries/Marketing/Employee[@ID=("2")]/Projects/Project[@ID="1"]/Description/text())[1]')

GO

 

select * from HR_XML

GO

 

 

 

[값수정]

Exployee ID = 2<Salary> 값을 수정하여 보자.

UPDATE HR_XML

SET Salaries.modify('replace value of

(/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] with ("60000")')

GO

 

select * from HR_XML

GO

 

 

 

[XQuery의산술값을 이용한 수정]

모든 Exployee에 대해서<Salary>의 값이 10% 증가한 값을 수정할 수 있도록 반복문을 사용 할수 있다.

DECLARE @i INT = 1

WHILE @i <= 3

   BEGIN

 UPDATE HR_XML

 SET Salaries.modify('replace value of

 (/Salaries/Marketing/Employee[@ID=(sql:variable("@i"))]/Salary/text())[1]

 with (/Salaries/Marketing/Employee[@ID=(sql:variable("@i"))]/Salary)[1] * 1.01')

 SET @i+=1

   END

GO

 

select * from HR_XML

GO

 

 

 

[수정제한]

XQuery에서 Modify() 메소드는수정하려는 값을 SELECT와 함께 사용 할 수 없다.

SELECT Salaries.modify('replace value of

(/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1]

with ("60000")')

FROM HR_XML

GO

 

 

 

 

기존의 값을 조회하여 수정하려면 다음과 같이 변수를 이용하여 사용하여야 한다.

DECLARE @x XML

SELECT @x = Salaries FROM HR_XML

SET @x.modify('replace value of (/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] with ("60000")')

SELECT @x

GO

 

 

 

쿼리문에서 UPDATE 구문을 동시에 2개 사용할 수 없다별도의 업데이트문을 사용하여야 한다.

UPDATE HR_XML

SET Salaries.modify('replace value of

(/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] with ("60000")'),

Salaries.modify('replace value of

(/Salaries/Marketing/Employee[@ID=("1")]/Salary/text())[1] with ("60000")')

GO

 

 

 

Employee ID = 2의 값을 참조하여 Employee ID = 1의 값을 업데이트 할 수 있다.

UPDATE HR_XML

SET Salaries.modify('replace value of

(/Salaries/Marketing/Employee[@ID=("1")]/Salary/text())[1]

with (/Salaries/Marketing/Employee[@ID=("2")]/Salary)')

GO

 

select * from HR_XML

GO

 

 

 

[계층값 변경]

@ID 값을 사용하여Employee ID = 1 tier = 4 값을tier =1로 변경 할 수 있다.

UPDATE HR_XML

SET Salaries.modify('replace value of

(/Salaries/Marketing/Employee[@ID=("1")]/@tier)[1] with "1"')

GO

 

select * from HR_XML

GO

 

 

 

 

XQuery를 사용하여 XMLDML 실습을 하였다많은 XML 편지기가있지만 SSMS를 이용하여 속성값을 사용하여 정확하게 수정할 때에 매우 유용하게 사용 할 수 있을 듯하다.

 

 

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG XQuery

 Centos 에서 Objective-c 와 gcc 설치 법...

 

 

 

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea

* WINVER
Using the Windows Headers
http://msdn.microsoft.com/en-us/library/aa383745(VS.85).aspx
_WIN32_WINNT ,WINVER
Windows 8    = 0x0602 (_WIN32_WINNT_WIN8 )
Windows 7    = 0x0601 (_WIN32_WINNT_WIN7 )
Windows Server 2008 = 0x0600
Windows Vista        = 0x0600
Windows Server 2003 with SP1, Windows XP with SP2    = 0x0502
Windows Server 2003, Windows XP    = 0x0501
Windows 2000    = 0x0500



* _MSC_VER
Visual Studio Version ( msc_ver )
https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#Version_history
VC 4.0 (1995) = 1000
VC 5.0 (1997) = 1100
VC 6.0 (1998) = 1200  <----
VC 7.0 (2002) = 1300
VC 7.1 (2003) = 1310
VC 8.0 (2005) = 1400
VC 9.0 (2008) = 1500
VC 10.0 (2010) = 1600  - msvcr100.dll
VC 11.0 (2012) = 1700

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea

출처 : http://polyphagia.egloos.com/10231978


아래 내용을 쿼리분석기에서 실행하면 찾으려는 단어가 포함된 프로시저 명을 알려준다.

단위는 데이터베이스 단위로 검색이 이루어져 특정 문자가 들어간 프로시저를 찾을때 용이하다.

 

-- 프로시저 검색
SELECT O.name
FROM   sysobjects O JOIN syscomments C
ON     O.id = C.id
WHERE  O.type = 'P' AND C.text LIKE '%찾으려는 단어%'


저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG MSSQL

Windows에서 개발을 한 경험이 있는 분들이라면, AQTime(예전 이름은 Memproof)이라는 상용 Tool을 잘 아실 것입니다.

AQTime은 Runtime 검사를 통해서 Application의 Performance를 진단하고 Memory Leak(Heap Memory를 할당했을 경우 사용 후에 System에 반환하지 않아서 발생하는 누수현상)을 검사할 수 있는 유명한 Profiling Tool입니다.

이러한 Profiling Tool을 사용하면 개발자가 미처 파악하지 못한 Application의 문제를 해결하고 Performance를 향상시킬 수 있기 때문에 Project의 안정화 시에 주로 사용됩니다.

이번 Post에서는 이와 비슷한 기능을 Linux에서 수행하는 Command-line Tool인 Valgrind를 통해서 Memory Leak을 검사하는 방법을 알아보도록 하겠습니다.

Table of Contents [Show]

1. Valgrind?

Valgrind는 앞서 말한 대로 Linux Program의 Profiling을 위한 Application입니다. (지원하는 Platform과 Architecture를 확인하려면 여기를 방문하면 됩니다.) 그리고 License가 GPL이기 때문에 AQTime과 달리 사용하는데 전혀 비용이 들지 않습니다.

또한 수많은 Open-source Project가 Valgrind를 통해서 Debugging과 Profiling을 수행하고 있다는 것은 이 Program의 정확성과 안정성을 증명합니다.

Valgrind는 엄밀하게 말하면 Profiling Suite로 여러가지 Profiling을 위한 도구를 포함하고 있으며 각각의 도구는 valgrind 명령을 통해 통합되어 수행됩니다. 그 중 일부를 소개하면 다음과 같습니다.

  • Memcheck: Memory 관리의 문제를 진단하는 Tool입니다. 할당받지 않은 Heap Memory에 접근하는 것이나 Memory Leak과 같은 문제를 이 Tool을 통하여 발견할 수 있습니다.
  • Callgrind: Runtime 시에 각 함수의 호출 history와 실행 Function간의 Call Graph를 만들기 위한 정보를 기록하는데 사용하는 도구입니다. 저장된 내용은 Command-line Tool이나 KCachegrind를 통해서 분석할 수 있습니다. KCachegrind에 대해서는 이후에 간단한 사용법을 설명하겠습니다.
  • Massif: Heap Memory에 대한 사용 정보를 확인할 수 있습니다. 간단한 예제와 설명은 여기를 참고하기 바랍니다.
  • Helgrind: POSIX Thread(간단히 PThread)를 Program에서 사용할 때 각 Thread간의 동기화와 관련된 문제를 진단하기 위한 Tool입니다.

Valgrind는 좋은 Tool이지만 사용할 때 유의할 점이 있습니다. Valgrind는 Code 상의 오류를 검사할 수 있지만, 문맥적인 오류는 잡아내지 못합니다. 다시 말하면, 기술적으로 해당 Program이 문제가 없다는 것을 증명할 수는 있지만, 실제 그 Program이 사용자가 원하는 대로 제대로 동작한다는 것을 증명할 수 없다는 것입니다. 생각 외로 이런 Profiling Tool을 과신하는 경우가 종종 있는데 그런 점은 조심해야 합니다.

좀 더 자세한 Valgrind의 정보를 얻기 원한다면 다음 Link를 방문하기 바랍니다.

2. 설치

Valgrind를 설치하는 것은 간단합니다. Terminal을 새로 열어서 다음과 같이 입력하여 설치합니다.

1
sudo apt-get install valgrind

다음 Section에서는 Test Program을 Build하면서 설명을 진행합니다. System에 Build를 위한 Package가 설치되어있지 않다면 다음과 같이 입력하여 설치합니다.

1
sudo apt-get install build-essential

3. Memory Leak 검사하기

Valgrind가 무엇인지에 대해서 알아봤고 설치도 마쳤으니, 이제 Test Program을 만들어서 Valgrind의 Memcheck 도구를 사용하여 Memory Leak을 검사하는 방법을 알아봅시다.

Gedit나 Vim과 같은 편집기를 사용해서 다음과 같은 내용을 입력한 후 test.c라고 저장합시다.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>

void test(void)
{
    char *= malloc(sizeof(char) * 10);
}

int main(void)
{
    test();
    return 0;
}

위의 C Code를 보면 test()에서 malloc()을 통해서 Heap Memory를 할당받았지만 사용 후에 free()를 사용하여 System에 반환하지 않았기 때문에 Memory Leak이 발생하는 것을 알 수 있습니다.

이제 이 Source를 Compile해 봅시다. Terminal에서 다음과 같이 입력하여 Debugging이 가능하도록 Option을 적용하여 Compile합니다.

1
gcc -g -o test test.c

test.c를 Compile하여 생성된 test라는 실행 File에 대해서 이제 Valgrind를 사용하여 Memory Leak을 검사해 봅시다. Terminal에서 다음과 같이 입력하여 Valgrind를 실행합니다.

1
valgrind --leak-check=yes ./test

만약 Memory Leak을 검사할 Program이 Argument(명령행 인자)를 가지고 있다면, 위의 명령에서 검사할 Program 뒤에 그대로 이어서 적으면 됩니다.

위의 명령을 실행하면 먼저 test Program이 실행되고 종료된 후에 Valgrind가 검사한 정보가 Terminal에 출력됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
==8067== Memcheck, a memory error detector
==8067== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==8067== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==8067== Command: ./test
==8067== 
==8067== 
==8067== HEAP SUMMARY:
==8067==     in use at exit: 10 bytes in 1 blocks
==8067==   total heap usage: 1 allocs, 0 frees, 10 bytes allocated
==8067== 
==8067== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8067==    at 0x4C28FAC: malloc (vg_replace_malloc.c:236)
==8067==    by 0x400505: test (test.c:5)
==8067==    by 0x400514: main (test.c:10)
==8067== 
==8067== LEAK SUMMARY:
==8067==    definitely lost: 10 bytes in 1 blocks
==8067==    indirectly lost: 0 bytes in 0 blocks
==8067==      possibly lost: 0 bytes in 0 blocks
==8067==    still reachable: 0 bytes in 0 blocks
==8067==         suppressed: 0 bytes in 0 blocks
==8067== 
==8067== For counts of detected and suppressed errors, rerun with: -v
==8067== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

위의 내용은 Valgrind를 실행하여 얻은 출력화면입니다. 중요한 부분은 11~14번째 Line에 있는 Memory Leak정보로, 이것은 test.c의 5번째 줄에 위치한 test()의 Code에서 Memory Leak이 발생했다는 것을 나타냅니다.

이와 같이 Memory Leak 정보에 표시된 Call Stack을 통해서 Source Code의 어느 함수의 몇번째 Line에서 Memory Leak이 발생했는지를 확인할 수 있습니다.

유의해야할 점은, Valgrind는 검사할 Program을 실행한 상태(Runtime)에서 검사하기 때문에 실행 시에 수행되지 않은 Code에 대해서는 검사할 수 없다는 것입니다. 그래서 Test Case를 잘 수행하지 않으면 Memory Leak을 검사하지 못할 수 있기 때문에 실행하기 전에 Test Case를 미리 준비하는 것이 좋습니다.

4. 마치면서…

지금까지 Valgrind를 사용하여 Memory Leak을 검사하는 방법을 간단하게 알아봤습니다. 다음 Post에서는 Valgrind의 Front-end를 사용하여 좀 더 쉽게 Memory Leak을 검사하는 방법과 Eclipse CDT로 Program을 개발했을 때 Eclipse 상에서 Valgrind를 사용하는 방법에 대해서 알아보도록 하겠습니다.


출처 : http://ioriy2k.pe.kr/archives/3754

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG valgrind

출처 : http://www.webmadang.net/database/database.do?action=read&boardid=4001&page=null&seq=5

[MSSQL - GROUP BY HAVING 을 이용한 중복 데이타 체크]



1 - 단일 테이블에서 GROUP BY HAVING 을 이용한 중복 데이타 체크

아래와 같은 로그인 정보를 기록하는 9개의 레코드를 가진 테이블이 있다고 가정하겠습니다. 테이블의 이름은
"loginLog_tbl" 이고 로그인 로그(log)를 기록해야 하니 당연히 중복된 데이타가 들어가 있을것입니다. 

위 테이블의 레코드를 보면 mfcchang이라는 uid가 3회나 로그인 했다는 것을 알 수 있습니다. 레코드의 갯수가 작아서
눈으로도 파악이 되지만 레코드의 갯수가 많을때는 다음과 같은 쿼리문으로 간편하게 파악 할 수도 있습니다.

SELECT uid, count(*) AS loginCount FROM loginLog_tbl GROUP BY uid ORDER BY loginCount DESC

위와 같은 쿼리를 실행해보면 GROUP BY 에 의해서 uid 컬럼의 count가 집계되고 결과는 아래와 같습니다.

 

여기에 Having 절의 제약조건을 이용해서 loginCount가 1 보다 큰것만 출력해 보겠습니다. loginCount 는 count(*)의 엘리어스 이니까, Having 절 이하는 아래와 같을 것입니다.

SELECT uid, count(*) AS loginCount FROM loginLog_tbl GROUP BY uid HAVING count(*) > 1

출력해 보면 결과는 아래와 같습니다. 아래의 결과에서 두번 즉 2회 이상 로그인한 사람은 kimchi 와 mfcchang 이라는 uid의 소유자 뿐임을 알수 있습니다.

 

어떠한 경우에서든 데이타의 중복을 확인하고자 한하면 바로 위의 쿼리문을 이용하시면 됩니다. 중복을 체크하고자 하는 
기준이 되는 컬럼만 바꾸면 될 것입니다.



2 - 두개 이상의 테이블에서 GROUP BY HAVING 을 이용한 중복 데이타 체크 

그렇다면 중복을 체크하고자 하는 컬럼이 위와 같이 테이블이 하나가 아니고 둘또는 셋이상에 걸쳐서 존재한다면 어떻게
처리하면 될까요? 테이블간에 합집합의 결과를 모두 보여주는 UNION ALL 로 테이블을 결합하시면 됩니다.

아래와 같이 로그인 정보를 기록하는 테이블이 하나 더 있고, 이 테이블은 예전에 퇴사한 선임자가 만들어 놓은 것이라고 
가정하겠습니다. 테이블의 이름은 loginLogTABLE 입니다.

 

처음에 사용했던 loginLog_tbl과 바로 위의 logLogTABLE에서 uid 컬럼의 데이타값이 2 개 이상인 것을 찾아내는
쿼리는 아래와 같습니다.

SELECT uid, count(*) AS loginCount FROM 
SELECT uid FROM loginLog_tbl UNION ALL SELECT uid FROM loginLogTABLE ) AS T
GROUP BY uid HAVING count(*) > 1

하나의 테이블을 사용할때와 틀린부분은 UNION ALL 로 해당 테이블을 묶어서 서브쿼리로 처리한다는 것뿐입니다.
결과는 아래와 같습니다. 두개의 테이블의 레코드 갯수가 몇개 없으니 눈으로도 확인 가능할 것입니다.

작성자: 다자래(mfcchang@naver.com) 
웹제작 토탈 커뮤니티 http://www.webmadang.net


저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG MS-SQL

출처 : http://sunjinyang.wordpress.com/2009/06/18/debugging-memory-leaks-with-tcmalloc-google-perftools/


리눅스에서 메모리 침범이나 메모리 누수, 혹은 복잡한 메모리 접근 관련 오류를 디버깅할때는 대부분 Valgrind 도구를 이용합니다. 하지만 Valgrind는 많은 메모리를 사용하고 실행 속도가 현저하게 느려지기 때문에, 별도의 타겟 장비에서 제한된 조건으로 동작하는 어플리케이션에는 조금 무리가 있습니다. 게다가 Valgrind의 메모리 검사 도구는 프로그램이 종료된 시점에서 누수된 메모리만 찾기 때문에, 실행 중에는 엄청나게 메모리를 사용하다가 정상적인 객체 해제 루틴이 호출되면 모든 메모리가 정리되어 찾을 수 없는 경우는 발견하지 못합니다.

구글 성능 도구를 다시 검토하고 사용하게 된 이유도 바로 여기에 있습니다. 현재 디버깅 중인 프로그램의 문제가 대략 이렇습니다. 몇날 며칠이고 문제없이 가동해야 하는 프로그램이 특정 설정을 적용한 후에는, 자고 일어나면 10~20메가씩 메모리 점유율이 한꺼번에 올라갑니다. 하지만 Valgrind 도구를 사용해도 어느 부분이 문제인지 찾을 수가 없습니다. 지난 글에서 TCMalloc 메모리 할당자로 교체한 후 문제가 해결된 줄 알았던 바로 그 패턴이기도 합니다. 그래서 이번에는 프로파일 기능을 이용해 직접 메모리 누수를 디버깅 해보고, 나중을 위해 그 과정을 정리해 보았습니다.

디버깅 환경

디버깅 환경은 우분투 9.04 x86_32 플랫폼입니다. x86_64 플랫폼에서는 이상하게 호출 그래프가 그려지지 않아서 일단 무시했습니다. 사용한 버전은 공식 홈페이지에서 다운로드 받은 1.2 버전입니다. 1.3 버전은 이상하게 프로파일 기능이 동작하지 않아 역시 무시했습니다.

TCMalloc 도구 설치 및 연결

호출 그래프를 생성하기 위해 dot 프로그램이 필요한데 이 프로그램은 graphviz 패키지에 들어있으므로 설치해야 합니다.

$ sudo apt-get install graphviz

우선, 공식 홈페이지에서 google-perftools 압축 파일을 다운로드 한 뒤 다음과 같이 빌드하고 설치합니다. [2011.05.02 추가]INSTALL 문서에도 명시되어 있듯이, x86_64 환경에서는 libunwind 라이브러리를 미리 설치한 뒤 빌드해야 정상적으로 동작합니다.

$ cd google-perftools*
$ ./configure --prefix=/usr
$ make
$ sudo make install

TCMalloc 라이브러리를 연결하는 방법은 공식 문서에도 나와 있듯이 디버깅할 프로그램 링크 마지막에 ‘-ltcmalloc‘ 옵션을 추가하거나, 실행할때 프로그램 앞에 ‘LD_PRELOAD=/usr/lib/libtcmalloc.so execute-file‘ 처럼 라이브러리를 먼저 로드해주면 됩니다. 저는 첫번째 방법을 사용했습니다. [2011.05.02 갱신] 언제부터인지는 확실치 않지만 첫번째 방법은 동작하지 않고 두번째 방법으로 해야 메모리 프로파일이 정상적으로 동작합니다.

참고로, 디버깅할 프로그램을 컴파일할때는 디버깅 심볼 옵션(-g)이 있어야 호출 그래프에서 정확한 함수 이름이 표시됩니다. 또한 최적화 옵션(-O2 등)을 사용 안하면 더 정확한 함수 호출 그래프를 얻을 수 있습니다.

프로파일 데이터 얻기

TCMalloc 라이브러리를 연결해도 기본적으로 프로파일 기능은 동작하지 않습니다. HEAPPROFILE 환경변수에 프로파일 정보를 주기적으로 덤프할 파일 이름 접두사(prefix)를 지정해야만 동작합니다.

$ HEAPPROFILE=/tmp/profile execute-file

접두사는 파일 절대 경로로 디렉토리를 포함할 수 있습니다.

만일 덤프 파일이 너무 자주 생성되거나 반대로 너무 드물게 생성된다면 환경 변수를 통해 간격을 조절할 수 있습니다.

$ HEAPPROFILE=/tmp/profile 
  HEAP_PROFILE_ALLOCATION_INTERVAL=107374182400 
  execute-file

더 자세한 옵션은 공식 문서를 확인해 보시기 바랍니다.

위 예제에서 지정한 방식대로 프로그램을 실행하면 /tmp/profile.0001.heap/tmp/profile.0002.heap/tmp/profile.0003.heap 등과 같은 프로파일 덤프 파일이 실행 도중 계속 생성됩니다.

결과 그래프 얻기

포스트스크립트(PostScript) 파일 형식으로 메모리 프로파일 정보를 포함한 함수 호출 그래프를 얻으려면 다음과 같이 형식으로 pprof 프로그램을 실행하면 됩니다.

$ pprof --ps --lines 
    execute-file 
    /tmp/profile.0001.heap 
    > profile-0001.ps

프로파일 덤프 파일에 대하여 하나씩 그래프를 생성해 직접 눈과 손으로 비교하는 것도 나쁘지 않지만, 두 프로파일의 차이점만 그래프로 만들어주는 옵션이 있으므로 이를 사용하면 더 편리합니다. 즉, 시간대별 메모리 사용량의 달라진 부분이 정확하게 어느 함수 호출 때문인지 알 수 있게 해줍니다.

$ pprof --ps --lines 
    --base /tmp/profile.0001.heap 
    execute-file 
    /tmp/profile.0002.heap 
    > profile-0002-diff.ps

물론, PDF(--pdf), GIF(--gif) 등과 같은 다른 형식으로 그래프 파일을 얻을 수도 있습니다. 더 자세한 옵션은 ‘pprof --help‘를 참고하시면 됩니다.

결론

며칠간 디버깅에 적용해보니 문제가 발생한 패턴이 무엇이었는지도 찾아내고, 간과했던 작은 메모리 누수 버그들도 함께 발견할 수 있었습니다. 하지만, 언제나 그렇듯이, 디버깅 사태까지 오기 전에 더 튼튼하게 설계하고, 더 꼼꼼하게 프로그래밍하고, 더 철저하게 코드 리뷰와 테스트를 거치는 게 정도임을 새삼 깨닫습니다.

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
TAG tcmalloc

출처 : http://blog.daum.net/jchern/13756799


download : http://code.google.com/p/google-perftools/downloads/list

toturial : http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html

 

설치방법

tar -zxvf google-perftools-1.6.tar.gz

cd google-perftools-1.6

./configure

make

su

make install

 

 

사전 작업

1. 프로파일링을 워하는 프로그램 컴파일시에 -lprofiler를 같이 컴파일한다.

2. <google/profiler.h>를 include한다

 

bash >

export LD_LIBRARY_PATH=/usr/local/lib

export CPUPROFILE=output.txt( CPUPROFILE로 설정된 파일명으로 출력파일이 생성된다 )

 

특정 함수에 대해서만 프로파일링을 하기 위해서는

ProfilerStart()와 ProfilerStop()를 사용하면 된다.

ProfilerStart()에는 함수명을 파라미터로 준다.( ProfileStart("test_func"); )

여기까지 설정한후에 실행을 하면

CPUPROFILE에 설정한 파일명으로 파일이 하나 생성이 된다.

이파일은 바이너리 포맷이라서 그냥 볼수는 없고

 

/usr/local/bin/pprof 라는 파일로 볼수 있다.

/usr/local/bin/pprof [실행파일명] [output.txt]

(pprof)라고 프로프트가 뜨면 top를 입력

 

(pprof) top

 

상위에 노출되는 함수들이 실행시간이 오래걸리는 함수들이다.

 

 

 

특정 함수에 대한 프로파일링 :

pprof    --gv --focus=함수명   실행파일 출력파일

 

특정 함수를 제외한 프로파일링

pprof   --gv --ignore=함수명  실행파일 출력파일


 

좀더 자세한 내용은 요기서 ; http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html

 

저작자 표시 비영리 변경 금지
Posted by 考えの進化 RosaGigantea
  1. iostat
  2. 별도의 설치 필요없음, 물리적 드라이버 별로 기본적인 Disk Read/Write 볼 수 있음
  3. 한 개 이상의 디스크 드라이브에 대한 입출력 통계와 CPU 활용량

    arg-cpu
    %user
    %nice
    %system
    %iowait
    %steal
    %idle
    마지막 재부팅 이후의 평균 CPU 활용량 어플리케이션 등 사용자 모드에 소모된 시간 nice를 사용하여 스케줄링 우선순위가 바뀐 프로세스에 소모된 시간 시스템(커널)이 사용한 시간 디스크I/O 요청 때문에 CPU가 대기한 시간 다른 가상 CPU가 서비스하는 동안 비자발적으로 대기한 시간 대기한 시간
    Device
    tps
    kB_read/s
    kB_wrtn/s
    kB_read
    kB_wrtn
    디바이스 구분 초당 전송(입출력) 수 초당 읽혀진 KB (Blk일 경우 512바이트 블록수) 초당 쓰여진 KB (Blk일 경우 512바이트 블록수) 지금까지 읽혀진 KB(Blk일 경우 512바이트 블록수) 지금까지 쓰여진 KB(Blk일 경우 512바이트 블록수)
  4. vmstat
  5. 별도의 설치 필요없음, 시스템의 리소스 상황(CPU, I/O, Memory)을 모니터링 할 수 있음 (http://jikime.tistory.com/286)

  6. vmstat(옵션없음) - 마지막 부팅 이후의 평균값

  7. vmstat 2 10 => 2초 간격으로 10회 정보 갱신

    procs memory swap io system cpu
    r b w swpd free buff cache si so bi bo in cs us sy id wa
    현재 실행중인 프로세스의 수(CPU 접근 대기 중인 실행 가능 프로세스 수) 인터럽트가 불가능한 sleep 상태에 있는 프로세스의 수 (I/O 처리를 하는 동안 블럭 처리된 프로세스) 강제로 스왑아웃된 프로세스 사용하고 있는 swap 메모리 양(사용된 가상 메모리 용량) 사용가능한 메모리 양 버퍼로 사용되고 있는 메모리 양 캐시로 사용되고 있는 메모리 양 swap in(디스크에서 메모리로 스왑된 메모리 용량) swap out(디스크로 스왑되어 나간 메모리 용량) 초당 블럭 디바이스로 보내는 블럭 수(블록 장치로 보내진 블록) 초당 블럭 디바이스로부터 받은 블럭 수(블록 장치에서 받아온 블록) 초당 인터럽트 되는 양 초당 context switch되는 양 사용자의 CPU 사용 시간 비율(CPU가 사용자 수준 코드를 실행한 시간, 백분율 단위) 시스템의 CPU 사용 시간 비율(CPU가 시스템 수준 코드를 실행한 시간, 백분율 단위) CPU idle time(백분율 단위) 입출력 대기
    1. top
    2. 별도의 설치 필요없음, CPU 점유 프로세스들을 실시간으로 조회하는 명령어 (http://weezzle.net/1360)

    • 1줄 top : 시스템의 전반적 상태(가동시간 등)

    • 2줄 Tasks : 프로세스들의 상황

    • 3줄 CPU : CPU의 상황

    • 4줄 Mem : 메모리 상황

    • 5줄 Swap : 스왑 메모리 상황

    • 6줄

      PID
      USER
      PR
      NI
      VIRT
      RES
      SHR
      S
      %CPU
      %MEM
      TIME+
      COMMAND
      프로세스 ID 프로세스를 실행시킨 사용자 ID 프로세스의 우선순위 NICE 값 가상 메모리의 사용량(SWAP+RES) 현재 페이지가 상주하고 있는 크기(Resident Size) 분할된 페이지, 프로세스에 의해 사용된 메모리를 나눈 메모리의 총합 프로세스의 상태(Sleeping, Running, sWapped out process, Zombies) 프로세스가 사용하는 CPU의 사용율 프로세스가 사용하는 메모리의 사용율 CPU TIME, hundredths 실행된 명령어
    1. free
    2. 시스템의 실제메모리와 스왑메모리에 대한 사용현황을 확인할 수 있는 명령어 (http://blog.naver.com/PostView.nhn?blogId=jwmoon74&logNo=100174011942)
    • 1줄 Mem : 시스템의 물리적인 메모리에 대한 사용량을 각 필드 단위로 표시

      total
      used
      free
      shared
      buffers
      cached
      전체 메모리의 용량으 Kbyte단위(default)로 표시 현재 시스템에서 사용중인 메모리의 량을 Kbyte 단위로 표시 현재 시스템에서 사용중이지 않은 메모리의 량을 Kbyte단위로 표시 현재 시스템에서 공유한 메모리의 용량을 표시 현재 시스템에서 buffering된 메모리의 량을 표시 현재 시스템에서 caching된 ㅣ메모리의 량을 표시
    • 2줄 -/+ buffers/cache : 현재 캐시 메모리에서 버퍼링된 사용량을 표시(used/free)
    • 3줄 Swap : 서버설치 시에 결정한 스왑메모리의 량, 스왑메모리는 디스크의 일부분을 메모리로 잡아서 설정되기 때문에 스왑메모리가 많이 사용되고 있다는 것은 시스템의 전체적인 속도가 떨어진다는 것을 의미하며 지속적으로 스왑메모리가 사용된다는 것은 결국 실제 메모리를 증설해야 한다는 것이다.

      total
      used
      free
      시스템의 전체 스왑메모리의 량을 표시 전체 스왑메모리 중에서 현재 사용중인 스왑메모리의 량을 표시 전체 스왑메모리중에서 사용되지 않고 남아 있는 메모리의 량
    1. iotop
      1. 별도의 설치 필요함, Python 2.5+, linux kernel 2.6.20+ 이 2개의 프로그램이 기본적으로 설치되어 있어야 함
      TID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND

     

     


    CPU - perf, top, htop

    Memory - valgrind, smem

    Disk I/O - nmon, bonnie, sysstat

    Network - netperf, iftop, netstat

     

     

    출처 : http://visu4l.tistory.com/398

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG Linux

    COPY & PASTE 로 간편하게 사용할 수 있는 SQL Server Trobleshooting 에 필요한 쿼리들을 정리하려고 합니다. 다소 심플하며 성의없어(?) 보이는 포스팅이 이어질지 모르겠습니다만 이 모든 것이 이~편한 세상을 위한 것임으로 ... ㅋㅋㅋ 

     

     

    sys.dm_exec_requests

     

    SQL Server 내에서 실행 중인 각 요청에 대해 현재 처리 상태를 확인합니다. 예를 들어, 대용량 DB CHECKDB 하면서 소요 예상 시간이 궁금할 , 현재 쿼리 요청에 대한 작업 상태를 확인할 , 유용하게 적용할 있습니다. 

     

    아래 예제는 Session ID 51에 대한 진행되는 명령어, 실행 상태, 진행 작업의 백분율을 확인할 수 있습니다.

    이 때, Session ID 51에서는 DBCC CHECKDB 를 수행하고 있는 상태입니다.

    SELECT session_id, command, status, percent_complete FROM sys.dm_exec_requests 

    WHERE session_id = '51';

     

     

    [실행결과]

     

    session_id     command                    status                percent_complete

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

    51               DBCC TABLE CHECK      runnable            84.04607

    (1개 행 적용됨)

     

     

    [참고자료]

    sys.dm_exec_requests

    http://technet.microsoft.com/ko-kr/library/ms177648(SQL.90).aspx

     

    원본 위치 <http://laigo.kr/111

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG MSSQL
    --1. 블러킹 정보 확인
    SELECT * FROM SYS.sysprocesses WHERE blocked > 0

    SP_WHO
    SP_WHO2

    --2. lock 세부 정보
    sp_lock

    select *
    from sys.dm_tran_locks


    --3. 실행쿼리 확인
    DBCC INPUTBUFFER(58)


    --4.KILL

    KILL 58


    /*
    BEGIN

    IF OBJECT_ID('UNILITE.LOCK_TEST') IS NOT NULL
    DROP TABLE UNILITE.LOCK_TEST

    CREATE TABLE UNILITE.LOCK_TEST
    (
    ID INT ,
    ITEM VARCHAR(20),
    QTY INT
    )


    BEGIN TRAN


    INSERT INTO LOCK_TEST (ID, ITEM, QTY)
    VALUES(1, 'APPLE',20)
    --ROLLBACK
    --COMMIT

    END


    BEGIN

    SELECT *
    FROM LOCK_TEST

    END


    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG MSSQL

    아쉽게도 VisualStudio2012버전 부터는 Macro IDE기능을 사용할 수 없게 됐다.

    개인적으로 Doxygen용 자동주석 매크로기능을 위해 활용하고 있었는데

    VS2012부터 보이지 않아 어디에 숨겼는지 한참을 찾았다.

    결국 구글링을 해보니 MS에서 의도적으로 뺀거라고 한다. 전체 VS유저중 사용률이 1%미만 이라나.

    Macro외에도 tool 전체의 Performance향상을 위해 몇몇 IDE기능을 줄였다고 한다.

    http://social.msdn.microsoft.com/Forums/vstudio/en-US/d8410838-085b-4647-8c42-e31b669c9f11/macros-in-visual-studio-11-developer-preview

     

    암튼 자동 주석같은 매크로 기능은 플러그인 프로젝트를 통해 계속 이용할 수 있다. :)

     

    1. Visual Studio 추가기능 프로젝트 생성

    적당한 이름으로 생성해 준다.

    2. 확장 기능을 개발할 언어를 선택해준다. (난 C# 으로)

    3. 메뉴에 확장기능 항목이 추가되도록 한다. (우리가 만든 확장기능은 도구 메뉴에서 확인가능하다.)

    이후에는 계속 다음을 눌러 프로젝트가 생성되도록 한다.

     

    4. Connect.cs파일에서 Exec함수를 찾고 아래 내용을 추가한다.

    Exec라는 함수이름 대로 우리가 개발한 확장기능을 나중에 VS에서 사용할때 호출되는 함수다. (사진 클릭해서 원본으로 보기)

     

    5. 컴파일이 성공하면 종료 후 다시 실행한다.

    적당한 프로젝트 하나를 연 후 소스코드의 적당한 위치에 커서를 위치 시키고 "도구" 메뉴에 우리가 추가한 플러그인을 실행한다.

    Doxygen 주석이 추가된걸 확인할 수 있다.

     

    6. 매번 메뉴를 클릭하는건 귀찮으니 우리가 개발한 확장기능에 단축키를 할당할 수 도 있다.

    도구 - 옵션 - 키보드 선택 후 comment(프로젝트 이름)로 검색하면 개발한 확장기능이 검색된다.

    선택 후 "바로 가기 키 누르기"에서 적당한 단측키를 할당해준다.(Ctrl + `은 개인적으로 사용하지 않는 키라서 이걸로 정했음)

    확인을 누르고 닫은 후 앞으로는 메뉴 대신 위에서 할당한 단축키를 사용하면 된다.

     

    7. 확장기능 배포

    내가 만든 확장기능은 dll형태로 일반 프로젝트와 동일하게 프로젝트 폴더(Debug나 Release)에 생성된다.

    VS에 설치되는 폴더는 C:\Users\jung\Documents\Visual Studio 2012\Addins 이며 FunctionComment - For Testing.AddIn

    이라는 파일명으로 위자드가 자동 생성해준 것을 볼수 있다.

    파일을 메모장으로 열어보면 <Assembly> 항목에 우리가 만든 dll위치가 들어가 있는것을 볼수 있는데

    원하는 위치에 dll을 설치하고 이 경로만 바꿔주면 된다.

     

     

     

     추가한 소스

    public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
      {
       handled = false;
       if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
       {
        if(commandName == "Doxgen2012.Connect.Doxgen2012")
        {
                        EnvDTE.TextSelection textSelection = (EnvDTE.TextSelection)_applicationObject.ActiveWindow.Selection;
                        textSelection.Insert("//-----------------------------------------------------------------------------------//");
                        textSelection.NewLine();
                        textSelection.Insert("//");
                        textSelection.NewLine();
                        textSelection.Insert("// @brief     :  ");
                        textSelection.NewLine();
                        textSelection.Insert("// @param     :  ");
                        textSelection.NewLine();
                        textSelection.Insert("// @return    :  ");
                        textSelection.NewLine();
                        textSelection.Insert("// @author    :  ");
                        textSelection.Insert(System.Environment.UserName);
                        textSelection.NewLine();
                        textSelection.Insert("// @date      :  ");
                        textSelection.Insert(System.DateTime.Now.ToLongDateString());
                        textSelection.Insert("  ");
                        textSelection.Insert(System.DateTime.Now.ToLongTimeString());
                        textSelection.NewLine();
                        textSelection.Insert("//");
                        textSelection.NewLine();
                        textSelection.Insert("//-----------------------------------------------------------------------------------//");

         handled = true;
         return;
        }
       }
      }
      private DTE2 _applicationObject;
      private AddIn _addInInstance;
     } 

     

     

     

     참고 : http://www.dzone.com/articles/missing-macros-visual-studio

     

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG doxgen

    출처 : http://www.2cpu.co.kr/bbs/board.php?bo_table=nas&wr_id=334


    Synology NAS의 Video Station을 설치하고, PC나 폰의 DS Video로 영상을 감상하면 Frame Rate가 낮아 정상적으로 영상을 보기가 어렵습니다. 그저 스트리밍이 가능하다는것에 의의를 두는 그런 느낌이였죠. 이제 이걸 AirVideo 부럽지 않은 동영상 스트리밍 환경을 구축하여 봅시다.

    제가 전에 동영상 스트리밍을 이용하던 환경은 이렇습니다.
    1) Synology NAS + WebDAV
    2) iPad + nPlayer($2.99) + Wibro, Tethering
    3) Android + Dice Player + 3G or LTE

    1번에서 동영상 파일에 접근을 위해  WebDAV를 사용합니다. WebDAV를 사용하면 80(HTTP), 443(HTTPS)포트를 이용하기 때문에 거의 모든곳에서 접속할 수 있습니다. 해당 환경을 만든 다음, WebDAV를 지원하는 iPad(iPhone)의 nPlayer 앱이나 Android의 DicePlayer 앱을 사용하여 구축해놓은 Synology NAS에 접속하여 정상적으로 영상을 즐깁니다.

    위와 같이 사용하면 다음과 같은 문제점이 발생합니다. 
    1) nPlayer의 DTS 라이센스 관련 업데이트로 인해 DTS 코덱이 들어간 영상은 소리가 나오지 않습니다. (AV Player 등 타 플레이어도 마찬가지)
    2) 모바일 환경(3G, Wibro, LTE)에서 대역폭 이상의 높은 비트레이트의 파일을 재생할 경우 끊김이 발생할 확률이 높습니다. 통신망 환경이 좋지 못하면 더더욱 그렇습니다.

    따라서, 두가지의 문제를 해결하려면 트랜스코딩(실시간 인코딩)이 가능해야 합니다. 첫번째 해결책은 iOS의 AirVideo 앱을 이용하는 것이고, 두번째 해결책은 Video Station을 이용하는 방법이 있습니다. iOS의 AirVideo를 써 본 결과, 컨테이터에 따라 자막까지 알아보기 힘든 화질 저하가 발생하는 영상들이 있었기에 해당 앱으로는 일찌감치 포기하였습니다. 안드로이드의 경우 Qloud Media가 있는것 같은데 써보지는 않았고요. 때문에, 마지막 카드인 Video Station에 집중하여 봤습니다.

    Synology NAS에 들어간 Video Station은 트랜스코딩도 지원할 뿐더러, 방대한 양의 DB를 통해서 라이브러리를 정리해 주기도 합니다. 정말 좋은 App이죠. 다 좋은데 이놈의 문제점은 트랜스코딩 과정을 거치면서 Frame Rate가 반토막나서 영상을 집중하여 보기가 힘듭니다. 고민을 좀 하다가, 시놀로지 홈페이지에 패키지 파일이 올라온것을 캐치하여 한번 뜯어보았습니다. tar로 압축이 되어있었기 때문에, 의외로 쉽게 찾아 낼 수 있었습니다. 

    이 과정에서 제가 목표했던것은 다음과 같습니다.
    1) Frame Rate가 24프레임 이상 지원 되어야 할 것
    2) 자막은 필수
    3) 모바일 환경에서 통신망 환경에 따라 비트레이트 변경을 통해 끊김없는 감상이 가능해야 할 것.



    자, 그럼 시작합니다.

    1) Frame Rate가 24프레임 이상 지원 되어야 할 것
    먼저, 패키지 관리자에서 Video Station을 설치하여 줍니다. (이미 설치되어있다면 패스) 
    그 다음 시놀로지 관리패널 > 제어판 > 터미널로 들어가 SSH 서비스 활성화에 체크하여 줍니다. 

    다음으로, 쉘을 이용해 root 계정으로 시놀로지에 접속합니다. 비밀번호는 관리패널의 admin 계정과 같습니다. 윈도우 사용자는 putty를 이용하세요.
    MacBook-Pro:~ Ryun$ ssh root@192.168.0.100

    그다음 VideoStation이 설치된 디렉토리로 접근합니다. 여기서 저의 경우 VideoStation이 volume1(볼륨1)에 설치되어있기 때문에 경로는  /volume1/@appstore/VideoStation/etc 로 나타납니다.
    RyunBox> cd /volume1/@appstore/VideoStation/etc

    그 다음 뭐가있는지 확인해보면, 
    RyunBox> ls -l

    drwxr-xr-x    2 root     root          4096 May  1 03:20 HDHomeRun_channelmap
    drwxr-xr-x    2 root     root          4096 May  1 03:20 KernelFirmwares
    drwxr-xr-x    2 root     root         20480 May  1 03:22 KernelModules
    -rw-r--r--    1 root     root           189 Apr 12 16:18 SYNO.SDS.VideoStation.Application.legacy.conf
    -rw-r--r--    1 root     root         37580 Apr 12 16:18 TransInfo_HLS
    -rw-r--r--    1 root     root         41468 Apr 12 16:18 TransInfo_SS
    -rw-r--r--    1 root     root          5034 May  1 04:17 TransProfile
    -rw-r--r--    1 root     root            95 Apr 12 16:18 country
    -rw-r--r--    1 root     root            31 Apr 12 16:18 default.conf
    drwxr-xr-x    2 root     root          4096 May  1 03:20 index
    -rw-r--r--    1 root     root           571 Apr 12 16:18 model
    -rw-r--r--    1 root     root           740 Apr 12 16:18 modelmap
    -rw-r--r--    1 root     root           865 Apr 12 16:18 plugin.conf
    drwxr-xr-x   38 root     root          4096 May  1 03:20 synodvb_t
    -rw-r--r--    1 root     root           262 Apr 12 16:18 videostation_port

    여기서 트랜스코딩에 관여하는 설정이 들어간 파일은 3가지 정도 됩니다.  (모두 JSON String으로 되어있음)
    -rw-r--r--    1 root     root         37580 Apr 12 16:18 TransInfo_HLS
    -rw-r--r--    1 root     root         41468 Apr 12 16:18 TransInfo_SS
    -rw-r--r--    1 root     root          5034 May  1 04:17 TransProfile

    TransInfo_HLS와 TransInfo_SS는 원본파일의 컨테이너를 분류하기 위한 JSON String이 들어가있고, TransProfile은 그렇게 컨테이너별로 분류된 것에 따라 실제 ffmpeg에 넣어주는 Parameter에 대한 정보가 JSON String으로 들어있습니다. 분류는 VideoStation이 알아서 해 줄 것이므로, 따라서 우리는 TransProfile 파일만 고쳐주면 됩니다 :) 
    (분류를 따로 해주고 싶으시다면 TransProfile을 참고하여 TransInfo_HLS TransInfo_HLS의 String을 변경해주시면 됩니다.)

    그럼 vi에디터로 TransProfile 파일을 열어봅시다.
    RyunBox> vi TransProfile
    {
    "mpegts_cga_x264_vb200_vr_aac_ab40": "-threads 0 -vcodec libx264 -vsync 2 -vprofile baseline -level 30 -s cga -b 200k -r 15 -acodec libfaac -ab 40k -ac 2 -f mpegts -mpegts_copyts 1",
    ....
    ...
    (파일 내용이 조금 긴 관계로 맨 처음 줄만 가져오고 나머지는 생략합니다.)

    여기서 Frame Rate를 담당하는 부분은 "-r" Parameter 입니다. 기본으로 15로 세팅되어 있는데, 이를 24이상으로 바꾸어 줍니다. 전 30으로 설정해 주었습니다. 이 부분은 NAS 성능에 따라 적당히 조절하여 주셔야 합니다. 참고로, 시중에 유통되는 DVD(NTSC PAL)의 Frame Rate는 23.976fps 입니다. 따라서 24fps 정도면 원활한 영상을 보실 수 있습니다.

    "mpegts_cga_x264_vb200_vr_aac_ab40": "-threads 0 -vcodec libx264 -vsync 2 -vprofile baseline -level 30 -s cga -b 200k -r 30 -acodec libfaac -ab 40k -ac 2 -f mpegts -mpegts_copyts 1",

    첫줄에 해당하는 것을 바꾸었으니, 다른 줄도 모두 바꾸어 줍니다. -r 값이 없는것들은 명시적으로 적어주시면 됩니다.
    :wq! 명령어를 입력하여 저장하고 나와서 이제 DS Video+ 앱으로 트랜스코딩을 하면 자연스러운 영상이 재생되는걸 보실겁니다.
    이렇게 3개의 조건 중, 1번 조건은 만족하였습니다.


    2) 자막은 필수
    두번째로, 자막이 나와야 합니다. 이 부분은 간단합니다. 보통 우리가 보는 영상에 같이 들어있는 대부분의 자막은 smi 확장자로 이루어져 있습니다. 하지만 Video Station에서는 srt 자막 파일만 지원하기에, 포털 사이트에서 smi to srt 변환 프로그램을 이용해서 smi 파일을 srt로 변환한 다음, 같은 폴더에 넣어주면 해결됩니다. 자막은 나중에 플레이 시작시 물어봅니다. (좋은건, 자막이 영상에 같이 인코딩되지 않음!)


    3) 모바일 환경에서 통신망 환경에 따라 비트레이트 변경을 통해 끊김없는 감상이 가능해야 할 것.
    마지막은 더이상 할게 없습니다. 720P영상들 중 1.4GB정도 되는 영상들은 영상 비트레이트가 1400~1600kbps 정도 됩니다. 원활한 3G 환경에서는 끊김없이 재생이 가능한데, 아시다시피 모바일 네트워크 환경은 Handover 구역이라던지, 늘 외부 요소에 의해 불안정 합니다. 따라서, 비트레이트를 조정할 방법이 있어야 합니다. 하지만 이 부분은 감사하게도 VideoStation에서 이미 지원하고 있습니다.

    DS Video+ 앱의 옵션에 가보면 영상 품질을 높음, 중간, 낮음,자동 처럼 3~4단계로 분류하고 있는데, 원본은 말 그대로 원본 파일 자체를 플레이어에 넘겨주는 것이고(iOS에서 MP4 컨테이너 외 재생 불가), 나머지 옵션은 트랜스코딩 옵션을 켜야 작동합니다. 옵션 별 비트레이트는 아까 위에서 수정하였던 TransProfile 파일에 잘 들어가 있으며, 기본적으로 VideoStation에서는 해상도와 영상 컨테이너에 따라 200k~1500k 까지의 품질을 지원합니다. 만약 변경하고 싶으신분은 파일을 열어 "-b" Parameter를 수정하여 주시면 됩니다. 통신망 상태에 따라 화면이 끊길때 중간중간 옵션을 변경하면서 감상하시면 됩니다.


    4) 마치며
    이렇게해서 목표를 모두 달성했습니다. 대부분의 코덱에 관련없이 PC, iOS, Android에서 자막과 함께 부드러운 영상 재생이 가능하며, 모바일 환경을 감안하여 비트레이트를 조절해서 볼 수 있습니다. 개인적으로는 VideoStation의 라이브러리 기능까지 더 해지니, 어떤 Application보다 더 좋은 스트리밍 환경을 구축했다고 봅니다.


    ps. TransProfile파일은 VideoStation 업데이트마다 매번 수정해주어야 할 것 같아 보입니다.
    ps2. smi 파일을 srt로 변환하는 스크립트를 이미 포고플러그에 적용중인 사람들이 있는것 같으니, 변환 프로그램이 불편하다면 직접 시놀로지에 적용시켜보는것도 좋을듯 하네요.
    ps3. Android에서 제조사 기본 동영상 플레이어에 따라 자막이 안나올수도 있으니, 그렇다면 DicePlayer를 이용하시는것도 괜찮습니다.

     


    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea

    Memory Allocator for Multithreaded Applications

    출처 : http://www.mimul.com/pebble/default/2011/05/15/1305469792430.html 

     

    응용 프로그램에서 메모리 사용은 필수 불가결한 거죠. 그런데도 메모리에 대한 사용상 효율, 성능을 잘 고려하지 않고 그냥 있는 malloc을 사용하고 말죠. 그런데 트래픽이 많은 서비스가 요즘 많아지다보니 메모리 사용에도 신경을 써야하는 경우가 많습니다.
    그래서 메모리 할당에 관해 정보를 정리하고 공유합니다.


    메모리를 할당받기 위해 사용하는 malloc()함수는 일반적으로 glibc에 포함된 메모리 할당자에서 구현이 되어 있습니다. 그리고 메모리 할당자는 ptmalloc입니다.
    메모리 할당자의 역할은 brk/sbrk/mmap등을 사용해서 시스템으로부터 큰 메모리 영역을 할당 받아서, 이것을 적절하게 분할하여 어플리케이션이 요청하는 메모리 할당을 처리하게 됩니다.
    하지만, 빈번하게 메모리를 할당/해제하고 수십 개의 쓰레드가 동작하는 프로그램에서는 어쩔 수 없이 메모리 단편화(Memory Fragmentation)가 발생하여 메모리 사용량이 늘어나게 되죠.
    그래서 규모가 큰 어플리케이션들은 glibc의 기본 메모리 할당자인 ptmalloc이 메모리 단편화 문제가 심하고, multi thread, multi processor에 대한 고려가 적어서 performance bottleneck이 존재하여 다른 메모리 할당자를 사용하는 경우가 있습니다.


    데이터 섹션(메모리 사용 구조)

     - 전역 메모리(Global Memory) : 지역 함수(Local Function), 전역 변수(Global Variable), 정적 변수(Static Variable)
     - 스택 메모리(Stack Memory) : 지역변수와 매개 변수가 저장된 곳. 컴파일 타임에 크기 결정. 메모리 생명 주기를 알기 때문에 단편화가 안일어남.
     - 힙 메모리(Heap Memory) : 동적 메모리 할당을 위한 곳. 프로그래머가 할당 및 해제해야 함. 런타임에 크기 결정. 메모리 생명 주기를 모르기 때문에 단편화가 일어남.

    컴파일러 및 링커가 메모리 할당 기능을 수행할 때에는 메모리 단편화가 일어나지 않습니다. 왜냐하면 컴파일러가 데이터의 수명을 알고 있기 때문입니다.
    메모리 할당자는 기본적으로 오버헤드, 내부 단편화, 외부 단편화 등 3가지 측면에서 메모리를 낭비합니다.

    메모리 낭비 종류

     - 오버헤드 : 메모리 할당자는 할당 상태를 설명해 주는 일부 데이터를 저장해야 하는데. 즉, 모든 자유 블록의 위치, 크기, 소유정보, 그리고 내부 상태와 관련된 정보를 저장해야 합니다. 일반적으로 메모리 할당자가 이러한 오버헤드(부가) 정보를 저장하기에 가장 좋은 장소는, 할당자가 자체 관리하는 메모리.
     - 내부 단편화 : 모든 메모리 할당 작업은 프로세서 아키텍처에 따라 4, 8, 16으로 나뉠 수 있는 주소에서 시작되어야 함. 이렇게 미리 정의된 크기로만 클라이언트에게 블록을 할당하는 데는 또 다른 이유가 있을 수 있다. 만약 클라이언트가 41바이트 블록을 요청하면 42, 48 또는 그 이상의 바이트를 얻게 된다. 클라이언트가 요청한 크기를 반올림한 결과 남게 되는 여분의 공간. 이것이 내부 단편화.
     - 외부 단편화 : 애플리케이션이 연속해서 세 블록을 할당한 후, 가운데를 제거하면 외부 단편화가 발생.


    Memory Allocator 종류

     - tcMalloc : Google에서 만들었고 오픈 소스며, 메모리 프로파일 기능도 제공함. Linux, 작은 사이즈의 allocation에 최적화 된 것, MacOsx, Windows 지원. 레퍼런스 가장 많음. 임베드 시스템에도 많이 활용됨, NUMA 아키텍처 지원
     - jemalloc : 오픈 소스며, Linux ,MacOsx에 최적화 되었고 Windows도 지원. FireFox, Facebook에서 쓰여 레퍼런스가 늘어가고 있음. NUMA 아키텍처 지원은 아직 레퍼런스 찾지 못함
     - nedMalloc : 오픈소스며 간단하고 Windows에 최적화. dlmalloc을 뿌리로 둠.

    싱글 스레드 환경에서는 ptmalloc과 위의 메모리 관리자와의 성능과 효율이 크게 다르지 않을 수 있고 멀티 쓰레드 환경에서 위의 메모리 할당자를 사용하므로써 많은 성능 향상을 보았다고 함.


    tcMalloc 소개

     1. 작동 방식(성능 향상 방식)
      - 중앙 메모리 관리자와 쓰레드별 메모리 관리자를 구분하고 작은 크기(32K 이하)의 메모리 할당/해제 요청은 쓰레드별 메모리 관리자가 처리하고, 부족할 경우 중앙 메모리 관리자에서 얻어오는 방식으로 처리함.
      - 큰 메모리(32K 이상)는 전역 관리자에서 페이지 크기(4K) 단위로 클래스를 나누어 mmap()을 이용하여 할당하는 함.
     2. 지원 환경
      - Linux (32 and 64 bit), Mac OSX, Windows (32 bit only), Solaris
      - NUMA-aware TCMalloc(NUMA아키텍처 지원됨)
     3. 사용 사례
      - Webkit, MySQL, HyperTable, Memcached, Redis, Nginx


    jemalloc 소개

     1. 작동 방식(성능 향상 방식)
      - 쓰레드별 메모리 관리자 Arena와 작은 단위의 잦은 메모리 할당의 경우, arena를 참조 하지 않고, 바로 malloc을 할 수 있도록, 각 스레드에게 thread cache라는 영역을 가지고 있어 성능 향상을 줌.
     2. 지원 환경
      - Linux(32, 64 bit), Windows, MacOSX
     3. 사용 사례
      - FireFox, FaceBook


    nedMalloc 소개

     1. 작동 방식(성능 향상 방식)
      - dlmalloc에서 출발했고, 오픈 소스며 내용은 간단하지만 성능도 좋다는 평으로 알려짐.
     2. 지원 환경
      - Windows(32) 최적화됨, MacOSX, Linux
     3. 사용 사례
      - 소소한 개인 사용자 위주. 윈도우 개발자들이 많이 채택함. 일부 Javascript 엔진에 사용


    SLAB Allocator

     - 슬랩은 내부 단편화 문제를 해결할 뿐만이 아니라 커널 내에서 흔히 일어나는 dynamic memory 할당의 overhead를 줄이기 위하여 캐싱하는 역할을 하여 성능 개선에도 큰 도움을 주고 있음.
     - 캐시는 관리가 필요한 오브젝트 종류별로(예를 들면task_struct, file, buffer_head, inode 등) 작성되고 그 오브젝트의 슬랩을 관리하고 슬랩은 하나 이상의 연속된 물리 페이지로 구성되어 있으며, 일반적으로 하나의 페이지로 구성된다. 캐시는 이러한 슬랩들의 복수개로 구성됨.
     - 자주 사용되는 오브젝트들을 미리 할당하여 놓고 사용자 요구가 있을 때 마다 바로 반환하는 것이다. 이것은 물리 메모리를 확보하기 위하여 검색 및 회수, 반환과 같은 긴 여행을 떠날 필요가 없으므로 시스템의 성능을 향상시킨다. 또한 다 사용된 오브젝트들을 반납받아 커널의 메모리 할당자에게 반환하지 않고 보관했다가 재사용하기 때문에 시스템의 성능을 향상시킬 수 있게 됨.


    응용 예(MySQL-tcMalloc)

     - MySQL 성능 향상 위한 tcMalloc 적용 예


    // 64bit 머신일 경우에만 필요함. libunwind
    wget http://download.savannah.gnu.org/releases/
      libunwind/libunwind-0.99-alpha.tar.gz
    tar zxvf libunwind-0.99-alpha.tar.gz
    cd libunwind-0.99-alpha/
    CFLAGS=-fPIC ./configure
    make CFLAGS=-fPIC
    make CFLAGS=-fPIC install
    
    wget http://google-perftools.googlecode.com/
       files/google-perftools-1.7.tar.gz
    tar zxvf google-perftools-1.7.tar.gz
    cd google-perftools-1.7/
    ./configure
    make && make install
    
    vi /etc/ld.so.conf
    /usr/local/lib 라인 추가
    /sbin/ldconfig
    
    vi /usr/local/mysql/bin/mysqld_safe
    export LD_PRELOAD=/usr/local/lib/libtcmalloc.so
    


    [참고 사이트]

     

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG tcmalloc

    출처 : http://www.gamedevforever.com/165

     

     

     

    Posted by 죠쉬

    지난 두번에 걸쳐 다루었던 PGP 방식의 프로토콜은 거의 모든 상황에서 사용이 가능한 완전체에 한없이 수렴하는 프로토콜 이다. 그렇기 때문에 대부분의 환경에서 안전하게 데이터를 주고받는 용도로 사용할 수 있는 좋은 방식이기도 하다. 왜 좋은지에 대해서는 아래의 링크에서 확인해 보시라.

     

    http://www.gamedevforever.com/141


    위의 포스팅에서 i5 2.5GHz 급의 CPU라면 하나의 코어로 대략 초당 282,489,299 바이트, 17,655,581번의 128비트 AES 암호화 연산이 가능하다는 것에 대해 이야기 했었다. 계산해 보면 개발중인 게임에서 PGP, GPG 혹은 SSL을 사용할 수 있을지 여부에 대해 확인 해 볼 수 있을 것이다. 틀림없이 어떤 환경에서는 그정도의 연산 조차 아끼고 싶은 경우가 있을 수 있다. 특히 휴대용 게임기나 스마트 폰 등의 제한적인 환경이라면 더더욱 암/복호화 연산이 부담스러울 가능성이 있다.

    이번 글을 통해 전달하고자 하는 내용은 두 가지 이다. 첫번째는 XOR이 암호화 하는데 사용되는 특징이 있기는 하지만, 그 자체는 암호화하는 효과가 전혀 없으므로 XOR 만 가지고는 해결되지 않는다는 것을 보이는 것 이고, 두번째는 암호화를 할 때 사용하는 몇가지 기법을 이야기 하고자 한다.


    1. XOR를 이용한 데이터 숨기기

    XOR은 CPU에서 제공하는 연산이므로 128비트를 암 복호화 하는데 만여 사이클이 소요되는 AES 암/복호화와 달리 많아야 100 사이클 정도면 128비트를 암호화 할 수 있다. 물론, 구현하는 방법에 따라, CPU의 종류에 따라 달라질 수 는 있겠지만, 근본적인 차이는 발생할 수 없다. XOR를 이용하여 암호화 한다면, 대충 다음과 같은 방법으로 암호화가 진행 될 수 있을 것 이다.



    오! 그럴듯 해 보인다. 뭔가 알 수 없는 형태로 데이터가 변환되어서 전송되었다. 훔쳐보고 싶어 하거나, 조작하고 싶어하는 공격자는 원래 데이터가 뭔지 상상할 수 도 없을 것이다. 그러니 이걸로 대 만족이다! 그럼 이걸로 글을 마치겠다... 일리가 없잖습니까?

     

    위에 언급한 XOR 방식의 가장 치명적인 단점은 쉽게 키의 크기를 알아낼 수 있고, 키 자체도 알아내기 어렵지 않다는 것 이다. 물론 어떤 방식으로 XOR를 했는지 알아내기 위해서 시간을 투자해야 하겠지만 결국 알아내는 건 많이 어렵지 않다. 그럼 위의 방식을 한번 공격해 보겠다. 먼저 공격을 위해서 한가지 전제를 해야 한다. 위의 방식을 이용해서 모든 데이터를 암호화 한다 - 채팅 내용까지 위의 방식을 사용한다는 전제를 해두겠다. 물론, 채팅을 암호화 하지 않는다고 해서 공격하는게 불가능 하지는 않지만, 가장 간단한 방법으로 공격하기 위해서 채팅의 내용까지 암호화 한다고 전제 하는 것 이다.

     

    공격 방법은 아주 단순하다. 채팅창에 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[각주:1]"를 입력하는 것 이다. 그리고 전송되는 패킷을 관찰하면 다음과 같은 내용을 포함한 패킷이 날아가는걸 볼 수 있을 것 이다.

     

     

    6E2AE83 6E2AE83 6E2AE83 6E2AE83 .... 6E2AE83 6E2AE83 6E2AE83 6E2AE83

     

    보기 좋으라고 네 바이트 단위로 잘라 놓기는 했지만, 어찌 되었든 XOR를 이용해서 숨겼다는 사실만 알면, 반복되는 주기만 관찰을 하면 쉽게 키의 크기를 알 수 있다. 위의 경우에는 숨길 때 사용한 키의 크기가 네 바이트 라는 것을 알 수 있다. 그리고, 운영체제에서 기본적으로 제공하는 계산기를 실행시킨 후 다음을 계산해 보면 된다.

     

    6E2AE83 XOR 41414141 (AAAA의 아스키 코드 값)

     

    그러면, 짜잔! 계산기에 다음과 같은 값이 나올 것 이다.

     

    47A3EFC2

     

    뭐... 뭐냐?

     

    프로그램 코드를 분석해 볼 필요도 없다. 그냥 시도해 보면 된다. 해봐서 아니면 그냥 다른 방법을 사용했을 가능 성이 높으니 사용된 다른 방법이 무엇이 있을지 고민하면 된다. 위의 암호문을 포함한 패킷이 날아가고, XOR을 이용한 게 아니라면 답은 하나 뿐이다. 128 비트 블럭 암호를 ECB 모드로 암호화 한 것 이다. 원하는 평문에 대한 암호문을 알아낼 수 있으니 이전에 이야기 했던 차분 공격법을 이용하면 오래 걸리지 않아 키를 알아 낼 수 있을 것이다. 또 다른 문제는, 궂이 키를 알아내지 않더라도 전달되는 데이터의 일부를 변경할 수 있다는 문제점도 있다. 단순히 특정 비트-바이트의 값을 수정하는 것 만으로도 원하는 데이터를 변형 할 수 있다. 그렇기 때문에 XOR로 데이터를 숨기는건 그다지 효용성이 높지 못하다.



    2. 기존의 암호화 방식을 끌어다 써보자 - CBC의 Chaining 을 가져다 써보자

     

    그러면 어떻게 해결을 해야 할 것 인가? 위에서 잠시 말했듯 이 암호화된 문자열이 6E2AE83 6E2AE83 6E2AE83 ... 와 같이 주기를 가지고 반복된다면 해당길이의 키를 사용한 XOR 이거나 ECB 모드를 사용한 블록암호중 하나로 볼 수 있는 것 이다. 이전 게시물 에서 ECB 모드의 문제점에 대해 이야기 하면서 다른 모드를 사용하면 문제가 해결 된다는 것에 대해 언급한 것을 혹시 기억할지 모르겠다. 다시 말해서 위의 경우에도 CBC, PCBC, CFB, OFB 등등의 방식을 흉내내서 문제를 해결 할 가능성이 있다는 걸 생각해 볼 수 있을 것 이다. 


    1에서 언급된 문제를 해결하기 위해, 다음과 같이 CBC를 변형해서 XOR를 이용해서 전송방식 - 프로토콜을 변형해 보도록 하겠다.


    XOR를 이용하여 원래 데이터를 숨길때 Chaining을 해보았습니다!


    오! 이전 보다 조금 더 그럴듯 해 보인다. 이전 암호화 단위의 암호문을 그 다음 단위의 평문을 암호화하는 키로 사용했다. 복호화 할 때에는 첫 번째 값을 그 다음 번 값을 복호화하는 키로 사용해 주면 된다. 게다가 이 방법을 사용하면 공격자가 임의의 위치에 있는 데이터를 바꾸게 되면 그 이후의 데이터들도 영향을 받게 되기 때문에 전달되는 데이터는 복호화 하기 전엔 변경할 수 없다. 이렇게 하면 같은 키를 사용하더라도 처음부터 끝까지 완전히 같은 데이터가 아닌 다음에는 만들어진 암호문이 다르게 보일 것 이다. 이렇게 해주면 공격자는 키를 예측하기 위해 더 많은 고민을 할 수 밖에 없다. 똑같이 단순하게 XOR를 사용한 것 이지만 앞서 사용했던 방법 보다는 조금더 공격자를 고민하게 만들 수 있다. 그러나, 같은 평문은 같은 암호문을 만들기 때문에, 공격자는 어떤 규칙이 있다는 것을 쉽게 발견해 낼 수 있고, 공격 방법을 찾아내기 위해 그 규칙을 이용 하려고 시도 할 것 이다. 그러면, 어떻게 문제를 해결 할 수 있을까?



    3. 그럼, 암호화 할 때 마다 다른 결과를 얻도록 해보자


    문제를 해결하기 위해서 암호화 하는 것의 가장 기본적인 정의를 끌어들여 보겠다. 가장 안전한 암호화 방식은 공격자가 암호문과 랜덤 값 간에 구별을 하지 못하는 방식이다. 즉, 평문을 암호화 해서 만든 데이터 100 바이트와, 랜덤 함수를 이용해서 만든 데이터 100 바이트를 구별하지 못하면 - 암호화 해서 만들어진 암호문이 랜덤 값 100 바이트로 보인다면 공격자는 공격할 방법을 찾지 못하게 된다. 이 내용을 단순하게 문자 그대로 받아들여 보자. 다시 말해서 평문의 첫 네 바이트를 랜덤값으로 채워 넣는 것이다. 이 방식은 블록암호의 모드에서 사용되는 IV와 유사한 역할을 하는 것이다. 오히려 블록 암호의 모드에서는 IV 값을 알아야 하지만, 이 경우에는 최초의 랜덤 값을 몰라도 복호화가 되기 때문에 IV를 전달하지 않아도 된다. 이 아이디어를 적용한 방식을 그림으로 그리면 다음과 같이 될 것 이다. 


    랜덤 값을 초기 값으로 하여 Chaining을 해보았습니다. 매우 그럴듯해 보입니다.


    굉장히 그럴 듯 해 보이는 결과물을 얻었다. 랜덤 값을 특정 값과 XOR을 하면, XOR의 특성상 그 결과 값은 랜덤 값 일 수 밖에 없다. 그러므로, 랜덤 값과 키를 XOR를 하면 랜덤 값이 되고, 암호화 하고자 하는 평문 값과 앞서 얻은 키가 포함된 랜덤 값을 XOR를 해주면 랜덤 값이된다. 출력 결과물인 암호문은 말 그대로 랜덤 값 열이기 때문에 공격자는 전송되는 데이터가 암호문인지 아니면 그냥 랜덤값을 보내는 것 인지 구별이 불가능하다. 암호학에서 말하는 가장 이상적인 암호문 인 것이다! ... 이게 사실 이라면, 그동안 암호학을 연구하던 수학자와 암호 연구자들은 전 부 다 Bing신이 되는 거다. 그러나, 필자같은 반푼이도 아는 것을 두번째로 똑똑하다고 하면 암살이라도 할 똑똑한 분들께서 발견하지 못하고 복잡한 수학적 계산을 필요로 하는 블록암호를 만들었을리는 없는 것 이다.


    위의 그럴듯해 보이는 방법의 문제점은, 키가 없어도 암호문을 복호화 할 수 있다는 것 이다. 일단 다음 그림을 보자.


    슬프게도, 키나 첫 번재 랜덤값은 몰라도 암호는 깨진다.


    위 그림을 보면 알겠지만, 전송되는 암호열의 첫번째 암호문을 그 다음 암호블록과 XOR 해주면 평문을 얻을 수 있다. 그러므로 애시당초 키는 필요도 없었다. 공격자가 암호화 방식을 모른다면 혹시 속아넘어가 줄 수... 조차 없다. XOR의 특성상 Chaining 방식은 사용이 불가능 하기 때문이다. 2에서 언급된 방법을 이용해서 암호화를 하는 경우 그 앞에서 사용했던 "A" 잔뜩 입력하기 기법을 사용하면 다음과 같은 결과물을 얻게 된다.


    6E2AE83 47A3EFC2 6E2AE83 47A3EFC2 6E2AE83 47A3EFC2 6E2AE83 47A3EFC2


    그리고, 여기에서 언급된 방법 이라면 다음과 같은 결과를 얻게 될 것 이다.


    RANDOM RANDOM 47A3EFC2 RANDOM 47A3EFC2 RANDOM 47A3EFC2 RANDOM 47A3EFC2

     


    ...

    (다시 한 번 수고해 주신 L 군에게 감사한다.)


    패턴이 반복 될 뿐 아니라, 전송되는 데이터열에 비밀 키 까지 예쁘게 포함되어 있다. 이... 이건 아니잖아...


    4. 결론


    기본적으로 XOR 연산은 두 값을 섞어서 제 삼의 값을 만들어 내기 때문에 암호화에 사용할 수 있을 것 처럼 보인다. 그러나 XOR 연산은 '복원'의 특성을 가지고 있기 때문에 XOR 만을 이용하여 암호를 만든다면 알려져 있는 그 어떤 방식을 사용한다 할 지라도 약간의 노동 만으로도 원문을 얻어낼 수 있다. 게다가 XOR 은 사칙연산과 마찬가지로 아주 단순하기 때문에 그 자체만 가지고는 응용할 수 있는 영역에 한계가 있다. 그러므로 아무리 연산시간이 부족하다 할 지라도 XOR 만 가지고 전송하는 데이터를 암호화 하려는 시도는 원하는 목적을 달성 할 수 없을 것 이다.


    겨우 그딴 소리를 하려고 이 글을 읽게 만들었단 말이냐! 죽고 시프냐!


    자... 잠시만, 돌은 내려 놓으시고! !!! !!!! !!!! 아악!


    그러나 XOR은 제일 처음에 말씀 드렸던 것과 같이 암호하는데 사용됩니다... 되... 되옵니다. 그렇기 때문에 XOR를 사용하는데 있어 한계를 알고 계시옵는 것은 이 후에 다른 방법을 사용하시어 전송하는 데이터를 암호화 하시옵는데 도움이 되실 것 이옵니다. 다음 번에는 XOR과 의사난수 발생장치(PRNG)와 해시 함수(HASH FUNCTIONS)를 이용하여 빠르고 깨지기 힘든 암호문을 만들어 전달하는 방법에 대하여 설명해 올리도록 하겠사옵나이다. 그리고, 그 다음에는 실제 사용할 수 있을 법 한 프로토콜을 만들어 올리겠나이다... 그... 그러니... 쇠구슬은...


    아악!!!!!


    *     *     *


    '발행' 버튼을 누르면 예약일과 상관없이 바로 포스트가 열린다는 걸 몰랐습니다. 그래서 어제 밤에 쓰다만, 아니 타이핑 하다 만 글이 올라가 버리고 말았네요. 오늘 내일 여유있게 쓰려고 했는데... 일단 트윗에 포스트가 날아갔기에 부랴부랴 타이핑 작업을 마쳤습니다. 혹시 미리 보시고 '이쉐키 뭐얏'이라고 생각하신 분이 계시다면 용서하시길 부탁드립니다. (__)


    우좌지 당간에, 그런 이유로  원래 스케쥴 보다 이틀 일찍 포스트 했습니다.


    1. A의 갯수를 세지는 말아 주세요. 그냥 충분히 많은 갯수의 A인 겁니다. 한 백개정도? [본문으로]
    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG 보안


     

    게임 해킹을 막는 보안 전략 

     

    온라인 게임을 지켜라!

     

    게임 해킹을 막는 보안 전략

     

    이른바 ‘핵’을 사용하는 많은 유저들 때문에 게임 밸런스가 파괴되며 매크로 범람으로 인해 게임 서버 부하가 늘어나게 된다. 또한 늘어나는 게임 해킹 툴 탓에 상용 서비스가 늦어지고 ‘핵 천지’ 게임이라는 오명에서 벗어나기가 어렵다. 게임 서비스에 치명타를 주는 온라인 게임 해킹은 어떤 식으로 대응할 수 있을지 현재의 동향과 그 보안 전략을 통해 살펴보자.

     

    온라인 게임 해킹/보안에 관한 글이 소개될 때는 항상 시작되는 서두가 있다. 우리나라의 온라인 게임은 양질로 급부상했으며, 아이템이나 게임머니들이 실제의 현금성 가치로 이어지면서 온라인 게임 분야에도 해킹 분야가 범람하고 있다는 사실을 환기하는 것이 그 내용이다.

     

    이제 온라인 게임 해킹에 대한 인식은 충분하다

     

    하지만 이제 이런 프롤로그를 작성할 시기는 지났다고 생각한다. 지금은 인식이 많이 넓어져 온라인 게임 해킹이라는 것에 대해 대부분 사람들이 인지하고 있으며 게임 개발자 치고 게임 핵에 당해 보지 않은 개발자가 거의 없다고 해도 과언이 아니다. 따라서 이젠 온라인 게임 해킹에 대한 콘텐츠를 구성할 때 게임 해킹에 대한 이해를 돕기 위해 그 개념을 풀어 설명하는 과정은 더 이상 필요 없다고 필자는 생각한다. 이제 온라인 게임 해킹도 웹 해킹이나 시스템 해킹처럼 그 분야와 목적성이 사람들 사이에서 뚜렷하게 밝혀져 있으며 이미 기반도 많이 닦여 있는 상황이다. 따라서 이제 개발자들은 온라인 게임 해킹에는 어떤 것이 있고 어떻게 이뤄지는가 등의 내용보다는 어떻게 대응해야 하고 어떤 처리 방법을 고려할 수 있을까에 더 주목하고 있는 편이다.

     

    해킹 소개에는 대응 방법이 반드시 뒤따라야 한다

     

    수많은 온라인 게임 해킹 소개에 있어서 필자가 가장 아쉬웠던 점은 콘텐츠 구성에 있어서 해킹 그 자체의 내용에만 중심을 두고 그 대응 방법은 거의 언급하지 않는다거나 아니면 결국에는 특정 솔루션의 홍보로 이어지는 내용이 많았다는 것이다.


    모의해킹 업무를 오래도록 해온 사람들이 일차적으로 직시하고 있는 자세가 있다. ‘모의해킹의 목적은 해킹의 근본적인 차단에 있다’라는 것이다. 따라서 해킹을 통해 현재 시스템의 취약점을 발견했으면, 그에 대한 대응법도 당연히 거론해 주어야 하는 것이 보안 컨설턴트의 가장 기본적인 의무임은 당연하다. 그러나 온라인 게임 해킹은 그러한 해킹/보안 컨설팅의 기본적 절차가 무시되는 경우가 많으며 게임이 치트 당하는 그 모습에만 포인트를 두어 보여주는 사례가 적지 않다. 물론 이 방법은 사람들의 이목을 꽤나 쉽게 끄는 편이다. 일차적으로 게임 해킹이라는 해킹의 희귀 분야를 선보이고 있고 또 게임이 치트 당하는 모습은 벽을 타고 날아다닌다거나 총알이 무한대로 떨어지지 않는다거나 하는 누가 봐도 진기한 광경들이 많기 때문이다


    하지만 필자는 이러한 형식의 PT는 별로 가치가 없다고 생각한다. 이미 수많은 게임 개발자들은 게임을 서비스하면서 다양한 핵을 경험해 보았다. 뿐만 아니라 게임 보안 컨설팅을 받거나 세미나에 참석하는 이유는 그런 해킹을 몰라서가 아니라 그런 해킹에 어떤 식으로 대응해야 하는지에 대한 노하우가 필요해서다. 필자가 생각하기에 그냥 단순히 게임 해킹 광경을 보여주는 식의 세미나는 초등학생이 인터넷에서 게임 핵을 검색해 나온 것을 튜토리얼 대로 따라해 보고 사람들 앞에서 실행해 보여주는 것과 다를 바 없다고 생각한다.


    물론 몇 년 전까지는 그런 것을 보여주는 것 자체가 신기한 때도 있었다. 하지만 지금은 아니다. 이 해킹은 어떻게 차단하는지 그리고 우리 게임에선 어떤 로직이 필요한지가 더 관건인 세상이다. 따라서 온라인 게임 해킹도 제대로 된 PT의 수순을 밟고 컨설팅으로서의 가치를 발휘하려면 단순히 해킹 소개에만 그치는 것이 아니라 그에 대한 대응 방법도 반드시 뒤를 이어 제시되어야 한다.

     

    증가 추세 1 - 가장 문제가 되는 치트 엔진

     

    게임 보안에 있어서의 로직을 설명하기에 앞서 현재 게임 핵의 동향을 먼저 살펴볼 필요가 있다. 메모리 핵이나 패킷 핵, 조준 핵(Aimbot) 등에 대한 분류는 이제 인터넷에 많이 공개되어 있으므로 그 분류를 다시 섭렵하는 것은 서론에서 얘기한 것처럼 시대에 뒤떨어진 브리핑이 될 것이다. 그러므로 그런 핵들이 요즘은 어떤 동향으로 흘러가고 있는지를 파악할 수 있도록 증가 추세와 감소 추세에 대해 살펴보자. 먼저 누구나 가장 많이 증가하고 있다고 입을 모을 만한 핵으로 치트 엔진(Cheat Engine)을 들 수 있다.

     


    <화면1> 게임 해킹 툴의 완성작이라고 할 수 있는 치트 엔진


    치트 엔진은 예전의 티서치(T-Search)라는 메모리 에디터의 업그레이드판이라고 생각할 수 있다(물론 개발자는 다르다). 치트 엔진의 개발자는 다크바이트(Dark Byte)라는 네덜란드 태생의 시스템 개발자이다. 치트 엔진은 그 훌륭한 기능도 기능이지만, 아무래도 가장 큰 심각성은 소스가 오픈되어 있다는 것에 있다. 초반에는 빌드한 바이너리만 공개했으나 게임 보안 솔루션과의 잦은 차단 패치로 인해 전 세계 해커들과 함께 우회 방법을 논의할 수 있도록 소스를 공개한 것으로 보인다. 그리고 그 효과는 컸다. 오픈소스 이후에 수백 종 이상의 게임 보안 솔루션 바이패스 버전이 등장했으며 게임 보안 솔루션의 패치 속도보다 더 빠른 업그레이드 속도를 보여주고 있다.


    필자가 게임 보안 솔루션 개발 업무를 하고 있었을 때 동료들과 하던 얘기가 있었다. “우리는 몇 명이서 막고 있는데 치트 엔진은 전 세계에서 뚫고 있다.” 그만큼 치트 엔진은 오픈소스라는 강점에 힘입어 여러 해커들의 아이디어가 모여 만들어진 게임 해킹의 완성작이라고 표현할 수 있다. 치트 엔진은 현재 온라인 게임 핵들 가운데 가장 심각하다. 이는 주로 로컬 메모리에 대한 의존도가 높은 캐주얼 게임에 많이 악용되고 있다.

     

    증가 추세 2 - 하드웨어 매크로

     

    다음으로 증가 추세가 뚜렷한 것은 하드웨어 매크로이다. 하드웨어 매크로 역시 게임 보안 솔루션들이 잦은 매크로 차단 패치를 수행하자 그것을 피하기 위해 매크로 아이디어를 USB 장치로 옮겨 개발한 핵이다. 본래 게임 해킹 문화 초창기에도 하드웨어 매크로는 존재했으나 가격이 비싸고, 또 소프트웨어에서 API로 구현한 것보다는 크리티컬한 움직임이 가능하지 않았던 탓에 대중적이지는 않았다.


    하지만 장비의 단가가 저렴해지고 하드웨어 매크로 개발에 대한 연구가 더욱 심층적으로 이뤄지면서 현재는 소프트웨어에서 구현한 매크로보다 더 훌륭하게 자동 사냥을 할 수 있도록 만들어지고 있다. 더욱 심각한 것은 이런 부류들이 인터넷 쇼핑몰에서 버젓이 정식 상품으로 판매되고 있다는 것이다. 이런 것들을 대응할 때, 매크로 세팅을 위해 애플리케이션이 있는 경우는 그 바이너리를 시그니처로 차단하기도 하지만, 심각한 경우에는 어떠한 프로그램도 없이 키보드나 마우스처럼 USB를 컴퓨터에 꽂기만 하면 바로 동작하는 것도 있다.

     

    MMORPG가 조금만 인기를 끌고 동시접속자수가 늘어나 아이템의 상품가치가 높아지면 그 게임에 대한 하드웨어 매크로는 거의 100% 등장한다고 생각하면 된다.


    <화면2> 하드웨어 매크로


    증가 추세 3 - 논 클라이언트

     

    논 클라이언트는 게임을 설치 및 실행하지 않고도 게임을 할 수 있는 핵을 말한다. <화면 3>과 같은 UI를 가진 프로그램을 하나 실행하면, 이 프로그램이 실제 게임 서버에 접속해 로그인도 하고 자동 사냥까지 해주는 등의 황당한 핵이다. 게임 클라이언트에 아무리 보안 코드를 집어넣어도 게임 클라이언트 자체가 가동되지 않으므로 무용지물이 되어 버리는, 게임 개발자들의 전의를 상실케 하는 핵이다. 심지어는 자동 사냥 방지 시스템으로 만든 GM의 질문에 자동으로 응답까지 해주기도 한다.


    논 클라이언트는 소스가 없는 상태에서 이론적으로는 절대 개발이 불가능하지만, 실제로는 게임 클라이언트의 바이너리를 뜯어 붙이는 코드 시뮬레이트 기법이나 네트워크 프로토콜을 분석해 패킷 구조를 실제 게임 클라이언트와 똑같이 구성해 주는 방식으로 개발되고 있다(혹은 서버 바이너리가 유출되면 100% 개발된다). 여담으로 나오는 얘기인데, 논 클라이언트는 현재 매우 많은 게임을 대상으로 등장해 있는 상황인데, 실제로 그 핵 툴을 다 입수해 보면 인터페이스는 거의 일치하는 편이다. 즉 논 클라이언트를 만드는 해킹 개발 집단은 동일 그룹이라는 소문이 있다.

     


    <화면3> 논 클라이언트


    증가 추세 4 - 게임 보안 솔루션 공격

     

    다음으로는 게임 보안 솔루션을 직접 공격하는 사례들이다. 예전에는 솔루션을 직접 공격하기보다는 솔루션이 적용된 몸체에서 솔루션을 호출하는 부분을 걷어내고 보안 모듈이 아예 뜨지 않게 하는 쪽으로 핵이 개발되거나 아니면 보안 솔루션이 감지하지 못하는 기법을 알아내어 해킹하는 쪽이 대세였다. 하지만 최근에는 웬만한 메모리 크래킹은 보안 모듈에서 거의 일임하고 있으므로 보안 홀이 거의 없어진 상황이라 아예 보안 모듈 자체를 무력화시키는 방법을 많이 사용하고 있다. 즉 분석의 대상이 게임 클라이언트에서 보안 솔루션으로 옮겨가고 있다. 게임 보안 솔루션은 주요 기술을 커널 단에서 사용하고 있으므로 이를 우회하기 위해 게임 핵도 커널 레벨로 내려가서 코드를 가동시키고 있다.

     


    <화면4> 게임 보안 솔루션 무력화


    감소하는 ring3의 해킹 툴


    다음으로는 감소 추세의 핵들을 알아 보자. 앞서 간단히 거론되었던 TSearch는 치트 엔진의 기세에 밀릴 뿐 아니라 게임 보안 솔루션에 거의 완벽히 차단되고 있기 때문에 요즈음 그 입지를 잃어가고 있다. 마찬가지로 TMK(Trainer Maker Kit)로 제작한 메모리 치팅 툴 역시 OpenProcess/WriteProcessMemory가 그 제작의 원리이므로 이 역시 현재에는 거의 쓰이고 있지 않다. 유저 레벨에서 send/recv를 후킹하는 원리인 WPE도 마찬가지이다.


    그리고 mouse_event()나 PostMessage() 등을 이용한 매크로도 감소 추세이다. 이처럼 과거에 API로 쉽게 구현할 수 있던 핵들은 모조리 감소되는 모습이다. 게임 보안 솔루션이 ring3에서는 나름 보호 대책이 되고 있다는 반증이기도 하다. 하지만 그 대신에 앞서 언급했다시피 해킹 툴은 대부분 커널 레벨로 내려가고 있으며 구현 수준 역시 높아지고 있다. 전체적으로 게임 해킹 툴이 유저 레벨에서 커널 레벨로 변환하는 시점이라 생각할 수 있다.

     

     


    <화면5> 요즘은 거의 사용되고 있지 않은 WPE

     

    게임 보안 솔루션과 게임 로직 보호는 서로 다른 영역

     

    지금까지 현재의 게임 핵 증감 추세를 통해 그 동향을 살펴봤다. 온라인 게임 해킹 보안이라고 하면 뭉뚱그려서 생각할 수도 있겠지만, 이쪽 분야는 엄연히 안티 크래킹(Anti-Cracking)의 성격이 기본적인 형태를 지니고 있다. 따라서 별도의 솔루션이 필요하며 그 솔루션의 역할을 현재는 보안회사에서 책임지고 있다.


    예를 들어 메모리를 보호하기 위해 API Hooking을 한다던가, ring0에서의 컨트롤을 위해 커널 드라이버를 설치한다던가 하는 것이 그것이다. 게임 회사에서는 이러한 것에 관여할 필요가 없다. 굳이 이해할 필요도 없고 필요할 때마다 보안회사의 라이브러리를 이용한다던가 엔진을 구매해 사용하면 된다(게임 개발하기에도 바쁜데 이러한 것까지 일일이 신경 써야 한다면 게임 개발자들은 영원히 잠을 못 잘지도 모른다).


    대신에 게임을 개발하는 입장에서는 보안 솔루션으로 처리해 주지 못하는 내부 보안 로직이 필수적이다. 예를 들어 패킷을 암호화한다던가 시퀀스 넘버링을 체크하는 것이다. 그리고 메모리 몇 바이트를 고쳐서 캐릭터의 능력치가 엄청나게 성장하는 취약점 등을 보강하기 위한 노력도 이것에 포함된다. 혹은 매크로 차단을 위해 게임 내부 기획 이벤트 등도 거론될 수 있다. 따라서 두 영역은 엄연히 서로 다른 영역이며 보안 처리도 대응 방법도 완전히 별개의 내용이다. 괜히 치트 엔진을 차단하기 위해 치트 엔진의 시그니처를 게임 바이너리에 넣어 놓고 주기적 스캔을 하는 루틴을 개발하는 게임회사도 있곤 한데, 그런 것은 무의미한 작업이다(치트 엔진이 스텔스 모드를 사용하면 유저 레벨에서는 치트 엔진의 존재를 감지할 수도 없기 때문).


    따라서 그러한 것들은 그냥 게임 보안 솔루션에 맡겨버리면 된다. 그리고 내부 로직 강화에 더욱 신경 써야 한다. 요약하면 게임 보안 솔루션은 시스템 프로그래밍, 안티 리버싱 프로그래밍의 성격이라 말할 수 있고, 게임 로직 보호는 안정된 네트워크 프로그래밍이나 메모리 사용의 지능화 정도로 구분할 수 있다. 게임 해킹이 해킹 트렌드에 있어 어엿한 하나의 해킹 파트가 된 요즈음은 하나의 영역만 설명하기에도 내용이 아주 방대해진 편이다. 따라서 이 글에서는 게임 보안 솔루션에 대한 내용은 생략하고 게임 개발자를 위한 내부 로직 보강을 위주로 언급하도록 하겠다.

     

    무결성 검사

     

    게임 EXE 바이너리와 DLL 파일은 해커들의 주공격 대상이다. 가장 많이 당하는 해킹은 역시 EXE나 DLL의 코드 섹션을 변조하는 행위이다. 따라서 개발자가 게임의 질서를 위해 범위를 정해 놓은 수치를 해커들이 마음대로 변경해 게임을 엉망으로 만드는 행위를 방지해야 한다. 여기서 쓰이는 것이 CRC 검사나 체크섬 등의 무결성 검사이다. 코드 변조 그 자체를 막을 수는 없으니, 대신에 변조하면 감지하겠다는 개념이다. 다만 무결성 검사는 간단한 체크섬으로 구현하거나 CRC32 알고리즘을 이용하는데, 웬만한 무결성 검사는 해커들에게 우회가 가능하다. 예를 들어 <화면 6>과 같은 코드가 있다.

     

     


    <화면6> CRC 검사 루틴


    0x4022A9 번지를 보면 call 문이 보이고 push 문이 위에 2개 등장하므로 인자 2개인 함수를 사용한다는 것을 알 수 있다. 그리고 그 함수가 끝난 뒤 ebx의 번지에 있는 값을 eax에 넣는데 이것을 sprintf로 기록하고 있다. 저 call 문이 CRC 값을 구하는 함수이고 결과 값은 포인터로 전달되며 그 번지가 ebx에 들어오는 것이다. 즉 그 루틴은 <리스트 1>과 같다.

     

    <리스트 1> CRC 검사 루틴의 디컴파일

    DWORD dwCrc = 0;
    GetCRC(szFileName, &dwCRC);
    sprintf(szTemp, "%X", dwCRC);

     


    따라서 dwCRC에 해당하는 값을 조작하면 <리스트 1>의 CRC 검사는 무력화된다. ebx나 eax를 조작할 수도 있고, 저 루틴 자체를 점프시켜 뛰어넘을 수도 있다. 해커들은 이러한 방법들을 즐겨 이용한다.


    이 외에도 CRC 연산 이후에 나온 값을 오리지널 값으로 동일하게 맞춰 준다던지, CRC 검사 영역을 코드 섹션이 아닌 다른 영역으로 속여서 그곳에는 정상 EXE를 덤프 떠 놓은 파일을 올려놓고 그 엉뚱한 영역을 검사하게 만드는 우회 기법도 있다. 게임 클라이언트의 코드를 변조하면 무결성 검사에 걸러져 버리므로, 먼저 무결성 검사 루틴부터 무력화시키는 작업을 하는 것이다. 게임 로직에서 직접 무결성 검사를 하는 것도 그렇고, 게임 보안 솔루션에서 해주는 것도 비슷하게 우회된다. 따라서 무결성 검사 루틴 자체는 쉽게 우회되지 않도록 강력하게 코딩해야 하며 많은 고민이 필요한 부분이다.


    따라서 정교한 무결성 검사 기능을 구현하는 방법은 사실 한두 번의 노력으로 가능한 것이 아니다. 항상 새로운 해킹 툴이 등장하면 코드 섹션을 변조해야만 이러한 행위가 가능한지부터 소스를 통해 체크해 보고 무결성 검사 기능이 무력화 당했는지도 확인해 보아야 한다. 그리고 우회 당한 것이 맞다면 어떤 식으로 바이패스된 건지 그 구체적인 동작부터 체크해 봐야 대응 코드를 작성하는 데 더 큰 도움이 된다. 그래서 게임 회사에서는 게임 개발자가 직접 소스를 비교하며 리버스 엔지니어링을 하던지 리버싱 가능한 인력이 있는 내부 보안팀을 통해 분석하기도 한다.

     

    패킷 암호화/시퀀스

     

    더 이상 강조하면 이제 지겹다고 생각할 만큼 필수적인 요건이다. 예전에는 암호화를 하지 않은 패킷이 네트워크 상에 그대로 전송되는 경우가 많았지만, 몹 공격 패킷, 캐릭터 이동 패킷, 퀘스트 완료 패킷 등이 분석되어 이것이 자동 사냥 등으로 악용되는 경우가 빈번하게 발생했다. 따라서 패킷 암호화와 시퀀스 넘버링은 이제 개발자들 사이에서 필수적인 부분이 되었다. 암호화는 하면서도 시퀀스 넘버링 체크는 하지 않는 게임도 의외로 많은데, 시퀀스 체크를 생략하면 이는 패킷 리플라이 공격으로 이어진다.


    예를 들어 한방 핵이나 퀘스트 완료 해킹 같은 것이 그것이다. 공격 패킷을 캡처링해서 계속 서버로 전송함으로써 한 번에 수십 수백 번 이상 공격한 것과 같은 효과를 낼 수 있다. 또 퀘스트가 완료되면 게임 서버로 퀘스트 완료 패킷이 날아가고, 게임 서버에서는 경험치 증가와 렙업 등을 처리하는데, 만약 퀘스트 완료 패킷도 캡처링하여 재사용이 가능하다면 해커들은 퀘스트 완료 패킷을 계속 서버로 전송할 것이다. 그러면 퀘스트를 완료하지 않았음에도 불구하고 계속해서 경험치가 늘어나게 된다(이 부분은 다음 파트인 서버 중심 설계 쪽에서 부연 설명한다).


    따라서 통신을 할 때마다 서버와 클라이언트에서 각각 시퀀스 넘버를 부여해 서버에서 받은 패킷이 방금 클라이언트에서 만들어진 패킷인지 아니면 그전에 캡처링해 둔 예전 패킷이었는지 반드시 체크해야 한다. 하지만 시퀀스도 역시 평범한 숫자로 기록하면 무력화가 가능하므로 CRC 체크섬도 반드시 병행해야 한다.

    서버 중심 설계

     

    게임 설계를 할 때 반드시 숙지해야 할 사항은 ‘클라이언트는 절대로 믿지 않는다’이다. 클라이언트에서 전달된 패킷은 반드시 서버에서 정밀한 검증을 통해 다시 한 번 체크해 주어야 한다. 초창기에 네트워크 설계를 할 때는 서버 개발자와 클라이언트 개발자는 서로 약속된 패킷 이외의 값은 오지 않을 거라 생각하고 개발을 시작했다. 하지만 알 수 없는 패킷이 날아와 데이터를 마구 망가뜨렸고 급기야는 서버가 다운되는 현상까지 발생했다.


    서버 다운 외에도 심각한 문제가 있는데, 클라이언트가 현재 어떤 기술이나 아이템을 사용할 수 없는 조건인데도 그런 기술을 사용할 수 있다는 것이다. 대표적인 것이 스킬 핵이다. 예를 들어 ‘아리랑’이라는 스킬이 있고 이 스킬은 마법사만이 사용할 수 있으며 렙이 20 이상이어야 한다는 조건이 있다고 가정하자.


    그런데 렙이 20도 되지 않는 전사 캐릭터가 아리랑 스킬을 사용한다는 게임 해킹의 보고를 받게 된 것이다. 이유가 무엇일까? 게임 개발자가 이런 데이터의 범위를 검증하는 코드를 클라이언트에 남겨둔 치명적인 실수를 했기 때문이다. 해커들은 클라이언트의 그 코드를 점프해 해당 스킬을 날리거나, 아니면 미리 캡처해 둔 스킬 패킷을 계속 서버로 전송하는 방식을 이용한 것이다.


    이런 해킹을 막기 위해 서버에서의 현재 클라이언트 각종 상황에 대한 점검 조건은 필수적이다. 서버는 아리랑 스킬을 받았을 때 클라이언트의 각종 조건을 면밀하게 검토해 본 후 패킷이 날아올 수 없는 상황에서 전송된 것이라면 이를 거부해야 한다. 이것이 바로 게임 서버 개발의 기본이다. 물론 쉽지 않은 작업이며 캐주얼 게임 은 경우는 퍼포먼스를 위해 어쩔 수 없이 클라이언트에 이런 코드를 많이 넣을 수밖에 없는 것이 현실이다. 하지만 그래도 해킹을 최소화시키기 위해 중요한 값들이나 주요 검증 코드는 반드시 서버에서 수행해 주어야 하며, 특히 MMORPG 같은 경우는 그런 자세가 더욱 필수적이다. 이런 규칙을 지키지 않고 게임 개발을 하게 되면 게임 핵이 많이 발생하게 된다.


    그리고 치트 엔진이 범람하는 게임들은 대부분 클라이언트에 주요 코드가 많이 부가되어 이런 취약점이 있는 게임이라고 보면 된다. 서버 사이드로 개발되면 치트 엔진 같은 것을 사용해도 내 PC에서 보이는 값만 바뀔 뿐, 서버가 가지고 있는 실 데이터는 그대로이므로 해킹 자체가 무의미하다. 예를 들어 리니지 같은 게임은 철저히 서버 사이드 단에서 개발되었기 때문에 웬만한 데이터는 다 서버가 가지고 있고 클라이언트는 거의 UI만 존재하는 편이다. 즉 몸뚱아리만 덩그러니 있는 상태로 본다 해도 아주 틀린 얘기는 아니다. 따라서 리니지에는 치트 엔진 같은 메모리 해킹 툴이 거의 사용되지 않는 편이다.

     


    <화면7>암호화되지 않은 패킷이 해커에게 분석되어 개발된 핵


    물론 이렇게 개발한다고 해서 모든 해킹이 다 근절되는 것은 아니지만, 치트 엔진이나 메모리 번지로 장난치는 해킹 행위는 상당수 예방할 수 있다. 따라서 클라이언트를 믿지 말고 게임 서버에서 각종 데이터를 검증할 수 있는 시스템이 되어야 하며, 게임 중에는 언제나 임의의 패킷이 올 수 있다는 가정에 대비해야 한다.

     

    파일 암호화

     

    요즈음은 리버스 엔지니어링 기술이 무척 발달해서 EXE나 DLL 파일의 경우 파일 암호화(프로텍팅/패킹)를 하지 않으면 치명적이다. 상당수의 게임 개발자들이 이 부분을 간과하며 그냥 넘어가는 경우가 많다. 물론 파일 암호화를 통해 모든 해킹을 예방할 수 있는 것은 아니지만 이는 기본적으로 처리해야 될 일차적인 과제라고 생각하며 수행하기를 권한다. 개발자들이 생각하기에 바이너리 암호화 툴을 이용해 패킹을 해도 어차피 메모리에 풀리면 다 보이니 마찬가지라고 생각하며 패킹하지 않는 경우도 있다, 물론 메모리 상에서 풀리는 것은 맞다. 하지만 메모리 상에서 변조하는 것 외에 파일을 미리 고쳐 놓고 실행하는 해킹도 매우 많으므로 그 부분에 대응하기 위한 파일 암호화는 필수적이다.


    파일 암호화 작업을 거치면 해당 파일을 완벽히 언패킹하지 않는 한, 파일 상에서 고친 후 실행하는 해킹은 100% 막을 수 있다. S급 리버스 엔지니어들에게는 빌드 상태 그대로의 깔끔한 바이너리는 소스 수준으로 컨버팅할 수 있는 능력이 있다. 따라서 빌드 상태 그대로 배포해 중국 등에 나가게 되면 S급 해커들에게는 소스를 준 것과 별반 차이가 없어진다. 믿겨지지 않겠지만 사실이다. 당해본 사람들은 알고 있다.

     


    <화면8> 현존 최강의 프로텍터로 알려져 있는 Themida

     

    매크로 방지

     

    매크로는 게임 개발사가 대응하기에 가장 어려운 해킹이 아닐까 싶다. 소프트웨어적으로 구현하는 매크로는 개발 로직 자체가 검증 루틴이나 패킷 처리 지침으로 해결하는 것이 불가능하다. 따라서 기본적으로는 게임 보안 솔루션에 의지하되, 게임 로직으로는 게임 내부의 매크로 동향을 파악해 기획적으로 대응 방법을 강구하는 방향으로 진행하는 편이 일반적이다. 대표적인 것이 오토플레이 신고제이다. 사실 매크로를 사용하고 있는 유저들은 조금만 살펴봐도 티가 크게 나는 편이다. 따라서 이 방법은 게임 내부에서 유저들이 직접 신고하는 시스템을 구축하는 형태이다. 현재 여러 게임에서 사용하고 있으며 나름 효과가 있는 것으로 알려져 있다.

     


    <화면9> 클린패드

     

    그리고 매크로가 사람 없이 계속 플레이된다는 점에 착안해, 무언가 수동으로 입력받는 화면을 내보이는 시스템이다. 던전 앤 파이터의 클린패드 같은 것이 그 좋은 예이다. 그 외에도 매크로 차단 기획을 위한 많은 아이디어가 있다. 예를 들어 사냥터 중간에 절대 건드리면 안 되는 몹을 하나씩 배치한다든가(매크로 이용자는 그 몹을 건드리게 되어 죽음에 이르게 된다), 오토 이용자로 100% 확정될 경우에 가지고 있는 아이템을 한 번에 모두 떨어뜨리도록 만들어 버리는 아이디어도 생각할 수 있다.

     

    스피드 핵

     

    스피드 핵은 현존하는 대부분의 형태가 타이머 API를 후킹한다는 점에 착안해 게임 보안 솔루션이 그 부분을 체크함으로써 나름 효과적으로 방어가 이뤄지고 있다. 하지만 게임 보안 솔루션을 통째로 걷어내는 해킹도 많이 존재하고 있으므로 근원적으로는 게임 서버와 클라이언트의 시간 동기화로 걷어내야 하는 작업이 필수적이다. 게임 서버는 10초밖에 시간이 흐르지 않았는데 클라이언트에선 1분 이상 흘렀다면 그 경우는 스피드핵 사용 말고는 그 이유가 없으므로 과감히 클라이언트의 접속을 해제시켜야 한다.

     

    문자열 암호화

     

    문자열은 상수 영역에 그대로 남기 때문에 메모리 상에서 쉽게 보이게 된다. 그리고 디버거나 디스어셈블러로 뜯어보았을 때 문자열이 사용되는 위치까지 친절히 알려주므로 이것이 곧 해킹의 일차적인 근간이 되곤 한다. 따라서 중요 문자열은 반드시 암호화하는 작업이 필요하다. 대부분의 게임 개발자들은 코드 가독성이 떨어지고 일일이 다 암호화하기가 귀찮기 때문에 이 부분을 간과하고 그냥 넘어가곤 하는데 문자열 암호화로 인한 안티 리버싱 효과는 매우 크다는 것을 염두에 둬야 한다.

     

    DLL의 익스포트 함수 보호

     

    DLL의 익스포트 함수는 외부에서 읽어 들여야 하기 때문에 역시 스트링이 그대로 노출되어 있다. 예를 들어 어떤 게임에서는 AttackRange()라는 함수명이 그대로 노출되어 있었고 해커들은 그 함수를 공격해 공격의 범위를 마구 수정하곤 했다. 이 기능은 비주얼 스튜디오의 DEF 기능을 이용하면 쉽고 간단하게 암호화할 수 있다. 방법은 다음과 같다. <화면 11>처럼 소스 파일과 동일한 이름으로 .def 파일을 작성한다. EXPORT에 함수 이름을 넣고 @1 @2처럼 함수 개수대로 순번을 붙여준다. 그리고 빌드한 후 디펜던시 워커로 확인해 보면 DLL 이름이 N/A로 표기된 것을 알 수 있다. 이름이 외부에 공개되지 않으므로 해커들이 타깃을 잡기가 어렵게 된다.

     


    <화면10> 스트링은 해커들에게 분석을 위한 소중한 힌트가 된다

     

    그렇다면 이름이 보이지 않으니 GetProcAddress()를 할 때에는 어떻게 해야 하는지 그 방법이 궁금해진다. 그것 역시 간단하다. MAKELONG() 매크로 함수를 이용해 Ordinal로 얻어오면 끝이다. <리스트 2>를 살펴보면 충분히 이해할 수 있을 것이다.

     

    게임 보안은 한두 번의 보안 조치로 해결되지 않는다

     

    이곳에 언급된 내용은 하나의 가이드라인일 뿐이지, 게임 해킹 대응의 전부가 아니다. 누락된 해킹 기법이 여전히 많다. 지면적인 제약으로 거론된 내용의 풀 코드를 제공하지 못한 아쉬움도 있고 개론적인 설명에 그친 내용도 있었다.

     

    <리스트 2> MAKELONG() 이용하기

    typedef DWORD (*INITMYENGINE);
    typedef DWORD (*RUNENGINE);
    INITMYENGINE fnInitMyEngine;
    RUNENGINE fnRunEngine;

    HINSTANCE hDllsample = LoadLibrary(szDllPath);
    if (hDllSample)
    {
    fnInitMyEngine = (INITMYENGINE)GetProcAddress(hDllSample, (LPCSTR)MAKELONG(1, 0));
    fnRunEngine = (RUNENGINE)GetProcAddress(hDllSample, (LPCSTR)MAKELONG(2, 0));
    }

     

    또한 게임 보안 솔루션의 영역은 거론하지 않았다. 게임 보안 영역은 시스템 프로그래밍과 네트워크 프로그래밍, 그리고 암호화 프로그래밍, 게임 프로그래밍, 리버스 엔지니어링 등 IT 기술의 각종 분야가 종합적으로 축약된 분야라고 생각된다.

     


    <화면11> DEF 파일


    따라서 현재의 그 방대한 기법에도 모자라 아직도 새로이 공개되고 있는 해킹 툴의 알고리즘을 보면 혀를 내두를 때가 많다. 따라서 한두 번의 컨설팅으로 모든 해킹이 다 근절된다는 생각을 버리고 사내의 보안 담당자와 협의해 해킹 툴을 끊임없이 입수 및 분석해 새로운 대응 방법을 생각해 나가야 한다. 또한 게임 보안 솔루션의 사용도 필수적이다. 전문 보안 업체와의 협업도 반드시 필요한 부분이라고 생각한다.

     

     

    Interview


    “보안 솔루션 도입으로 정보보호 습관화해야”

    시만텍코리아 시스템 엔지니어링 본부 윤광택 팀장

     

    웹2.0 시대에 가장 두드러지는 보안 위협의 형태는 무엇인가?
    이용자 참여를 강조하는 웹2.0 서비스들이 대부분 이용자들 간의 신뢰를 바탕으로 하고 있는 만큼 최근의 보안 위협은 이런 신뢰에 허를 찌르는 형태로 만들어지고 있다. 종전의 보안 위협이 이용자를 따라다니는 형태였다면 현재의 보안 위협은 사용자들이 안심하고 방문하는 웹서비스에 ‘그물'을 쳐놓고 이용자들의 개인 정보를 노리는 형태로 만들어지고 있다. 다시 말해 보안 위협의 형태는 이전과 크게 다르지 않지만 보안 위협이 경유하거나 감염시키는 경로에 큰 변화가 생긴 것이다.

     

    내부자를 대상으로 하는 정보 보호의 필요성이 나날이 커지고 있는데….
    현실적으로 기업이 가진 모든 정보를 보호하는 것은 불가능하다. 따라서 일상적이면서 보편적인 데이터는 보안 제품의 관리에서 제외하고, 기밀정보로 등록된 데이터만 집중적으로 관리하는 메커니즘이 내부적인 정보 보호를 실현하는 가장 실질적인 수단으로 평가되고 있다. 실제로 핵심 데이터의 열람이나 이동을 철저히 감시함으로써 혹 있을지 모르는 대부분의 유출 시도를 막을 수 있다. 또한 이런 솔루션의 도움을 받아 구성원의 보안 의식을 점차 트레이닝하면 머지않아 보안의 습관화가 가능하다는 점에 우리는 주목해야 한다.

     

    익스플로러 새 버전과 구글의 ‘크롬' 출시 등으로 보안적 대응이 요구되는 웹 브라우저의 수가 늘었는데….
    새로운 웹 브라우저가 이용자의 호응을 얻어 점유율을 높이면 그 브라우저를 타깃으로 하는 위협이 증가할 것이므로 보안 벤더의 입장에서는 대비해야 할 사항들이 크게 늘어난다. 하지만 어떤 웹 브라우저를 사용하든 유저들이 느끼는 보안 위협의 정도는 비슷할 것이다. 웹 브라우저의 종류가 늘어나더라도 대부분의 경우 하나의 브라우저를 주력으로 사용하게 될 것이므로 선택하지 않은 브라우저상의 보안 위협은 이용자와 무관하기 때문이다.

     

    Mpack이나 피싱 툴킷과 같은 보안 위협 툴킷의 동향은 어떠한가?
    바이러스와 같은 하나의 보안 위협을 탐지하면 수일 내에 동일한 패턴을 가진 새로운 변종 바이러스가 여러 개 출현하곤 한다. 그런 보안 위협을 분석해 보면 똑같은 디렉토리 구조와 개더링(gathering) 형태를 가져 특정한 툴킷에 의해 생산된 것임을 추측할 수 있다. 경우에 따라서는 우리나라에서만 활동하는 변종도 생성되고, 심지어는 특정 기업만을 타깃으로 만들어진 변종도 탐지되고 있다. 이런 툴킷으로 더 빠르고 더 최적화된 위협을 만들어 낼 수 있다는 점에서 툴킷은 위협적이다.

     

     

    “대규모 정보 유출 사고로 DRM 수요 증가세”

    파수닷컴 안혜연 부사장

     

    기업용 문서보안(DRM) 솔루션의 개념과 원리를 설명한다면?
    DRM(Digital Rights Management)은 콘텐츠나 파일의 사용을 통제하는 기술로 허가된 사용자가 허용된 권한 범위 내에서만 콘텐츠를 사용할 수 있게 하는 기술이다. 일반적으로 비즈니스 문서를 보안하는 DRM 솔루션은 Enterprise DRM을 의미하며, 이는 전자문서를 암호화해 불법 유출을 방지함으로써 기업의 핵심 정보자산의 지속적인 보안을 제공한다. 메커니즘적으로 볼 때 DRM은 디지털 콘텐츠를 사용하기 위해 반드시 저작권자가 배포하는 라이선스를 발급받아야 한다는 개념을 기술적으로 보장하는 시스템이다.

     

    문서보안에 대한 우리 기업들의 인식은 어느 정도 수준인가?
    고객 개인정보 유출로 곤욕을 치르고 있는 기업들이 증가하면서, 과거 해킹과 바이러스 차단에만 치중하던 보안 시장이 데이터 및 콘텐츠와 같은 핵심 정보 유출 차단에 역점을 두는 방향으로 보안의 중심이 변화하고 있다. 지난 9월 16일 한국소프트웨어진흥원이 내놓은 ‘기술 및 정보유출 증가로 기업 DRM 시장 급성장'이라는 보고서에 보면 내부자에 의한 개인정보 유출 및 산업스파이에 의한 산업기술 유출 사례가 증가하면서 DRM-Free 추세로 다소 위축되었던 DRM 시장이 다시금 뚜렷한 성장세를 보이고 있다고 분석했다.

     

    웹이나 시스템 인프라의 발전이 DRM에 미치는 영향은 무엇인가?
    인터넷 응용프로그램 기술이 발전하고 웹서비스의 형태가 다양해짐에 따라, 참여와 공유 개념으로 진화하고 있는 개방형 웹 환경은 불법 정보 유출의 대표적인 통로가 되고 있다. 이에 권한에 따라 사용자의 웹 브라우저 사용이 제한되며 웹 콘텐츠의 복사, 저장, 인쇄 등의 기능을 제어함으로써 기업의 소중한 정보를 보호하는 DRM 솔루션 시장 역시 동반 상승세를 이어가는 추세이다.

     

    앞으로의 내부정보 유출 방지 시장을 전망한다면?
    현재까지 국내 DRM 시장은 2007년 기준으로 6,429억 원을 형성하고 있으며 2008년에는 1조4,614억 원 규모로 성장할 것으로 추정되고 있다. 이는 연간 성장률이 127.3%를 상회하는 추세로 기업용 문서보안 솔루션인 Enterprise DRM 시장과 멀티미디어 콘텐츠 저작권 보호 솔루션 시장인 Consumer DRM 시장을 합친 결과이다. DRM을 중심으로 한 내부정보 유출방지 시장은 FTA와 관련해 저작권보호법, 산업기술유출방지법, 개인정보보호법 등이 강화되고 정비되는 추세에 따라 관련 수요도 함께 증가할 것으로 전망된다. 향후 DRM 시장은 보안 환경 진단에서부터 솔루션 구축, 유지보수서비스까지 통합되는 통합 보안시스템으로의 연동이 주요 이슈가 될 것으로 예상되며 이미 성숙 단계에 진입한 대기업 시장을 넘어 중견중소기업 시장에까지 DRM에 대한 수요가 증가할 것으로 예상된다.


    전도영 기자 mir@imaso.co.kr

     

     

    필자소개

     

    강병탁 window31@empal.com , www.window31.com

     

    온라인 게임 보안 솔루션인 nProtect GameGuard 엔진 개발을 다년간 해왔다. Anti-Debugging / Anti-Cracking / 리버스 엔지니어링 / 시스템 프로그래밍 등이 주 관심사이다. 현재는 넥슨에서 보안 프로그래밍과 악성코드 분석, 게임 해킹 방지 등에 대해 연구하고 있다.

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG 보안
     
     
    리눅스 랜카드 장치 모델명 확인
    리눅스 이더넷 모델명, 벤더 확인
    리눅스 NIC 카드 모델 확인
    • VM이 아니고, 인터페이스가 PCI인 경우에만 가능

    목차

      [숨기기

    모델명 확인

    명령어
    /sbin/lspci | grep -i Ethernet
    
    실행예시 (Dedi)
    [root@localhost ~]# /sbin/lspci | grep -i Ethernet
    04:00.0 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    04:00.1 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    04:00.2 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    04:00.3 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    0b:00.0 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    0b:00.1 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    11:00.0 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    11:00.1 Ethernet controller: NetXen Incorporated NX3031 Multifunction 1/10-Gigabit Server Adapter (rev 42)
    71:00.0 Ethernet controller: Intel Corporation 82571EB Gigabit Ethernet Controller (rev 06)
    71:00.1 Ethernet controller: Intel Corporation 82571EB Gigabit Ethernet Controller (rev 06)
    7a:00.0 Ethernet controller: Intel Corporation 82571EB Gigabit Ethernet Controller (rev 06)
    7a:00.1 Ethernet controller: Intel Corporation 82571EB Gigabit Ethernet Controller (rev 06)
    
    → PCI 이더넷 장치가 12개 있다.
    실행예시 (VM)
    [root@localhost ~]# /sbin/lspci
    
    → 가상 머신에서는 lspci 결과가 나오지 않는다.

    기타

    벤더 및 서브시스템 모델명 확인

    보다 자세한 정보를 확인하려면 -m 옵션을 주면 된다.

    명령어
    /sbin/lspci -m | grep -i Ethernet
    
    실행예시
    [root@localhost ~]# /sbin/lspci -m | grep -i Ethernet
    04:00.0 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC375i Integrated Quad Port Multifunction Gigabit Server Adapter"
    04:00.1 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC375i Integrated Quad Port Multifunction Gigabit Server Adapter"
    04:00.2 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC375i Integrated Quad Port Multifunction Gigabit Server Adapter"
    04:00.3 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC375i Integrated Quad Port Multifunction Gigabit Server Adapter"
    0b:00.0 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC522SFP Dual Port 10GbE Server Adapter"
    0b:00.1 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC522SFP Dual Port 10GbE Server Adapter"
    11:00.0 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC522SFP Dual Port 10GbE Server Adapter"
    11:00.1 "Ethernet controller" "NetXen Incorporated" "NX3031 Multifunction 1/10-Gigabit Server Adapter" -r42 "Hewlett-Packard Company" "NC522SFP Dual Port 10GbE Server Adapter"
    71:00.0 "Ethernet controller" "Intel Corporation" "82571EB Gigabit Ethernet Controller" -r06 "Hewlett-Packard Company" "NC360T PCI Express Dual Port Gigabit Server Adapter"
    71:00.1 "Ethernet controller" "Intel Corporation" "82571EB Gigabit Ethernet Controller" -r06 "Hewlett-Packard Company" "NC360T PCI Express Dual Port Gigabit Server Adapter"
    7a:00.0 "Ethernet controller" "Intel Corporation" "82571EB Gigabit Ethernet Controller" -r06 "Hewlett-Packard Company" "NC360T PCI Express Dual Port Gigabit Server Adapter"
    7a:00.1 "Ethernet controller" "Intel Corporation" "82571EB Gigabit Ethernet Controller" -r06 "Hewlett-Packard Company" "NC360T PCI Express Dual Port Gigabit Server Adapter"
    

    벤더 목록 뽑기

    명령어
    /sbin/lspci -m | grep -i Ethernet | while read line
    do
            echo $line | awk '{split($0,a,"\""); print a[8]}'
    done
    
    실행예시
    [root@localhost ~]# /sbin/lspci -m | grep -i Ethernet | while read line; do echo $line | awk '{split($0,a,"\"); print a[8]}'; done
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    Hewlett-Packard Company
    

    서브시스템명 뽑기

    명령어
    /sbin/lspci -m | grep -i Ethernet | while read line
    do
            echo $line | awk '{split($0,a,"\""); print a[10]}'
    done
    
    실행예시
    [root@localhost ~]# /sbin/lspci -m | grep -i Ethernet | while read line; do echo $line | awk '{split(0,a,"\""); print a[10]}'; done
    NC375i Integrated Quad Port Multifunction Gigabit Server Adapter
    NC375i Integrated Quad Port Multifunction Gigabit Server Adapter
    NC375i Integrated Quad Port Multifunction Gigabit Server Adapter
    NC375i Integrated Quad Port Multifunction Gigabit Server Adapter
    NC522SFP Dual Port 10GbE Server Adapter
    NC522SFP Dual Port 10GbE Server Adapter
    NC522SFP Dual Port 10GbE Server Adapter
    NC522SFP Dual Port 10GbE Server Adapter
    NC360T PCI Express Dual Port Gigabit Server Adapter
    NC360T PCI Express Dual Port Gigabit Server Adapter
    NC360T PCI Express Dual Port Gigabit Server Adapter
    NC360T PCI Express Dual Port Gigabit Server Adapter
    

    같이 보기


     

     

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG 리눅스

    dmidecode 를 루트에서 실행하면 장비의 스팩을 출력해 준다.


    #dmidecode






    자세한 사용법은 http://bwhite.tistory.com/entry/Linux-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0-dmidecode-lshw



    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG 리눅스

    출처 : http://mrdingku.tistory.com/41


    앞의 글(SQL Server Management Studio를 사용해서 연결된 서버를 등록하기)을 따라 연결된 서버를 등록했다


    이제 SQL Server에서 연결된 서버의 Table을 OpenQuery를 이용하여 조회하고 등록하고 수정하고 삭제해보겠다.


    ## Select

    1
    SELECT * FROM OPENQUERY(L_TESTDB, 'SELECT * FROM LinkedServerTestTable');


    ## Insert

    1
    INSERT OPENQUERY(L_TESTDB, 'SELECT NO, NAME FROM LinkedServerTestTable ') VALUES (1, '홍길동');


    ## Update

    1
    UPDATE OPENQUERY(L_TESTDB, 'SELECT NAME FROM LinkedServerTestTable WHERE NO = 1') SET NAME = '임꺽정';


    ## Delete

    1
    DELETE FROM OPENQUERY(L_TESTDB, 'SELECT NO FROM LinkedServerTestTable WHERE NO = 1');


    어렵지않다. 찾으면 쉽게 알 수 있다.

    공부하자~~!!!!


    ########## 추가 ##########

    프로시저에서 openquery를 이용하여 update하는데, where절에 NO = @P_NO 파라미터를 넣었더니 바로 에러가 나더라는.....

    그래서 확인해보니 openquery에는 string 만들어가야 한다는 사실을 알게 되었다.. 그놈 참... 까다롭네그려... ㅡㅡ;;;


    그래서


    1
    2
    3
    4
    5
    @_QUERY = '
    UPDATE OPENQUERY(L_TESTDB, 'SELECT NAME FROM LinkedServerTestTable WHERE NO = '+@SMS_SEQ+'')
          SET NAME = '''+@NAME+'' '
     
    Execute sp_executesql  @_QUERY, @_PARAMS


    주의사항 : 파라미터 부분에 작은 따옴표 " ' "를 넣을때 특수문자여서 하나더 넣어야한다는거 다들 아시지요?? 

    @SMS_SEQ가 스트링일 경우, '''''+@SMS_SEQ+''''' 요렇게...

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG MS-SQL

    일본에서 일 하고 있을때...

    2011년 7월경 일본에서는 디지털 방송으로 수신이 전환되었습니다.

     

    그래서 당시 아날로그 TV를 버리고 LED32 인치 TV를 구매 했었습니다...

    (3.4만엔 줬던거 같네요.. 당시 환율은 1400~1500원대;;)

    하지만... 2011년 11월 일하던 겜회사 사정상 구조조정? 중 외국인 노동자를 잘랐고...

    그렇게 낙담해서 한국으로 귀국하려고 준비 하면서... 산지 얼마 안된 tv 처분하려 하니 3개월(8월 초~11월)만 썻는데도,
    1만엔 밖에 안준다고 하더군요... 도둑넘들... ㅜㅜ

     

    그래서 그냥 가지고 왔습니다. 32인치 LED TV인데 이 무슨 하면서...

    하지만, 일본 수신에 맞춰진 내수용 TV니... 한국 방송이 나올리는 없고...
    (정확히 나오기는 합니다만.. 지저분하게 나오네요)

    셋톱박스 사서 그럭저럭 한국 방송을 보고 있었습니다.

    이런 일본 TV에서 리모컨 옆에 있는 셋톱박스(외장형 tv수신기?)힘을 빌려

     

    이런 방송이 나옵니다 (공중파 HD방송만 잘... 나머지는...그럭 저럭 화질이네요...)

     

    허나, 더 이상 이런 TV로는 한국 방송보기 힘들어서 TV를 살까 하다가

    이 TV가 위성 수신 안테나만 달면 일본 방송을 볼 수 있는걸 알았습니다.

    (TV 밑에 빨간게 B-CAS 라는 일본 방송 수신시 컨텐츠 제어(불법복제 방지등..) 하는 카드.... 뭐 그런거 있습니다)

     

    점점 잊어가는 일본어 능력을 어떻게든 하려고(언어란게.. 안하면 잊어먹는 거라..), 위성 안테나를 설치해 볼까 하고 있었습니다...

     

    허나, 위성 안테나를 조사하던 도중 충격적인 사실이... 제가 살고 있는 인천은 수신지역에서 벗어나 있더군요... ㅠㅠ

    (회사가 있는 판교는 수신지역 안이구요.. 이 무슨......)

    <참고 페이지>

    http://www.satbeams.com/footprints

     찍은 지역은 회사가 있는 판교 입니다...75cm위성 안테나로 잡힌다네요... 허나.. 인천은 수신지역에서 벗어나 있죠... ㅠㅠ

     

     하지만, 위 데이터를 이용해서 잘 계산해 보면, 인천에서 얼추 90cm ~ 120cm 정도면 수신이 가능할 것 같더군요...

    그렇다고, 120cm 위성 안테나가 약14~15만원 하고, 수신기(LNB? ) 라는거 달면 한 20은 나가는것 같더군요...

     

    (대충 저런 안테나 + LNB + 여기에 안나온 베란다 거치대 6만원이랑 TV케이블...... orz..)

     

     

    그래도 나오면 괜찮지만.... 안나오면......

     

    차라리 포기하고 한국 방송을 수신하는 새 LED TV(대우께 32인치가 30~32만원 하더군요) 

     

    걍 TV를 살지, 위성안테나를 감행할지... 고민 하던중

    위성 안테나를 파는 사이트에 물어 보았습니다...

     

    그리고 그 결과... (답변을 하루만에 주시네요.) 

     

    요약하면, 120cm 위성 안테나를 달면 나오기는 하겠지만, 수신율이 떨어져서 잘 안나올수도 있고

    17층에 베란다 안테나 설치는 권하지 않습니다..... orz

     

    당분간 ps3 전용 머신으로 사용해야 할거 같네요... ㅠㅠ

    그리고 돈 모아서 회사 근처로 이사 가면 그때 다시 도전할지.. 아니면 미친척 하고 지금 지를지...

    (ps3 전용 머신)

     

     

     

    결국 결론.....

    넷북에다가

    일본 실시간 방송 보는 사이트 http://j.tv.je/ 를 띄우고 전체화면 및 tv로 띄우기...

    중국형들 힘을 빌린 방법...(헐...)

    버퍼링도 있지만, 못볼 레벨은 아니고 하고.. 그냥 이걸로 만족 하려고 합니다.

     

    음... 다음 tv처럼 스마트형 셋톱박스가 일본에도 있을텐데... (하이브리드 셋톱박스라고 하더군요..)

    그걸 이용해서 인터넷으로 기본 컨텐츠 방송이라도 볼 수 있다면.... 그때 도전해봐야 할거 같네요..

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG BS방송

    출처 : http://kuaaan.tistory.com/118


    어가기에 앞서 : 이 글은 IIS 등 웹서버를 운영하시는 서버운영자를 위한 글이 아닙니다. 이 글에서는 Socket 프로그래밍을 통해 서버 프로그램을 개발할 때 안전한 소켓 종료절차에 대해 논해보고자 합니다. TIME_WAIT를 없애는 웹서버 설정을 찾고 계신 분은 다른 글을 검색하시기 바랍니다. ^^

    ----------------------------------------------------------------------------------------------------------
    화장은 하는것보다 지우는 것이 중요하다고 하죠. 마찬가지로 네트워크도 세션을 시작하는 것보다 잘 마무리짓는 것이 중요할 수 있습니다. 이 글에서는 어떻게 세션을 종료해야 데이터 손실 없이 서버에 무리를 주지 않고 잘 마무리 지을 수 있는지를 논하고자 합니다.

    1. TCP의 세션 종료 매커니즘과 TIME_WAIT
      흔히 알고 있듯이 TCP 프로토콜은 세션을 맺는 3-way Handshake라는 매커니즘을 가지고 있습니다. (아래 그림의 각 상태들은 cmd 창에서 netstat -n 명령을 치면 나타나는 상태들입니다.)


     마찬가지로 TCP 세션을 종료할 때도 이와 비슷한 4-way Handshake라는 매커니즘이 적용됩니다.
    (그림 출처 : 갱주니 블로그)

       종료 절차를 잠시 설명하면 다음과 같이 되겠지요. (위의 그림에서 Client는 흔히 얘기하는 개념이 아니라 종료절차를 먼저 시작한 측을 말합니다. 말하자면 closesocket()을 먼저 호출한 측이 client가 되는 거죠)

    ① Client 에서는 세션 종료를 시작한다는 의미로 Server에 FIN 패킷을 전송합니다.(C : FIN_WAIT1시작) Server에서 FIN을 수신하면 "니가 보낸 FIN 잘 받았다"라는 의미의 ACK를 Client에 전송하게 됩니다. (S : CLOSE_WAIT, C : FIN_WAIT2 시작)
      코드상으로는 closesocket() 혹은 shutdown()함수를 호출하는 작업이 FIN 전송에 해당합니다.

    ② Client로부터 FIN을 받은 Server는 Client가 세션 종료를 시작했다는 것을 인지합니다. Server는 종료에 필요한 Application 적인 작업을 진행합니다. (CLOSE_WAIT) 종료할 준비가 되면 Server는 closesocket()을 호출하여 Client에 FIN을 전송합니다. 말하자면 "OK, 나도 종료할께"하고 알려주는 거죠 (S : LAST_ACK, C : TIME_WAIT시작) 
      코드상으로는, 서버에서 recv()를 호출했을 때 0을 return하는 경우가 Client가 먼저 closesocket()을 호출한 경우에 해당합니다.

    ③ Client에서는 Server에서 전송한 FIN을 수신하고, 이에 대한 ACK를 보내고 나면 세션을 종료할 수 있는 상태가 됩니다. 
    그런데 만약 "Server에서 FIN을 전송하기 전에 전송한 패킷이 Routing 지연이나 패킷 유실로 인한 재전송 등으로 인해 FIN패킷보다 늦게 도착하는 상황"이 발생한다면 어떻게 될까요? Client에서 세션을 종료시킨 후 뒤늦게 도착하는 패킷이 있다면 이 패킷은 Drop되고 데이터는 유실될 것입니다. (이러한 현상이 발생할 수 있는 것은 패킷이 반드시 전송한 순서대로 도착하는 것이 아니기 때문입니다.) 이러한 현상에 대비하여Client는 Server로부터 FIN을 수신하더라도 일정시간(디폴트 240초) 동안 세션을 남겨놓고 잉여 패킷을 기다리는 과정을 거치게 되는데 이 과정을 "TIME_WAIT" 라고 합니다. (이런 내용은 Stevens 아저씨의 TCP/IP Illustrated vol 1을 보시면 자세히 나와 있습니다.)

    TCP/IP의 상태를 나타내는 State Diagram에는 조금 다르게 그려놓은 그림도 있습니다만, 대략적인 내용은 비슷합니다.

    (그림 출처 : 인터넷)

    위의 그림을 잘 살펴보면 TIME_WAIT라는 상태에 대해서 몇가지 중요한 사실을 확인할 수 있습니다.

    ① TIME_WAIT는 반드시 세션 종료를 먼저 시작한 쪽(closesocket()을 먼저 호출한 쪽)에 남게 됨.
    ② 세션 종료 과정을 먼저 시작한 측에서는 반드시 TIME_WAIT를 거쳐야 CLOSED 상태로 갈 수 있음. 따라서 TIME_WAIT 상태 자체는 비정상적이거나 문제가 되는 상태가 아님.
    ③ 반드시 양쪽에서 모드 세션을 종료처리 해야 (즉, FIN을 전송해야) TIME_WAIT로 갈 수 있음.

      이 TIME_WAIT라는 상태가 중요한 이유는, 만약 종료절차가 잘못 진행되어 서버쪽에 TIME_WAIT가 남게 되면 심각한 문제가 발생할 수도 있기 때문입니다. 일단 TIME_WAIT가 시작되면 2분여 이상 상태가 지속되게 되는데 모든 클라이언트들의 세션 종료시마다 서버 측에 TIME_WAIT가 발생한다면, 서버측에 부하가 될 뿐만 아니라 최악의 경우 서버에서는 더이상 새로운 연결을 받아들일 수 없는 상황이 발생할 수 있습니다. 말하자면.. 장애 상황이 발생하는 거죠. (실제로 실 운영서버에 이런 일이 발생하는 것을 직접 목격한 적이 있습니다. )


    2. Linger 옵션에 대하여
     Linger옵션이란 closesocket()를 호출했을 때 아직 send되지 않고 SendBuffer에 남아있는 Data를 어떻게 처리해야 할지를 OS에게 알려주는 옵션입니다. 이 Linger 옵션을 사용하면 TIME_WAIT를 남지 않게 소켓을 종료할 수 있습니다.

     Linger 옵션의 의미를 알아보기 위해, 먼저 일반적인 send() / recv() 가 호출될 때 어떤 일이 벌어지는지 살펴보겠습니다.


    1) send() 함수는 인자로 주어진 Buffer의 데이터를 해당 Socket에 할당된 SendBuffer에 복사하고 Return합니다. 중요한 것은 send()가 return했다고 해서 인자로 넘긴 데이터가 Peer에 전송된 것이 아니라는 것입니다.

    2) OS는 SendBuffer에 들어온 Data를 Peer에게 전달합니다. 이때 Data가 전달되기 위해서는 상대편의 RecvBuffer에 공간이 남아있어야 하는데, 만약 Receiver.exe가 바쁘거나 해서 recv()를 제때 호출해주지 못할 경우 RecvBuffer에 공간이 부족하여 SendBuffer의 Data가 곧바로 전송되지 못한 채 지연될 수도 있습니다. 
      그렇다면 Sender는 Receiver의 RecvBuffer에 공간이 부족하다는 것을 어떻게 확인해서 전송을 하거나 중단하는 것일까요? TCP 프로토콜은 Ack패킷을 이용해 Sender와 Receiver 간에 RecvBuffer에 공간이 있는지를 체크하는 매커니즘을 제공하는데 이것을 "Sliding Window"라고 합니다. (이 부분에 대해서는 역시 스티븐스 아저씨의 TCP/IP Illustrated를 참고하세요. ^^)


    3) 만약 위의 그림과 같이 SendBuffer에 Data가 남아있는 상황에서 Sender.exe가 closesocket()을 호출한 경우 어떤 일이 벌어질까요? 일단 Socket을 정리하려면 Socket에 할당된 SendBuffer도 파괴되어야 합니다. SendBuffer에는 아직 Data가 남아있는 상황이구요... 이런 상황에서 어떻게 처리해야 할지를 지정하는 방법이 LINGER 옵션입니다. LINGER 구조체의 값을 어떻게 설정하느냐에 따라 다음과 같은 세가지 처리가 가능합니다.

    - 처리방법 1 : LINGER.l_onoff = 1, LINGER.l_linger = 0인 경우, closesocket() 함수는 즉시 return하고, 버퍼에 남아있는 Data는 버려집니다. 말하자면... 비정상종료(Abortive Shutdown)이 됩니다.
    - 처리방법 2 : LINGER.l_onoff = 1, LINGER.l_linger = non-zero인 경우, 정상적인 종료과정(Graceful Shutdown)이 진행되며, 정상적 종료가 완료될때까지 closesocket()은 리턴하지 않습니다. 그렇지만, l_linger에 명시된 시간(초) 가 지나도 정상적인 종료가 완료되지 않을 경우 비정상종료(abortive shutdown)가 진행되고, closesocket()이 곧바로 리턴되며, Buffer에 아직 남아있는 데이터는 버려집니다.
    - 처리방법 3 : LINGER.l_onoff = 0 인 경우, closesocket() 함수는 즉히 return한 후 정상적인 종료 과정(Graceful Shutdown)을 Background로 진행합니다. 이것이 Default 동작이지만, Application은 Background로 진행되는 종료작업이 언제 완료되었는지를 확인할 방법은 없습니다. 
    (참고 : http://msdn.microsoft.com/en-us/library/ms738547.aspx)

      위에서 설명된 "처리방법 1" 대로 Linger 옵션을 지정했을 경우 TIME_WAIT가 남지 않습니다.명시적으로 Abortive Shutdown을 지정했기 때문이죠. 
    다음과 같이 Linger 옵션을 사용하면 TIME_WAIT가 남지 않습니다.
    1. // LINGER 구조체의 값 설정  
    2. LINGER  ling = {0,};  
    3. ling.l_onoff = 1;   // LINGER 옵션 사용 여부  
    4. ling.l_linger = 0;  // LINGER Timeout 설정  
    5.   
    6. // LINGER 옵션을 Socket에 적용  
    7. setsockopt(Sock, SOL_SOCKET, SO_LINGER, (CHAR*)&ling, sizeof(ling));  
    8.   
    9. // LINGER 옵션이 적용된 Socket을 closesocket()한다.  
    10. closesocket(Sock);  


    3. Graceful Shutdown에 관하여
      위에서 검토한 바와 같이 Buffer에 데이터가 남아있는 상태에서 연결을 강제로 종료할 경우 SendBuffer에 있는 데이터가 유실될 수도 있는데, 이러한 종료방식을 "Abortive Shutdown"이라고 합니다. 반대로 TCP 프로토콜의 4-way Handshake에 따라 데이터 유실 없이 종료하는 것을 "Graceful Shutdown"이라고 합니다.

      인터넷의 TIME_WAIT 관련된 글 중 일부는 Linger 옵션을 사용하여 TIME_WAIT를 남기지 않고 세션을 종료하는 것을 "Graceful Shutdown"이라고 표현한 글이 있는데, 이것은 잘못된 표현입니다. 오히려 TIME_WAIT는 Graceful Shutdown이 이루어지는 과정에서 자연스럽게 발생하는 과정입니다. 억지로 TIME_WAIT를 남기지 않기 위해 Linger 옵션을 사용하는 것은 데이터 유실을 초래할 수도 있으므로 조심해야 합니다. (비록 저도 실제로 이런 경우를 보지는 못했지만... 이론적으로는 그렇다고 합니다. ^^)
      또한 TIME_WAIT가 FIN 신호를 제대로 교환하지 못했기 때문에 발생한다는 의견도 잘못된 것입니다. 위에서 살펴보았듯이 TIME_WAIT는 Graceful Shutdown 과정에서 필수적으로 거쳐가야 할 과정입니다. (실제로 테스트를 해 본 결과, Linger옵션을 조작하는 경우를 제외하면, 어떠한 방식으로 세션을 종료하더라도 서버 혹은 클라이언트 양쪽에 모두 TIME_WAIT가 생기지 않도록 종료하는 방법을 찾지 못했습니다.)

      우리가 지향해야 할 세션 종료는 무조건 TIME_WAIT를 남기지 않는 것이 아니라, TIME_WAIT가 서버 측이 아닌 클라이언트 측에 생기도록 하는 Graceful Shutdown입니다. 이렇게 되기 위해서는 위에서 살펴본 바와 같이 "Client가 먼저 closesocket()을 호출하도록 하는 것"이 가장 중요합니다. 이것을 보장하기 위해서는Client와 Server 간에 종료 프로토콜을 설계할 때 Client가 먼저 closesocket()을 먼저 호출하도록 반영되어야 합니다. 이것만 확실히 지켜진다면 서버에는 TIME_WAIT가 남지 않습니다.

      다음은 종료 프로토콜의 예입니다. 

    1) 서버에서 클라이언트에 "너 종료해라"는 커맨드를 전송합니다.
    2) "너 종료해라"를 수신한 클라이언트는 서버에 "알았다 종료하겠다"를 전송한 후 즉시 closesocket()를 전송합니다. ( 마지막 통신이 클라이언트 -> 서버 방향으로 일어난다는 것이 중요합니다.)
    3) "알았다 종료하겠다"를 수신한 서버는 해당 소켓에 대해 closesocket()을 호출합니다. 이때 안전장치로 Linger 옵션을 주어 Abortive Shutdown을 시키는 것이 좋습니다. 어차피 서버에서 클라이언트로는 더이상 보낼(유실될) 데이터가 없다는 것이 확인되었고, 간혹 Client 중에 프로토콜을 따르지 않고 종료하는 녀석들이 있기 때문입니다.
    ※ IOCP 를 사용한 서버에서는 주기적으로 마지막 통신한 TimeStamp를 체크하여 Idle Session에 대해 Gabage Collection(?)을 수행해주어야 합니다. 이러한 경우에는 서버측에서 먼저 closesocket()을 호출할 수 밖에 없기 때문에, TIME_WAIT를 남기지 않기 위해 반드시 Linger 옵션으로 Abortive Shutdown을 시켜주어야 합니다.


    4. 비고
     closesocket() 함수는 내부적으로 두가지 역할을 수행합니다.

    1) 세션의 종료 절차
    2) 소켓 핸들의 Close 등 자원 해제 절차

    이 중, 첫번째 종료절차만을 수행하는 shutdown()이라는 함수가 있습니다. 세션 종료 과정을 명시적으로 수행하고 싶을 때 사용하는 함수입니다.
    아래의 MSDN 문서를 보면 shutdown 함수를 사용해 완전한 Graceful Shutdown을 수행하는 방법이 안내되어 있습니다. (제 개인적인 생각으로는... 이렇게까지 할 필요는 없을 것 같습니다. ^^)

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea
    TAG TCP IP

    출처 : http://kuaaan.tistory.com/116


    들어가기에 앞서 : 이 글은 크리티컬 섹션등의 동기화 개체나 관련 API 사용법 혹은 동기화가 이루어지지 않았을 때 발생하는 문제점 등을 설명하지 않습니다. 이 글은 성능저하를 보다 줄이면서 안전한 멀티스레드 프로그램을 구성하는 방법(스레드 모델)에 대해 얘기합니다.


    1. 동기화 개체에 대한 잘못된 생각들

      스레드를 동기화한다 함은 스레드 간의 실행순서를 정하거나, 스레드 간 특정작업이 동시에 일어나지 않도록 구현하는 것을 말합니다. 

    1) Manager Thread가 Worker Thread를 생성한 후, Worker Thread의 초기화가 끝날 때까지 기다려야 하는 경우. 이런 경우에는 보통 Event 개체나 메시지를 사용합니다.

    2) 전역 데이터에 접근할 때에 한번에 스레드 한개씩만 해당 데이터를 사용하도록 구현하는 경우 : 이 경우에는 보통 CriticalSection 을 사용합니다. 일반적으로 스레드 동기화라 함은 이러한 경우를 말합니다.

    3) 일정 숫자 이상의 스레드(혹은 프로세스)가 동시에 특정 자원을 사용하거나 동작하지 못하도록 하는 경우 : 이 경우에는 보통 Semaphore를 사용합니다.


    CriticalSection을 사용하는 데에 있어서 초보자분들은 다음과 같은 착각을 하기 쉽습니다.

    착각1) EnterCriticalSection()을 호출하면 Data에 Lock이 걸리고 다른 Thread가 접근하지 못하게 된다. (X)

    ==> CriticalSection 등의 동기화 개체에 Lock을 건다 함은 화장실이라는 Resource에 문을 걸어 잠그는 개념이 아니라, 화장실의 문에 "사용중"이라고 써붙이는 개념입니다. 이게 무슨 얘기냐 하면, "g_szData 라는 Data에 접근할 때는 crit_szData에 Lock을 걸어야 한다"라고 개발자가 스스로 규칙을 세우고, 코드의 모든 부분에서 g_szData를 사용할 때 CriticalSection에 Lock을 걸도록 구현해야 한다는 뜻입니다. 화장실의 문에 "사용중"이라고 써붙이고 들어갔는데, 어떤 사람이 문에 붙어있는 표시를 확인하지 않고 문을 열고 들어간다면? 운이 좋으면 문제가 안생기겠지만 재수가 없으면 민망한 상황이 벌어질 것입니다. 

    엄밀히 말하면 데이터에 동기화개체는 스레드 들이 특정 데이터에 동시 Read/Write하지 못하도록 하는 기능을 제공하지 않습니다. 단지 동기화개체에 동시에 Lock을 걸지 못하는 기능을 제공할 뿐이죠. :)


    착각2) 데이터에 Read할 때는 동기화가 필요 없다. 따라서 EnterCriticalSection은 Data에 Write를 수행할 때만 수행하면 된다. (X)

    ==> Data를 Read하는 스레드에서는 그냥 Read를 하면서 Data에 Write를 하는 스레드에서만 Lock을 건다고 해서 다른 스레드가 그 Data에 접근을 못하게 되는 것이 아닙니다. 어떤 CriticalSection에 Lock을 건다고 해서 스레드간 ContextSwitching이 중지되는 건 더더욱 아닙니다. 보호하고자 하는 데이터를 참조하는 모든 부분에 동기화 처리를 해주지 않으면 동기화 개체를 사용하는 것은 아무런 의미도 없습니다. 
    간단하게 생각하면 CriticalSection이란 보호할 데이터를 사용하는 중에 BOOL형 변수에 "사용중"이라는 의미로 "TRUE" 값을 Setting하는 이상의 의미는 없습니다. 단지 두 Thread에서 동시에 TRUE를 설정하지 못하도록 OS에서 보장해준다는 점과 TRUE를 Setting하려고 할 때 이미 TRUE라는 값이 Setting되어 있다면 이 변수가 FALSE로 바뀔 때까지 스레드가 BLOCK되는 등의 처리가 자동으로 구현되어 있다는 정도가 차이가 있을 뿐입니다.
    단지 프로그램 전체에 걸쳐서 어떠한 "Write"도 수행되지 않고 "Read"만 수행되는 데이터라면 동기화가 필요 없습니다. 

    대부분의 개발자 분들은 이런 얘길 보면서 "이렇게 당연한  얘기를 왜 하나?"라고 생각하시겠지만... 바로 얼마 전에 십 수년간 개발을 하신 서버 개발자께서 "CriticalSection에 Lock을 걸면 말이쥐... 다른 스레드들은 이 데이터에 접근을 못하게 되는 거란다"라고 친절하게 설명해주시는 걸 듣고... 까무라치는 줄 알았습니다. ㅡ.ㅡ

    ※ 선언 함수나 초기화 함수를 보면 알수 있듯이, CriticalSection은 그 자체로서 어떠한 데이터와도 연관되지 않습니다. 어떠한 데이터를 보호하는데 어떤 CriticalSection 개체를 사용하겠다 하는 것은 순전히 개발자의 로직상으로 구현되는 부분입니다. 위에서 얘기했듯이 OS에서는 동시에 한개의 스레드만 EnterCriticalSection에 성공할 수 있다는 것 외에는 아무것도 보장하지 않습니다. 나머지는 개발자의 몫인 거죠.


    2. Case I : Multiple-Reader + Multiple-Writer 환경 (CriticalSection Per Data Model)

      멀티스레드 환경에서 발생할 수 있는 가장 일반적인 경우는 다수의 스레드가 동시에 데이터를 읽고 쓰는 경우입니다. 이 경우에는 어떠한 "읽기"와 "읽기", "읽기"와 "쓰기" 혹은 "쓰기"와 "쓰기" 도 동시에 발생해서는 안되며, 완전하게 동시접속이 차단되어야 합니다. 이렇게 처리하는 것을 직렬화(Serialize)한다고 합니다.

      이런 환경을 구현하기 위해서는 CriticalSection을 접속하고자 하는 Data의 수만큼 만들고, (읽기/쓰기에 상관없이) Data에 접근할 때마다 해당 Data에 Matching 되는 CriticalSection에 대해 EnterCriticalSection을 수행해주어야 합니다. 말하자면 "CriticalSection Per Data" 정도 되겠네요.

    가장 안전한 환경이 구현되지만, 병렬수행이 불가능해지므로 성능은 최악이 됩니다. 

    그림으로 표현하면 다음과 같이 되겠네요.



    3. Case II : Multiple-Reader + Single-Writer 환경 (CriticalSection Per Thread Model)

      모든 스레드가 읽기와 쓰기를 행한다면 Case I  과 같이 직렬화를 시켜야겠지만, 대부분의 Server용 Application에서는 다수의 Worker Thread는 전역 데이터에 대해 Read만을 수행하고 한개의 Manager Thread가 Write를 수행하는 구조인 경우가 많습니다. 대표적인 예가 Manager Thread가 기록한 정책을 참조하여 Worker Thread가 클라이언트의 접속을 처리하는 경우를 예로 들 수 있겠죠. (Thread Pool 이 보통 이런 동작을 합니다.)

    이러한 경우는 다음과 같은 특징이 있습니다.


    1) Read의 빈도가 매우 높고, Write의 빈도는 낮다. 

    2) Read와 Read는 동시에 이루어지는 것이 바람직하지만 Write와 Read는 동시에 이루어져선 안된다.

    3) WokerThread간의 병렬처리가 이루어져야 하므로 Read의 속도가 매우 중요하며 Write의 성능은 상대로 덜 중요하다.


    위의 조건을 만족시키기 위해서는 스레드와 동기화 개체들을 어떤 식으로 구성해야 할까요? 제가 내린 결론은 다음과 같습니다. (더 좋은 방법을 아시는 분은 가르쳐주세요. ^^)

    1) WorkerThread 마다 CriticalSection을 하나씩 생성한다. WorkerThread는 각자 자기의 CriticalSection을 가지고 있다. 말하자면 워커스레드가 6개라면 CriticalSection도 6개가 생성되며 이것이 Array처럼 접근되도록 구현된다.

    2) WorkerThread 는 작업을 시작할 때마다 자기의 CriticalSection에 대해 EnterCriticalSection()을 호출하고 작업을 끝낸 후에는 LeaveCriticalSection을 호출합니다. 이렇게 되면 각 WorkerThread 간에는 작업이 병렬수행되며, 전역 Data에 대해서도 병렬 Read가 이루어집니다. (이때 ManagerThread 는 쉬고 있겠죠)

    3) ManagerThread 가 정책을 업데이트해야 할 때는 For Loop 를 돌면서 모든 CriticalSection에 대해 EnterCriticalSection()을 호출합니다. 일단 ManagerThread가 CriticalSection에 Lock을 걸고 나면 해당 WorkerThread는 새로운 작업을 시작하지 못하기 때문에, For Loop가 끝나게 되면 결과적으로 ManagerThread는 모든 WorkerThread를 정지시키게 됩니다. (이때는 물론 시간이 좀 걸리겠죠? ^^)

    4) ManagerThread는 정책을 수정합니다.

    5) ManagerThread는 For Loop를 돌면서 모든 CriticalSection에 대해 LeaveCriticalSection()을 호출합니다. 이제 WorkerThread들은 다시 자신의 CriticalSection을 획득하고 밀린 일을 처리할 수 있습니다.


    그림으로 표현하면 대략 다음과 같이 되겠네요.


    WorkerThread간의 읽기작업을 표현하면 다음과 같습니다.


    ManagerThread가 쓰기 작업을 진행할 때는 다음과 같이 됩니다.


    말하자면... Write속도를 희생시켜서 Read속도를 증가시킨다고나 할까요? ^^


    4. Case III : 동기화를 하지 않는 방법(?)

      "멀티스레드 프로그래밍에 관한 고찰 (1)" 에서 논했듯이 멀티스레드가 Data에 동시접근한다고 해도 경우에 따라서는 동기화 하지 않고 사용할 수 있습니다. 만약 이게 가능하다면 멀티스레드 프로그램은 최고의 효율을 얻을 수 있습니다. 이렇게 구성하기에 앞서 해당 Case가 이런 방식으로 구성 가능한지 검토해야 하며, 가능하다면 자료구조가 안전한 동시접근이 가능하도록 설계되어야 합니다.

    정수형 Data가 Linked List에 저장되는 경우의 예를 들어 보겠습니다.

    1) Data가 변경될 때 Linked List에 Entry가 추가/삭제되는 경우에는 반드시 스레드 동기화가 필요합니다. 반대로 시종일관 LinkedList 자체는 변화가 없이 각 Entry의 정수형 데이터값만 변경시키는 경우에는 동기화 없이도 Concurrent Read/Write가 안전하게 수행될 수 있습니다. 
       이러한 경우의 예를 들면 오목게임의 바둑판을 들 수 있습니다. 모든 좌표에 대해 게임을 시작하기 전에 19 X 19 개의 좌표 Data를 생성한 후, 게임이 진행되는 동안에는 해당 좌표에 대한 Data(흑/백/無)만 Update될 뿐 새로운 좌표가 추가되지는 않습니다. 이런 경우라면 동기화가 필요하지 않습니다.

    2) 만약 Data가 Linked List에 추가/삭제되어야 하는 경우라면, 추가되는 데이터의 "경우의 수"에 대해 생각해보아야 합니다. 경우에 따라서는 Insert될 수 있는 Data의 경우의 수가 한정되어 있어 초기화시에 모든 경우의 수에 대한 메모리를 미리 생성해놓고 시작할 수 있는 경우가 있습니다. 이러한 경우라면 동기화 없이 멀티스레드가 동작할 수 있습니다.
       예를 들어, 사내 IP체계를 B Class를 사용하는 회사에서 IP를 사용하는 Mac주소와 사용자 정보를 저장하는 자료구조가 있다고 가정해보겠습니다. 

    . 새로운 IP가 생성될 때마다 Linked List에 Entry가 생성되는 방식으로 설계한다면 스레드 동기화가 필요하며, 동기화하지 않을 경우 Entry를 LinkedList에 Insert하는 순간 Access Violation이 발생할 수 있습니다.

    . 모든 경우의 수 (255 * 255 = 65525개)만큼의 Entry를 처음에 일괄 생성해놓고, 사용하지 않는 IP 에 대해서는 FALSE, 사용하는 IP에 대해선 TRUE를 기록하여 미사용 IP를 관리하고, 사용중인 IP에 대해서는 Mac주소와 사용자정보를 미리 할당된 메모리에 memcpy하는 방식으로 자료구조를 설계할 경우 동기화 처리 없이도 안전하게 Concurrent Read/Write가 가능합니다. 한마디로 "메모리를 희생하여 속도를 향상"시키는 방법이죠 :)



    이 글에 있는 내용들은 저의 경험에 비추어 적은 것입니다. 물론 원칙적으로 얘기하면 정수형 데이터 하나에 대해서도 동기화를 해 주어야 합니다. 알고 있으니까요... 이런 태클은 사양합니다. ^^;;

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea

    출처 : http://kuaaan.tistory.com/114


    예전에는 고사양이라 하면 힘쎈 CPU를 의미했지만 지금 시대의 고사양이란 CPU 여러 개를 의미합니다. 이른바 멀티코어의 시대죠. 예전에 3.4GHz 4CPU가 최고사양 서버였다고 하면 요즘에는 1.6GHz 16Core (4Core * 4ea)가 동급으로 받아들여집니다. 

     이러한 H/W적인 패러다임의 변화는 S/W 개발에도 영향을 주어 예전에는 (어셈블리) 코드 한줄이라도 줄이는 게 퍼포먼스 향상의 열쇠였다고 한다면 지금은 여러 스레드(Thread) 들이 한 머쉰에서 서로 엉키지 않고(!) 조화롭게 돌아가는 구조를 구현하는 것이 퍼포먼스 향상의 열쇠라고 볼 수 있습니다. 

     그렇다면 멀티스레드 프로그램의 성능을 결정하는 열쇠는 무엇일까요?

     먼저 "스레드의 수"를 생각해 볼 수 있습니다.
     멀티스레드 프로그래밍을 해보시지 않으신 분들은 일단 스레드를 많이 만들면 퍼포먼스도 올라갈 것이라고 생각하시는 경향이 있읍니다만 이것은 잘못된 생각입니다. 
     중요한 것은 한개의 CPU는 동시에 한 개 씩의 스레드만 실행시킬 수 있다는 사실입니다. 스레드가 여러개가 생성되면 CPU는 각각의 스레드를 시분할하여 각각의 스레드를 번갈아가며 실행하게 되는데, 이때 이전 스레드의 문맥 정보 (레지스터 값, 실행중인 스택 정보 등)을 백업받고 백업받아놓았던 다음 스레드의 문맥정보를 로딩하는 과정을 거치게 됩니다. 이 과정을 Context Switching(문맥 교환이라고 번역하더군요) 이라고 하는데, 이러한 스레드가 많아질 수록 Context Switching 에 많은 부하가 걸리기 때문에 오히려 퍼포먼스는 떨어지게 됩니다. 
     그렇다면 스레드가 적을수록 퍼포먼스가 좋아질까요? 물론 그렇지는 않습니다. ^^
     

     위 그림과 같이 스레드는 작업을 진행함에 따라 Running <-> Waiting , Sleeping, Blocked 등으로 상태변화를 하게 되는데, 멀티스레드 프로그램은 한 스레드가 Waiting, Sleeping, Blocked 중일 때 CPU가 다른 스레드를 실행시킬 수 있기 때문에 동시성(Concurrency)이 높아져서 성능이 좋아지는 것입니다. 바꾸어 말하면 I/O가 많이 발생하는 프로그램일 수록 멀티스레딩의 효과를 크게 볼 수 있다고 말할 수도 있습니다.

    위의 두가지 면을 종합할 때 멀티스레드 프로그램의 성능은 스레드의 "동시성 향상 효과"와 "Context Switching 비용"의 Trade Off 에 의해 결정된다고 말할 수 있습니다.
     따라서 가장 적절한 스레드의 수는 CPU의 수와 스레드가 수행하는 작업의 성격을 함께 고려하여 결정되는데 일반적으로는 연산 위주의 작업의 경우 CPU당 2~3개, I/O 위주 작업의 경우 CPU당 5개 내외를 적절한 스레드의 수로 가이드하며, 보통 스레드 풀(Thread Pool)을 생성할 때 워커 스레드 (Worker Thread)의 수를 산정하는 방식으로 사용됩니다.

     요즘 네트워크 프로그램에서 많이 사용되는 IOCP (Input Output Completion Port)가 성능이 좋은 이유는 I/O가 진행되는 동안 스레드가 스위칭되거나 블러킹되지 않기 때문에 스레드의 Context Switching을 최소화할 수 있고, 따라서 필요한 스레드의 수를 최소화할 수 있기 때문입니다. 그런 면에서 어찌보면 최소한의 스레드를 써야 높은 성능을 낼 수 있다고 볼 수 있는 거죠.


       그렇다면 "동시성 향상 효과"를 높이는 요인은 무엇이 있을까요? 바로 "효율적인 스레드 동기화" 를 들 수 있습니다.
      멀티스레드 프로그래밍은 프로세스의 동시성을 향상시켜 성능을 향상시키는 효과가 있지만, 여러 스레드 들이 동시에 데이터를 접근하면서 생기는 문제들이 빛과 그림자처럼 쫓아다니게 됩니다. 예를 들어 스레드 A가 링크드 리스트에서 Read를 시도하려고 하는 순간에 스레드 B가 해당 링크드 리스트에 ClearAll() 을 수행한다면 어떤일이 벌어질까요? 스레드 A는 메모리 폴트를 발생시키고 해당 프로세스는 중지되고 말 것입니다. 이러한 일을 방지하기 위해서는 스레드들이 링크드 리스트에 동시에 접근하지 못하도록 직렬화(Serialize)시킬 필요가 있는데 이런 작업을 "스레드 동기화"라고 합니다. 
      스레드 동기화가 잘못되었을 때 생기는 문제들이나 동기화 개체 사용법 등에 대해서는 인터넷에 좋은 포스트가 많이 공개되어 있으니, 여기서는 어떻게 하면 보다 효율적인 스레드 동기화를 구현할 수 있을 것인가에 대해 생각해보겠습니다. 
     문제는 스레드 동기화의 정도가 높아질수록 데이터에 대한 단일접근이 보장되어 안정성과 데이터 무결성은 높아지지만, 반면에 데이터에 접근할 때의 동시성이 저하되어 성능은 (형편없이) 떨어지게 됩니다. 여기서도 Trade Off 문제가 발생하는 거죠. 원칙적으로 모든 전역 데이터(Global Data)에 접근할 때 CriticalSection등의 동기화 개체를 사용하여 동시접속을 차단하는 것이 원칙이겠지만 그렇게 하게 되면 너무 성능이 떨어지기 때문에, 대부분의 서버 개발자들은 "이정도는 괜찮더라"는 나름대로의 선을 정해놓고, 그 범위 안에서 "적당한 물타기"를 시도하게 됩니다. 
     멀티스레드 프로그래밍을 할 때 제 나름대로의 동기화 기준을 공개한다면 대략 다음과 같습니다.

    1. 전역 데이터에 멀티스레드가 읽기를 동시에 시도하는 경우에는 동기화 할 필요가 없습니다. 

    2. 전역 데이터에 멀티스레드가 읽기와 쓰기를 혹은 쓰기와 쓰기를 동시에 시도하는 경우에는 "원칙적으로" 직렬화를 시켜야 합니다. (직렬화를 한다 함은 동시접근을 차단한다는 뜻입니다.) 이때, 저는 읽기 및 쓰기가 행해지는 데이터의 성격에 따라 다음과 같이 "적당한 대처"를 합니다. ^^
       1) 멀티CPU 환경에서는 기본적으로는 4바이트 정수 연산 하나에 대해서도 원자성을 100% 보장할 수 없습니다. 따라서 동기화의 비용(성능 감소)와 데이터 무결성이 깨어졌을 때의 발생가능한 손해를 비교하여 동기화 수준을 결정해야 합니다.
       2) 포인터 데이터에 대한 무결성 문제는 바로 메모리 폴트로 이어집니다. 따라서 대상 데이터 중에 "포인터"가 포함된 경우에는 반드시 동기화를 시켜야 합니다. 
       3) 단순한 정수/실수형의 경우 "대부분의 경우"에는 동기화에 신경쓸 필요가 없습니다. 하지만 이 데이터가 무결성에 얼마나 민감한지를 검토해볼 필요는 있습니다. 예를 들어, 어떤 일을 해야 할지 말지를 나타내는 Boolean (True/False) 값 등은 굳이 동기화할 필요가 없습니다. 만약 어떤 값을 카운트하는 변수일 경우, 카운트가 1~2개정도 어긋나면 어떤 문제가 생길지를 체크해봅니다. 만약 ++연산을 100번했을 때 99만 증가해도 큰 문제가 되지 않는다면 동기화하지 않아도 되겠지만, 정확한 카운트가 보장되어야 하는 경우라면 해당 변수를 Volatile 로 선언한 후 InterlockedAdd 함수 등을 이용해 동기화해주어야 합니다.
       4) Linked List, Binary Tree 등의 자료구조는 각 Entry들이 포인터로 연결되어 있습니다. 따라서 반드시 동기화해주어야 합니다. STL도 예외는 아닙니다. 다만, Linked List에 Entry가 Insert, Delete되지 않는다면 (포인터 연산이 일어나지 않기 때문에) 동기화가 필요하지 않을 수도 있습니다.
       5) 구조체나 클래스, 배열 등의 경우에는 각 Entry 의 데이터 타입이나 성격에 따라 위의 1)~3) 기준에 따라 판단해주면 됩니다.
       6) Memory Mapped File 등 프로세스간 공유되는 데이터의 경우에는 가급적이면 동기화해주는 것이 좋습니다.

    3. 요즘에는 그런 일은 거의 없겠지만, 멀티CPU에서 실행되지 않는다는 것이 보장된다면... 사실 왠만한 동기화는 신경 안써도 됩니다. 뒤집어서 말하면... 멀티CPU에서는 동기화에 대해 깊이 고민해야 합니다.


    다음번에는 각 케이스 별로 데이터 무결성을 보장하면서 성능 저하를 최소화하는 동기화 방법(구조)에 대해 생각해 보려고 합니다.

    ※ 위 글에는 필자의 개발 경험에서 우러나온 "적당"한 통밥이 사용되고 있습니다. 위의 내용을 실제로 개발에 적용하였다가 낭패를 보더라도 당연히 저는 책임지지 않습니다. ^^

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea

    렌카드 설정을 했는데 ping이 안 간다면..

    아래의 path_to_inst 내용에서 

    ethernet 번호와 ifconfig 상 나오는 index번호가 같은지 확인해 봐야 한다.



    path_to_inst 출처 : http://radiocom.kunsan.ac.kr/lecture/unix_cmd/path_to_inst.html


    /etc/path_to_inst 파일 내용


    Disk Administration
    물리적 장치 이름과 인스턴스 이름(instance name)을 매핑한 내용이 담긴 파일임

    이 path_to_inst 파일 내용을 수동으로 변경하려면 devfsadm 명령을 사용하면 된다. 시스템에 어떤 장치를 hot-plugging에 의해서 자동으로 /dev, /devices 파일을 수정하는 것이 바로 devfsadmd라는 데몬이 수행한다.

    또한 prtconf 명령을 이용하여 확인할 수 도 있다.

    다음 내용은 x86 솔라리스 시스템의 예이다.

    # cat /etc/path_to_inst
    #
    #       Caution! This file contains critical kernel state
    #
    "/options"                                0 "options"
    "/pci@0,0"                                0 "pci"
    "/pci@0,0/pci-ide@1f,1"                   0 "pci-ide"
    "/pci@0,0/pci-ide@1f,1/ide@0"             0 "ata"
    "/pci@0,0/pci-ide@1f,1/ide@0/cmdk@0,0"    0 "cmdk"
    "/pci@0,0/pci-ide@1f,1/ide@1"             1 "ata"
    "/pci@0,0/pci-ide@1f,1/ide@1/sd@0,0"      0 "sd"
    "/pci@0,0/pci-ide@1f,1/ide@1/st@0,0"      0 "st"
    "/pci@0,0/pci8086,2561@1"                 0 "pci_pci"
    "/pci@0,0/pci8086,2561@1/display@0"       0 "vgatext"
    "/pci@0,0/pci8086,244e@1e"                1 "pci_pci"
    "/pci@0,0/pci8086,244e@1e/pci10b7,9055@b" 0 "elxl"
    "/pci@0,0/pci174b,174b@1d"                0 "uhci"
    "/pci@0,0/pci174b,174b@1d,1"              1 "uhci"
    "/pci@0,0/pci174b,174b@1d,2"              2 "uhci"
    "/pci@0,0/pci174b,174b@1d,7"              0 "usba10_ehci"
    "/objmgr"                                 0 "objmgr"
    "/pseudo"                                 0 "pseudo"
    "/isa"                                    0 "isa"
    "/isa/i8042@1,60"                         0 "i8042"
    "/isa/i8042@1,60/keyboard@0"              0 "kb8042"
    "/isa/i8042@1,60/mouse@1"                 0 "mouse8042"
    "/isa/asy@1,3f8"                          0 "asy"
    "/isa/asy@1,2f8"                          1 "asy"
    "/isa/fdc@1,3f0"                          0 "fdc"
    "/isa/fdc@1,3f0/fd@0,0"                   0 "fd"
    "/isa/fdc@1,3f0/fd@0,1"                   1 "fd"
    "/isa/lp@1,378"                           0 "lp"
    "/xsvc"                                   0 "xsvc"
    # 
    
    여기서 나타내는 각 필드의 의미는 다음과 같다.

    fielddescription
    physical namefull physical device name or full path device name
    instance nameThe unique number(typically starting with 0)
    driver binding namename assigned to the device driver


    % cat /etc/path_to_inst
    #
    #       Caution! This file contains critical kernel state
    #
    "/pseudo"                                        0 "pseudo"
    "/scsi_vhci"                                     0 "scsi_vhci"
    "/options"                                       0 "options"
    "/pci@1f,0"                                      0 "pcipsy"
    "/pci@1f,0/pci@1,1"                              0 "simba"
    "/pci@1f,0/pci@1,1/ide@3"                        0 "uata"
    "/pci@1f,0/pci@1,1/ide@3/sd@2,0"                 1 "sd"
    "/pci@1f,0/pci@1,1/ide@3/dad@0,0"                1 "dad"
    "/pci@1f,0/pci@1,1/ebus@1"                       0 "ebus"
    "/pci@1f,0/pci@1,1/ebus@1/power@14,724000"       0 "power"
    "/pci@1f,0/pci@1,1/ebus@1/su@14,3083f8"          0 "su"
    "/pci@1f,0/pci@1,1/ebus@1/su@14,3062f8"          1 "su"
    "/pci@1f,0/pci@1,1/ebus@1/se@14,400000"          0 "se"
    "/pci@1f,0/pci@1,1/ebus@1/ecpp@14,3043bc"        0 "ecpp"
    "/pci@1f,0/pci@1,1/ebus@1/fdthree@14,3023f0"     0 "fd"
    "/pci@1f,0/pci@1,1/ebus@1/SUNW,CS4231@14,200000" 0 "audiocs"
    "/pci@1f,0/pci@1,1/SUNW,m64B@2"                  0 "m64"
    "/pci@1f,0/pci@1,1/network@1,1"                  0 "hme"
    "/pci@1f,0/pci@1"                                1 "simba"
    "/iscsi"                                         0 "iscsi"
    %
    
    openBoot

    file system Admin

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea

    * 작업 환경

      - 4 port 랜카드가 서버에 2개 꽂여 있음

        즉 8개의 port 를 사용할 수 있는 NIC (ce0 ~ ce7)

      - 고객사에서 LAN 선을 하나 추가적으로 할당 받아 서버에 설정할 경우

      - 추가적인 물리적인 랜선이 꽂을때 네트워크 인터페이스 장치명이 ce0 인지? ce1 인지?  ce2 인지 알수 없음

     

     

    * 인터페이스 확인

    [command]# cat /etc/path_to_inst


    #
    #       Caution! This file contains critical kernel state
    #
    "/options" 0 "options"
    "/pci@8,700000" 0 "pcisch"
    "/pci@8,700000/ide@6" 0 "uata"
    "/pci@8,700000/ide@6/sd@0,0" 0 "sd"
    "/pci@8,700000/SUNW,XVR-100@2" 0 "pfb"
    "/pci@8,600000" 1 "pcisch"
    "/pci@8,600000/pci@1" 0 "pci_pci"
    "/pci@8,600000/pci@1/network@0" 0 "ce"
    "
    /pci@8,600000/pci@1/network@1" 1 "ce"
    "
    /pci@8,600000/pci@1/network@2" 2 "ce"
    "
    /pci@8,600000/pci@1/network@3" 3 "ce"
    "/pci@8,600000/pci@2" 1 "pci_pci"
    "/pci@8,600000/pci@2/network@0" 4 "ce"
    "
    /pci@8,600000/pci@2/network@1" 5 "ce"
    "
    /pci@8,600000/pci@2/network@2" 6 "ce"
    "
    /pci@8,600000/pci@2/network@3" 7 "ce"
    "/pci@9,700000" 2 "pcisch"
    "/pci@9,700000/ebus@1" 0 "ebus"
    "/pci@9,700000/ebus@1/rtc@1,300070" 0 "todds1287"
    "/pci@9,700000/ebus@1/i2c@1,30" 0 "pcf8584"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,80" 0 "ssc050"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,82" 1 "ssc050"
    "/pci@9,700000/ebus@1/i2c@1,30/temperature@0,30" 0 "max1617"
    "/pci@9,700000/ebus@1/i2c@1,30/temperature@0,34" 1 "max1617"
    "/pci@9,700000/ebus@1/i2c@1,30/fru@0,a0" 20 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,30/fru@0,a2" 21 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,30/fru@0,a6" 22 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,30/fru@0,a8" 23 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,30/fru@0,ae" 24 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,44" 0 "pcf8574"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,46" 1 "pcf8574"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,4c" 2 "pcf8574"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,70" 3 "pcf8574"
    "/pci@9,700000/ebus@1/i2c@1,30/ioexp@0,72" 4 "pcf8574"
    "/pci@9,700000/ebus@1/i2c@1,30/temperature-sensor@0,9c" 0 "lm75"
    "/pci@9,700000/ebus@1/serial@1,400000" 0 "se"
    "/pci@9,700000/ebus@1/rsc-control@1,3062f8" 0 "su"
    "/pci@9,700000/ebus@1/rsc-console@1,3083f8" 1 "su"
    "/pci@9,700000/ebus@1/i2c@1,2e" 1 "pcf8584"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a0" 0 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a2" 1 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a4" 2 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a6" 3 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a8" 4 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,aa" 5 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,ac" 6 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@0,ae" 7 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a0" 8 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a2" 9 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a4" 10 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a6" 11 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a8" 12 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,aa" 13 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,ac" 14 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@2,ae" 15 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@4,a0" 16 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/nvram@4,a4" 17 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@4,a8" 18 "seeprom"
    "/pci@9,700000/ebus@1/i2c@1,2e/fru@4,aa" 19 "seeprom"
    "/pci@9,700000/ebus@1/pmc@1,300700" 0 "pmc"
    "/pci@9,700000/ebus@1/power@1,30002e" 0 "power"
    "/pci@9,700000/ebus@1/gpio@1,300600" 0 "gpio_87317"
    "/pci@9,700000/usb@1,3" 0 "ohci"
    "/pci@9,700000/usb@1,3/hub@2" 0 "hubd"
    "/pci@9,700000/usb@1,3/hub@2/keyboard@4" 0 "hid"
    "/pci@9,700000/network@2" 8 "ce"
    "/pci@9,600000" 3 "pcisch"
    "/pci@9,600000/SUNW,qlc@2" 0 "qlc"
    "/pci@9,600000/SUNW,qlc@2/fp@0,0" 0 "fp"
    "/pci@9,600000/SUNW,qlc@2/fp@0,0/ssd@w2100001d3851de19,0" 0 "ssd"
    "/pci@9,600000/network@1" 9 "ce"
    "/memory-controller@0,400000" 0 "mc-us3"
    "/memory-controller@2,400000" 1 "mc-us3"
    "/pseudo" 0 "pseudo"
    "/scsi_vhci" 0 "scsi_vhci"

     

    * link_status 를 체크

      - ndd 명령의 결과에서 link_status 가 있을 경우 ndd -get /dev/ce link_status 할 때 결과로 링크 상태 체크

      - ce 장치의 경우 link_status 항목이 없음

     

    [command]# ndd -get /dev/ce \? 
    ?                             (read only)
    instance                      (read and write)
    adv_autoneg_cap               (read and write)
    adv_1000fdx_cap               (read and write)
    adv_1000hdx_cap               (read and write)
    adv_100T4_cap                 (read and write)
    adv_100fdx_cap                (read and write)
    adv_100hdx_cap                (read and write)
    adv_10fdx_cap                 (read and write)
    adv_10hdx_cap                 (read and write)
    adv_asmpause_cap              (read and write)
    adv_pause_cap                 (read and write)
    master_cfg_enable             (read and write)
    master_cfg_value              (read and write)
    use_int_xcvr                  (read and write)
    enable_ipg0                   (read and write)
    ipg0                          (read and write)
    ipg1                          (read and write)
    ipg2                          (read and write)
    rx_intr_pkts                  (read and write)
    rx_intr_time                  (read and write)
    red_dv4to6k                   (read and write)
    red_dv6to8k                   (read and write)
    red_dv8to10k                  (read and write)
    red_dv10to12k                 (read and write)
    tx_dma_weight                 (read and write)
    rx_dma_weight                 (read and write)
    infinite_burst                (read and write)
    disable_64bit                 (read and write)
    accept_jumbo                  (read and write)
    laggr_multistream             (read and write)

     

    * IP 셋팅


    [command]# ifconfig ce5 plumb 192.168.100.100 netmask 255.255.255.0 up

     

    [command]# ping 192.168.100.1
    192.168.100.1 is alive

     

    [command]# vi /etc/hostname.ce5
    192.168.100.100

     

    * 백승찬 강사님의 도움 감사드립니다 (^^)(__)(^^)

     

    * http://cafe.naver.com/eitlinux.cafe - 바두기 짱 -

     

     

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea

    출처 : http://www.solanara.net/solanara/hardware


    하드웨어 호환 목록(HCL) - 윈디하나의 솔라나라

    목차

    하드웨어 호환성

    솔라리스의 하드웨어 지원

    오라클과 후지쯔에서 판매하는 모든 Sparc 및 x86 시스템은 솔라리스를 매우 잘 지원한다. 적어도 이들 시스템에서는 하드웨어와 관련된 이슈가 없다. 아래의 내용은 모두 x86에 해당되는 내용이다.
    • CPU
      • AMD와 Intel CPU 및 32Bit, 64Bit를 잘 지원한다. Via는 지원을 보장하지 않는다.
      • AMD의 PowerNow!, Intel의 Speedstep은 Solaris 10 x86 5/08 부터 지원한다.
      • Sun xVM을 제대로 지원하기 위해서는 AMD 옵테론 리비전 F 이상, VT확장이 지원되는 인텔 CPU가 필요하다. 2008년 여름 이후에 리비전되어 나온 CPU는 모두 이를 지원한다.
    • VGA
      • nVIDIA칩을 사용한 그래픽 카드는 최신 그래픽 카드까지, 3D(OpenGL)까지 잘 지원해준다. nVidia 유닉스 드라이버에서 드라이버를 받을 수 있다. 최신 GPU를 사용하는 경우 솔라리스에 번들된 드라이버보다 좋으니 반드시 설치하자.
      • Intel칩셋을 사용한 IGP는 최신 버전까지 잘 지원해 준다.
      • 2011.09 현재 샌디브릿지CPU에 있는 내장 그래픽 시스템을 지원하지 못한다.
      • ATI칩을 사용한 그래픽 카드는 Radeon HD 이전의 그래픽 카드까지만 잘 지원해준다. Radeon HD 그래픽 카드라면, 화면이 나오는 정도다.
      • 그 이외의 VGA 카드는 화면이 나오는 정도다. (가속기능을 사용 못한다)
    • Storage
      • 표준 ATA및 SATA장치와, Adaptec, LSI Logic, Intel 의 SCSI, SAS, RAID 장치는 (저가형 어댑터를 제외하고) 대부분 지원한다. 벤더 홈페이지에 별도의 드라이버를 등록해 놓고 있다.
      • 솔라리스 10 5/08 이상부터는 Intel ICH6/7/8/9, VIA vt8251의 SATA AHCI와 NCQ를 지원한다. ICH10의 AHCI는 솔라리스 10 9/10부터 지원한다. 솔라리스 10 8/11 부터는 AMD 칩셋을 포함해 대부분의 AHCI 드라이버를 지원한다. 자세한 사항은 ahci(7D)를 읽어보자.
      • 솔라리스는 하드웨어에서 디스크 스케줄링이 지원되어야 성능이 좋아진다. IDE 컨트롤러와 SATA HDD 보다는, AHCI 컨트롤러와 SATA HDD가 훨씬 좋다는 의미다. 물론 SAN이나 SCSI기반 장치를 사용하면 더욱 좋다.
    • NIC
      • Intel과 BroadCom, 3Com, Realtek의 NIC이면 대부분 지원된다. 그렇지만 마더보드에 임베드되어 나오는 것은 간혹 지원되지 않는 경우가 있다.
      • 네트워크 가상화를 위해서는 GLDv3(Generic LAN Driver v3)와 호환되는 NIC가 필요하다. 드라이버 이름이 bge, e1000g, xge, nge, rge이면 지원될 것이다.
        bge: BroadCom 기가비트 이더넷 장치
        e1000g, igb: Intel의 기가비트 이더넷 장치
        xge: Neterion의 10기가비트 이더넷 장치
        nge: nVIDIA의 기가비트 이더넷 장치
        rge: Realtek의 기가비트 이더넷 장치
        이외에도 최근에 나온 솔라리스 설치본에 번들된 드라이버는 대부분 GLDv3를 지원한다. 각각의 드라이버에 대해서는 man페이지를 참고하자.
      • Wi-Fi는 솔라리스 11부터 지원한다. NIC지원만 놓고 본다면 솔라리스 11이 더 많은 장치를 지원한다.
      • 솔라리스에 번들되지 않은 NIC는 Free NIC drivers for Solaris에 방문해보자. 대부분의 마더보드 내장 NIC를 지원해준다.
    • Audio
      • 솔라리스 10 이하에서는 오디오 지원이 거의 전무하다. AC 97, Intel High Definition Audio 호환 사운드카드라면 지원 하는 경우도 있다. 그냥 바이오스에서 끄는 것이 좋다고 생각한다.
      • 솔라리스 10 이하에서 반드시 필요하다면 Open Sound System드라이버를 사용하는 것을 고려해보자.
      • 솔라리스 11부터 Open Sound System 4 가 번들되어있다. 따라서 대부분의 오디오 기기를 지원한다.
    • Printers
      • Lexmark는 자사가 제조한 대부분의 프린터에 대해 솔라리스 프린터 드라이버를 제공하고 있다.
      • Xerox 의 비즈니스 프린터라면 대부분 잘 지원해준다.
      • HP 의 경우 JetDirect 및 PostScript Driver를 이용해 네트워크로 연결할 수 있다. 솔라리스용 JetDirect 드라이버를 HP홈페이지에서 구할 수 있다.
      • 윈도우에서 공유중인 프린터가 PostScript 명령을 해석할 수 있으면 연결할 수 있다.
      • 솔라리스 11부터 CUPS(The Common UNIX Printing System)를 지원한다. 솔라리스 10까지 사용되었던 LP 프린팅 시스템을 대체할 것이다. CUPS는 대부분의 네트워크 프린터에서 지원되기 때문에, 프린터 사용에 문제가 없을 것으로 본다.
    • 서버 제조사별
      • 당연하지만, 오라클(썬 마이크로 시스템즈)에서 판매하는 x86서버는 솔라리스를 지원한다.
      • HP 나 IBM 에서 판매하는 x86서버도 대부분 솔라리스를 지원한다. 벤더에 확인해보라.
      • HP의 경우 Oracle Solaris Supported Matrix에서 각 서버의 지원 사항을 확인할 수 있다.

    HCL(Hardware Compatibility List)

    Solaris SPARC/x86 에서 호환되는 장치에 대해 Oracle Solaris OS: Hardware Compatibility Lists에 가서 검색해볼 수 있다. 사용자가 직접 올린 코멘트도 볼 수 있다.

    Solaris for x86 Device Support (추천)

    Solaris on x86 Platforms Device Support에서는 솔라리스 버전및 릴리즈 별로 호환되는 장치의 목록에 대해 볼 수 있다.

    시스템의 장치와 솔라리스와의 호환성 알아내기

    Oracle Device Detection Tool

    Oracle Device Detection Tool페이지에서 Oracle Device Detection Tool(sddtool_23.jnlp)을 다운로드 받는다. 자바로 되어있는 프로그램이라 자바 1.4.2 이상 지원되는 운영체제라면 모두 사용할 수 있다. 현재 사용하고 있는 시스템에서 솔라리스가 지원되는지 지원되지 않는지, 드라이버는 어디서 받을 수 있는지 알려준다.
    솔라리스: prtconf -pv > hcl.txt
    리눅스: lspci -vv -n > hcl.txt
    윈도우: reg query hklm\system\currentcontrolset\enum\pci /s > hcl.txt
    
    와 같은 커맨드를 실행시키면 hcl.txt가 생성된다. 이를 Oracle Device Detection Tool의 [파일 → 가져오기...] 에서 읽으면 아래와 비슷한 결과를 얻을 수 있다.


    ※ [atge]장치가 지원되지 않는 것으로 나와있으나, 아래의 주를 보면, 서드파티 드라이버를 다운로드 할 수 있는 주소가 나와있다.

    PCI ID

    ※ PCIDatabase.com이나 The PCI ID Repository에 가서 PCI 장치ID값을 입력하면 장치의 제조사와 종류및 이름을 알려 준다. 대부분의 PCI장치는 등록되어있기 때문에, 거의 확실하게 알 수 있다. PCI장치 아이디(벤더ID와 장치ID)는 바이오스에서 부팅시 알려주며, 다음과 같이 알아낼 수 있다.

    * 솔라리스 x86, x64
    root@wl ~ # /usr/X11/bin/scanpci
    ...
    pci bus 0x0002 cardnum 0x05 function 0x00: vendor 0x10b7 device 0x1700
     3Com Corporation 3c940 10/100/1000Base-T [Marvell]
    ...
    root@wl ~ #
    
    * 리눅스
    root@wllinux ~ # /sbin/lspci
    ...
    00:07.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)
    ...
    root@wllinux ~ # /sbin/lspci -n
    ...
    00:07.0 0200: 10ec:8139 (rev 10)
    ...
    root@wllinux ~ #
    
    ※ 솔라리스x86에서 각각의 PCI장치에 대해 붙어있는 드라이버의 이름을 보려면 아래와 같이 한다.
    root@wl ~ # prtconf -D
    ...
                pci10b7,9055, instance #0 (driver name: elxl)
                pci8086,70, instance #0 (driver name: iprb)
    ...
    root@wl ~ # 
    

    장치보기

    • sysdef

      시스템의 모든 장치(Pseudo장치 포함)에 대해 알려준다. 주로 -d옵션과 같이 사용된다.
      root@wl ~ # sysdef -d
      Node 'i86pc', unit #-1
              Node 'scsi_vhci', unit #0
              Node 'isa', unit #0
                      Node 'i8042', unit #0
                              Node 'keyboard', unit #0
                              Node 'mouse', unit #0
                      Node 'fdc', unit #0
                              Node 'fd', unit #0
                      Node 'lp', unit #0
              Node 'pci', unit #0
                      Node 'pci8086,1237', unit #-1 (no driver)
                      Node 'pci8086,7000', unit #-1 (no driver)
                      Node 'pci-ide', unit #0
                              Node 'ide', unit #0
                                      Node 'cmdk', unit #0
                              Node 'ide', unit #1
                                      Node 'sd', unit #0
                      Node 'display', unit #0
                      Node 'pci8086,1e', unit #0
                      Node 'pci80ee,cafe', unit #0
                      Node 'pci8086,0', unit #-1 (no driver)
                      Node 'pci106b,3f', unit #0
                      Node 'pci8086,7113', unit #-1 (no driver)
                      Node 'pci8086,265c', unit #0
                      Node 'pci1000,8000', unit #0
                              Node 'sd', unit #10
                              Node 'sd', unit #9
              Node 'iscsi', unit #0
              Node 'pseudo', unit #0
                      Node 'zconsnex', unit #1
      ...
                      Node 'llc2', unit #0
              Node 'options', unit #0
              Node 'agpgart', unit #0
              Node 'objmgr', unit #0
              Node 'xsvc', unit #0
              Node 'acpi', unit #-1 (no driver)
              Node 'used-resources', unit #-1 (no driver)
              Node 'cpus', unit #0
                      Node 'cpu', unit #-1 (no driver)
      root@wl ~ #
      
    • prtconf

      시스템 설정 사항을 출력해준다.
      root@wl ~ # prtconf
      System Configuration:  Sun Microsystems  i86pc
      Memory size: 768 Megabytes
      System Peripherals (Software Nodes):
      
      i86pc
          scsi_vhci, instance #0
          isa, instance #0
              i8042, instance #0
                  keyboard, instance #0
                  mouse, instance #0
              fdc, instance #0
                  fd, instance #0
              lp, instance #0
          pci, instance #0
              pci8086,1237 (driver not attached)
              pci8086,7000 (driver not attached)
              pci-ide, instance #0
                  ide, instance #0
                      cmdk, instance #0
                  ide, instance #1
                      sd, instance #0
              display, instance #0
              pci8086,1e, instance #0
              pci80ee,cafe, instance #0
              pci8086,0 (driver not attached)
              pci106b,3f, instance #0
              pci8086,7113 (driver not attached)
              pci8086,265c, instance #0
              pci1000,8000, instance #0
                  sd, instance #10
                  sd, instance #9
          iscsi, instance #0
          pseudo, instance #0
          options, instance #0
          agpgart, instance #0
          objmgr, instance #0
          xsvc, instance #0
          acpi (driver not attached)
          used-resources (driver not attached)
          cpus, instance #0
              cpu (driver not attached)
      root@wl ~ #
      
    • prtdiag

      시스템을 분석해 출력한다. x86보다는 스팍에서, 대형 서버에서 더 잘 출력해준다.
      root@wl ~ # prtdiag -v
      시스템 구성: Sun Microsystems  sun4u Sun Ultra 45 Workstation
      시스템 클록 주파수: 200 MHz
      메모리 크기: 1GB
      
      ==================================== CPUs ====================================
                     E$          CPU                    CPU
      CPU  Freq      Size        Implementation         Mask    Status      Location
      ---  --------  ----------  ---------------------  -----   ------      --------
      0    1600 MHz  1MB         SUNW,UltraSPARC-IIIi    3.4    on-line     MB/0
      
      ================================= IO 장치 =================================
      
      ============================== 메모리 구성 ==============================
      세그먼트 테이블:
      -----------------------------------------------------------------------
      기본 주소       크기       인터리브 계수 포함
      -----------------------------------------------------------------------
      0x200000000        1GB               1           BankIDs 0
      
      뱅크 테이블:
      -----------------------------------------------------------
                 물리적 위치
      ID       ControllerID  GroupID   크기       인터리브 방식
      -----------------------------------------------------------
      0        0             1         1GB             0
      
      메모리 모듈 그룹:
      --------------------------------------------------
      ControllerID   GroupID  Labels         Status
      --------------------------------------------------
      0              1        MB/DIMM2
      0              1        MB/DIMM0
      
      =============================== usb 장치 ===============================
      
      Name          Port#
      ------------  -----
      mouse           1
      keyboard        2
      
      =============================== usb 장치 ===============================
      
      Name          Port#
      ------------  -----
      hub             7
      
      =============================== 환경 상태 ===============================
      팬 상태:
      -------------------------------------------
      Location             Sensor          Status
      -------------------------------------------
      F0                   cpu0-fan        okay
      F2                   pci-fan         okay
      F3                   system-fan3     okay
      F4                   system-fan4     okay
      
      온도 센서:
      -----------------------------------------
      Location       Sensor              Status
      -----------------------------------------
      MB/0           cpu0-sensor         okay
      MB             mb-sensor           okay
      MB             adt7462-sensor      okay
      MB             lm95221-sensor      okay
      MB             fire-sensor         okay
      MB             lsi1064-sensor      okay
      FIOB           front_panel-sensor  okay
      MB             psu-sensor          okay
      
      ================================= HW 개정 =================================
      ASIC Revisions:
      -------------------------------------------------------------------
      Path                   Device           Status             Revision
      -------------------------------------------------------------------
      /pci@1e,600000         pciex108e,80f0   okay               3
      /pci@1f,700000         pciex108e,80f0   okay               3
      
      시스템 PROM 개정:
      ----------------------
      OBP 4.21.6 2006/03/31 15:02 Sun Ultra 45 Workstation
      POST 4.21.6 2006/03/31 15:24
      root@wl ~ #
      
    • dmidecode

      x86 시스템의 DMI영역을 디코드해 보여주는 유틸리티다. 리눅스 배포판에는 기본으로 포함되어있지만, 솔라리스에서도 설치해 사용할 수 있다. dmidecode
      root@wl ~/src # wget http://download.savannah.gnu.org/releases/dmidecode/dmidecode-2.11.tar.bz2
      root@wl ~/src # tar xvfj dmidecode-2.11.tar.bz2
      root@wl ~/src # cd dmidecode-2.11
      root@wl ~/src/dmidecode-2.11 # vi Makefile
      CC      = /usr/sfw/bin/gcc
      root@wl ~/src/dmidecode-2.11 # /usr/sfw/bin/gmake
      root@wl ~/src/dmidecode-2.11 # cp dmidecode /usr/local/bin
      root@wl ~/src/dmidecode-2.11 # dmidecode
      # dmidecode 2.11
      SMBIOS 2.4 present.
      60 structures occupying 1782 bytes.
      Table at 0x000F0000.
      
      Handle 0x0000, DMI type 0, 24 bytes
      BIOS Information
              Vendor: Phoenix Technologies, LTD
              Version: ASUS M2A-VM HDMI ACPI BIOS Revision 5001
              Release Date: 02/04/2010
              Address: 0xE0000
              Runtime Size: 128 kB
              ROM Size: 1024 kB
      
      ...
      
      End Of Table
      

    • CPU 마이크로 코드 업데이트

      마이크로 업데이트
      특별한 문제가 없다면 굳이 마이크로 코드를 업데이트 할 필요 없다. 솔라리스에서도 기본적으로 제공해준다.
      ※ 인텔 프로세서 마이크로 코드 데이터 파일 업데이트
      우선 인텔 다운로드 사이트에서 Processor Microcode Data File로 검색해 최신의 마이크로 코드 파일(microcode-*.tgz)을 다운로드 받는다.
      root@wl ~ # tar xvfz microcode-20110915.tgz
      root@wl ~ # mv microcode.dat /platform/i86pc/ucode/intel-ucode.txt
      root@wl ~ # ucodeadm -i /platform/i86pc/ucode/intel-ucode.txt
      root@wl ~ # ucodeadm -u /platform/i86pc/ucode/intel-ucode.txt
      root@wl ~ # reboot
      
      ※ AMD 프로세서 마이크로 코드 데이터 파일 업데이트
      우선 AMD 다운로드 사이트(OSRC: Microcode)에서 최신의 마이크로 코드 파일(amd-ucode-latest.tar)을 받는다.
      root@wl ~ # tar xvf amd-ucode-latest.tar
      root@wl ~ # cd amd-ucode-2011-01-11
      root@wl ~/amd-ucode-2011-01-11 # mv microcode_amd.bin /platform/i86pc/ucode/amd-ucode.bin
      root@wl ~/amd-ucode-2011-01-11 # ucodeadm -i /platform/i86pc/ucode/amd-ucode.bin
      root@wl ~/amd-ucode-2011-01-11 # ucodeadm -u /platform/i86pc/ucode/amd-ucode.bin
      root@wl ~/amd-ucode-2011-01-11 # reboot
      
    • IRQ 충돌 확인

      부팅 도중 아래와 같은 메시지가 나오는 경우가 있다.
      root@wl ~ # cat /var/adm/messages
      ...
      Nov 14 xx:xx:xx wl unix: [ID 954099 kern.info] NOTICE: IRQ19 is being shared by drivers with different interrupt levels.
      Nov 14 xx:xx:xx wl This may result in reduced system performance.
      ...
      
      IRQ가 충돌된다는 의미다. 그리고 시스템 성능을 낮추는 결과를 가져온다. 필요 없는 장치를 제거하거나, BIOS에서 IRQ를 수동으로 조절해 해결할 수 있다.

      솔라리스의 IRQ 할당 내역은 아래와 같이 확인할 수 있다. 솔라리스의 APIC테이블을 조회해서 알아온다.
      root@wl ~ # mdb -k
      Loading modules: [ ... ]
      > ::interrupts
      IRQ  Vector IPL Bus   Type  CPU Share APIC/INT# ISR(s)
      ...
      19   0x83   9   PCI   Fixed 0   2     0x0/0x13  uhci_intr, ata_intr
      ...
      > Ctrl+D
      root@wl ~ # 
      
      uhci(7D)는 USB 1.x 컨트롤러 드라이버고, ata(7D)는 ATA/SATA 하드디스크 드라이버의 이름이다. uhci, ata 와같은 단어의 의미는 man페이지를 참조하면 된다. 만약 이런 시스템에서 USB장치와 하드디스크를 동시에 사용하면 시스템 성능이 느려질 수 있다. IRQ 충돌 해결은 서버의 바이오스에서 해결해 줘야 한다. IRQ를 강제로 할당할 수 있는 마더보드라면 시도해볼 수 있다.

      SPARC 에서는 이런 문제가 없다.

    링크

    HCL 관련

    서드파티 드라이버 관련

    Twitter RSS IconTexto 올바른 XHTML 1.0 Transitional 입니다 올바른 CSS입니다!

    저작자 표시 비영리 변경 금지
    Posted by 考えの進化 RosaGigantea

    최근에 달린 댓글

    최근에 받은 트랙백

    글 보관함

    티스토리 툴바