반응형

- 출처

https://www.ibm.com/support/knowledgecenter/ko/SSYKE2_7.0.0/com.ibm.java.aix.70.doc/diag/problem_determination/aix_cmd_ps.html

https://www.ibm.com/support/knowledgecenter/ko/ssw_aix_71/com.ibm.aix.cmds4/ps.htm


프로세스의 ps 모니터링을 시작하려면 다음을 입력하십시오.


ps -fp <PID>


출력 내용은 다음과 같습니다.

UID     PID     PPID      C    STIME    TTY    TIME    CMD

user12  29730   27936    0    21 Jun   -      12:26   java StartCruise



이에 대한 설명은 다음과 같습니다.


UID

프로세스 소유자의 사용자 ID입니다. 로그인 이름은 -f 플래그 아래에 인쇄됩니다.


PPID

상위 프로세스 ID입니다.


PID

프로세스 ID입니다.


C

CPU 활용도로서, 프로세스가 실행 중이고 시스템 클럭 틱이 발생할 때마다 증분됩니다. 

스케줄러가 값을 매초 2로 나눠서 값이 줄어듭니다. 

sched_other 정책에서, CPU 활용도는 프로세스 스케줄링 우선순위를 결정하는 데 사용됩니다. 

큰 값이 CPU 집약적 프로세스를 나타내고 따라서 낮은 프로세스 우선순위를 갖는 반면, 작은 값은 입출력 집약적 프로세스를 나타내고 보다 높은 우선순위를 갖게 됩니다.

+

프로세스 또는 스레드의 CPU 사용량이며, 시스템 클럭이 째깍거리고 프로세스나 스레드가 실행 중임을 발견할 때마다 증가됩니다. 

값은 스케줄러에 의해 초당 2로 나뉘어져서 감소됩니다. sched_other 정책의 경우, CPU 사용량은 프로세스 스케줄링 우선순위를 판별하는 데 사용됩니다. 

큰 값은 CPU 집중 프로세스를 표시하며 결과적으로 프로세스 우선순위가 낮아지는 반면, 작은 값은 입출력 집중 프로세스를 표시하며 결과적으로 프로세스 우선순위가 높아집니다.



STIME

프로세스의 시작 시간입니다(시, 분, 초). 프로세스의 시작 시간은 ps 조회가 실행되기 24시간 이상 전에 시작됩니다(월, 일).


TTY

프로세스의 제어 워크스테이션입니다.


TIME

프로세스의 총 실행 시간입니다. 실행시간이 100분에 도달하면 시간이 mm:ss 또는 mmmm:ss 형식으로 표시되며, 이는 -o time 플래그를 사용할 때 표시된 형식과는 다릅니다


CMD

전체 명령 이름 및 해당 매개변수입니다.

아직 실행 중인 스레드를 확인하려면 다음을 입력하십시오







아직 실행 중인 스레드를 확인하려면 다음을 입력하십시오.


ps -mp <PID> -o THREAD


출력 내용은 다음과 같습니다.

    USER        PID   PPID     TID ST  CP PRI SC    WCHAN        F     TT BND COMMAND

    user12    29730  27936       - A    4  60  8        *   200001 pts/10   0 java StartCruise

           -      -      -   31823 S    0  60  1 e6007cbc  8400400      -   0 -

           -      -      -   44183 S    0  60  1 e600acbc  8400400      -   0 -

           -      -      -   83405 S    2  60  1 50c72558   400400      -   0 -

           -      -      -  114071 S    0  60  1 e601bdbc  8400400      -   0 -

           -      -      -  116243 S    2  61  1 e601c6bc  8400400      -   0 -

           -      -      -  133137 S    0  60  1 e60208bc  8400400      -   0 -

           -      -      -  138275 S    0  60  1 e6021cbc  8400400      -   0 -

           -      -      -  140587 S    0  60  1 e60225bc  8400400      -   0 -



이에 대한 설명은 다음과 같습니다.


USER

프로세스를 실행 중인 사용자의 이름입니다.


TID

각 스레드의 커널 스레드 ID입니다.


ST

다음과 같은 스레드의 상태입니다.


1) O - 존재하지 않습니다.

2) R - 실행 중입니다.

3) S - 휴면 상태입니다.

4) W - 스왑되었습니다.

5) Z - 취소되었습니다.

6) T - 중지되었습니다.


CP

스레드의 CPU 활용도입니다.


PRI

스레드의 우선순위입니다.


SC

일시중단 수입니다.


ARCHON

대기 채널입니다.


F

플래그입니다.


TAT

제어 중인 터미널입니다.


BAND

스레드가 바인드되는 CPU입니다.



반응형
반응형


출처 : http://www.antsys.co.kr/data/vi_editor.htm


자주 쓰는 내역에 대해서 잘 정리가 되어 있어서 자주보려고 포스팅 합니당.


vi 시작 명령어

vi filename

vi +18 filename

vi +/"문자열" fn

vi -r filename

view filename

파일 열기, 작성

18행으로 파일 열기

"문자열"의 처음발생 단어부터 열기

손상된 파일 회복

읽기 전용으로 파일 열기

vi 커서 이동 명령어

h()

j()

k()

l()

왼쪽으로 커서 이동

아래로 커서 이동

위로 커서 이동

오른쪽으로 커서 이동

w

e

b

Enter

Back space

Space Bar

^

$

H

M

L

숫자G

오른쪽 한 단어의 끝 부분으로 커서 이동

오른쪽 한 단어의 앞 부분으로 커서 이동

왼쪽 한 단어의 앞 부분으로 커서 이동

한 행 아래로 커서 이동

한 문자 왼쪽으로 커서 이동

한 문자 오른쪽으로 커서 이동

행의 맨 왼쪽으로 커서 이동

행의 맨 오른쪽으로 커서 이동

화면의 맨 위로 이동

화면의 중간으로 이동

화면의 맨 아래로 이동

숫자만큼 지정한 줄로 커서 이동

Ctrl + f

Ctrl + b

Ctrl + d

Ctrl + u

Ctrl + e

Ctrl + y

한 화면 위로 이동

한 화면 아래로 이동

반 화면 위로 이동

반 화면 아래로 이동

한 줄씩 위로 이동

한 줄씩 아래로 이동

문자,행 삽입 명령어

a (종료:ESC)

A (종료:ESC)

i (종료:ESC)

I (종료:ESC)

o (종료:ESC)

O (종료:ESC)

커서 오른쪽에 문자 삽입

커서 오른쪽, 행의 끝에 문자 삽입

커서 왼쪽에 문자 삽입

커서 왼쪽, 행의 처음에 문자 삽입

커서 아래에 행 삽입

커서 위에 행 삽입

텍스트 변경 명령어

cw (종료:ESC)

cc (종료:ESC)

C (종료:ESC)

s (종료:ESC)

S (종료:ESC)

r

r-Enter

J

xp

~

u

U

:u

.

단어 변경

행 변경

커서 오른쪽의 행 변경

커서가 위치한 문자열 대체

커서가 위치한 라인의 문자열 대체

커서 위치 문자를 다른 문자로 대체

행 분리

현재 행과 아래 행 결합

커서 위치 문자와 오른쪽 문자 교환

문자형(,소문자) 변경

이전 명령 취소

행 변경 사항 취소

이전의 최종 행 취소

이전 최종 명령 반복

텍스트 삭제 명령어

x

5x

dw

dd

5dd

db

D

:5,10d

커서가 있는 문자 삭제

커서가 있는 위치부터 5개의 문자를 삭제

현재 커서가 있는 한단어 삭제

커서가 있는 라인 삭제

커서가 있는 라인부터 5개의 라인 삭제

커서의 위치에서 꺼꾸로 한단어 삭제

커서 오른쪽 행 삭제

5-10번째 행 삭제

복사 및 이동 명령어

yy

Y

P

p

 

:1,2 co 3

:4,5 m 6

yank 또는 복사

yank 또는 복사

yank되거나 삭제된 행 현재 행 위에 삽입

yank되거나 삭제된 행 현재 행 아래에 삽입

1-2행을 3행 다음으로 복사

4-5행을 6행 위로 이동

행 번호 설정 명령어

:set nu

:set nonu

행 번호 표시

행 번호 숨기기

행 찾기 명령어

G

21G

Ctrl + G

파일의 마지막 행으로 가기

파일의 21번째 행으로 가기

현재의 filenameline수를 알려줌

탐색 및 대체 명령어

/검색할 문자열/

?검색할 문자열?

n

N

:g/search-string/s//replace-string/gc

:s/str/rep/

:1,.s/str/rep/

:%s/str/rep/g

 

:.$/aaa/bbb/

오른쪽 아래 방향으로 문자열 검색

왼쪽 위 방향으로 문자열 검색

문자열의 다음으로 계속 검색

문자열의 이전으로 계속 검색

각 발생 탐색 후 확인하고 대체

 

 

현재 행의 strrep로 대체

1부터 현재 행의 strrep로 대체

파일 전체 strrep로 전부 대체

커서의 위치로부터 화일의 끝까지 있는 모든 aaabbb로 대체

화면정리 명령어

Ctrl + l

불필요한 화면정리 후 다시 표시

파일 명령어

:r filename

:34 r filename

커서 다음에 파일 삽입

파일을 34번째 행 다음에 삽입

보관 및 종료 명령어

:w

:w filename

:wq

ZZ

:q!

q

e!

 

변경사항 보관

버퍼를 파일로 보관

변경사항 보관 후 vi 종료

변경사항 보관 후 vi 종료

변경사항 보관하지 않고 종료

수정한 파일을 저장하지 않고 vi 종료

수정한 것을 무시하고 다시 편집상태로





출처 : http://gyuha.tistory.com/157


정리가 잘 되어 있습니다. 다른분 블로그의 내용을 가져왔습니다.


VIM 명령어 정리

 

 

1. 저장 및 종료

명령어

설명

:w

저장

:w file.txt

file.txt 파일로 저장

:w » file.txt

file.tx파일에 덧붙여서 저장

:q

vi 종료

:q!

vi 강제 종료

ZZ

저장 후 종료

:wq!

강제 저장 후 종료

:e file.txt

file.txt파일을 불러옴

:e

현재 파일을 불러옴

:e#

바로 이전에 열었던 파일을 불러 옴

 

 

2. 입력모드 전환

a

커서 위치 다음칸부터 입력

A

커서 행의 맨 마지막부터 입력

i

커서의 위치에 입력

I

커서 행의 맨 앞에서 부터 입력

o

커서의 다음행에 입력

O

커서의 이전 행에 입력

s

커서 위치의 한글자를 지우고 입력

cc

커서위치의 한 행을 지우고 입력

 


3. 이동

h

왼쪽으로 이동

l

오른쪽으로 이동

j

아래행으로 이동

k

위 행으로 이동

w 또는 W

다음 단어의 첫 글자로 이동

b 또는 B

이전 단어의 첫 글자로 이동

e 또는 E

단어의 마지막 글자로 이동

<CR>

다음행 글자로 이동

^

그행의 첫 글자로 이동

$

그 행의 마지막 글자로 이동

+

다음 행의 첫 글자로 이동

-

위 행의 첫 글자로 이동

(

이전 문장의 첫 글자로 이동

)

다음 문장의 첫 글자로 이동

{

이전 문단으로 이동

}

다음 문단으로 이동

H

커서를 화면 맨 위로 이동

z<CR>

현재 행을 화면의 맨우로 이동

M

커서를 화면 중안으로 이동

z.

현재 행을 화면의 중앙으로 이동

L

커서를 화면 최하단으로 이동

z-

현재 행의 화면의 최하단으로 이동

[n]H

커서를 위에서 n행으로 이동

[n]L

커서를 아래에서 n행으로 이동

ctrl+u

반 화면 위로 스크롤

ctrl+d

반 화면 아래로 스크롤

ctrl+b

한 화면 위로 스크롤

ctrl+f

한 화면 아래 스크롤

gg 또는 1G

문서의 맨 처음으로 이동

G

문서의 맨 마지막 행으로 이동

[n]G 또는 :[n]

n행으로 이동


 

4. 삭제

x 또는 dl

커서 위치의 글자 삭제

X 또는 dh

커서 바로 앞의 글자 삭제

dw

현재 위치부터 스페이스 까지 삭제

diw

현재 위치에 있는 단어 삭제

dd

커서가 있는 행을 삭제

[n]dd

현재 커서 부터 아래 n번째 줄까지 삭제

dj

현재 커서와 아래 줄 삭제

[n]dj

현재 커서 부터 아래 n+1번째 줄까지 삭제

dk

현재 커서와 윗로 n+1번째 줄까지 삭제

[n]dk

현재 커서와 줄 삭제

D 또는 d$

현재 커서가 있는 위치부터 행 끝까지 삭제

d0 또는 d^

현재 커서가 있는 위치부터 행 시작 까지 삭제

 

 

5. 복사 & 붙여넣기

yy 또는 Y

커서가 있는 한 행 복사

p

현재 커서에 붙여 넣기, 행 복사 일 경우 아래 줄에 붙여넣음.

P

현재 커서위치의 앞행에 붙여 넣기, 행 복사일 경우에는 줄에 붙여 넣음

[n]yy 또는 [n]Y

커서가 위치한 이후로 n행 복사

[n]p

n번 만큼 붙여넣기 반복

 

 

6. 블록 지정

v

블록 지정

V

줄단위 블록 지정

ctrl+v(윈도우에서는 ctrl+q)

비쥬얼 블록 지정

블록 지정 중 명령

y

블록 복사 하기

r

치환

d

지정 블록 지우기

U

대문자로 바꾸기

u

소문자로 바꾸기

~

대소문자 전환

J

행 합침

:

선택 영역에 대하여 ex명령

<

행 앞에 탭 제거

>

행 앞에 탭 삽입

 

 

7. 문자열 탐색 및 치환

/[문자열]

문자열 탐색

:s/old/new

현재 행의 처음 old new로 교체

:s/old/new/g

현재 행의 모든 old new로 교체

:10,20s/old/new/g

10행부터 20행까지 모든 old new로 교체

[블록지정중]:s/old/new/g

지정 블록 내에서 모든 old new로 교체

:-3,+4s/old/new/g

현재 커서 위치에서 위로 3행 아래로 4행까지의 old new로 교체

:%s/old/new/g

문서 전체에서 old new로 교체

:%s/old/new/gc

문서 전체에서 old new로 확인하며 교체

:g/pattern/s/old/new/g

pattern이 있는 모든 행의 old new로 교체

 

 

8. vim 정규 표현식

^

행의 첫 문자([]안에서는 not의 의미)

$

행의 끝

.

아무 문자나 한 문자 의미

\|

or의 의미

[ ]

[]사이의 문자 중 하나

\{min,max\}

min이상 max이하 반복됨

*

앞의 내용이 0번 이상 반복됨

\+

앞의 내용이 1번 이상 반복됨

\<

단어의 시작

\>

단어의 끝

\n

새 행 문자

\t

탭 문자

 

 

9. vim 확장 정규 표현 문자열

\i

변수 지정에 사용되는 문자들 [0-9A-Za-z]

\I

\i와 같지만 숫자는 제외

\k

keyword로 사용하는 문자 [_\.\-0-9A0Za-z]

\f

파일 이름으로 사용하는 문자

\p

프린트 가능한 문자

\P

\p와 같지만 숫자는 제외

\s

whitespace character(공백과 탭)

\S

non-whitespace character

\d

숫자 [0-9]

\D

숫자가 아닌 문자 [^0-9]

\x

16진수 숫자 [0-9A-Fa-f]

\X

16진수 숫자가 아닌 문자 [^0-9A-Fa-f]

\o

8진수 숫자 [0-7]

\O

8진수 숫자가 아닌 문자 [^0-7]

\w

영문 단어의 시작에 사용되는 문자 [0-9A-Za-z-]

\W

영문 단어에서 사용되지 않는 문자 [^0-9A-Za-z-]

\h

영문 단어의 시작에 사용되는 문자 [A-Za-z-]

\H

영문 단어의 시작에 사용 되지 않는 문자 [^A-Za-z-]

\a

모든 알파벳 [A-Za-z]

\A

알파벳이 아닌 문자 [^A-Za-z]

\l

소문자 [a-z]

\L

소문자가 아닌 문자 [a-z]

\u

대문자 [A-Z]

\U

대문자가 아닌 문자 [^A-Z]

\e

Esc

\t

Tab

\r

캐리지 리턴

\b

백스페이스

\n

새 행

 

 

10. POSIX 문자 클래스

문자 클래스

내용

[:alnum:]

알파벳과 숫자 [A-Za-z0-9]

[:alpha:]

알파벳 [A-Za-z]

[:cntrl:]

제어 문자

[:blank:]

탭과 공백 문자

[:digit:]

숫자 [0-9]

[:graph:]

제어문자와 공백 문자를 제외한 문자

[:lower:]

소문자 [a-z]

[:upper:]

대문자 [A-Z]

[:print:]

제어문자를 제외한 문자, 즉 프린터 할 수 있는 문자

[:punct:]

[:graph:]문자 중 [:alnum:]을 제외한 문자. ex)!,@,#,$,%,^....

[:space:]

화이트스페이스 ex)공백, , 케리지 리턴, 새행, 수직탭, 폼필드

[:xdigit:]

16진수

 

 

11. 파일 버퍼

:files 또는 :ls 또는 :buffers

버퍼 목록 나열

:b[n]

n번 버퍼로 이동

:bd[n] 또는 :bw[n]

n번 버퍼를 삭제 (n이 없으면 현재의 버퍼를 삭제)

:bp[n]

이전 버퍼로 이동,n 붙이면 n번만큼 이전 버퍼로 이동

:bn[n]

이후 버퍼로 이동,n 붙이면 n번만큼 이후 버퍼로 이동

:sb[n]

창을 수평분할 하여 n번 버퍼를 로드

:bf

첫 번째 버퍼로 이동

:bl

마지막 버퍼로 이동

양식의

 

12. Tab

Vim 7.0부터 추가된 기능

:tabnew

새로운 탭을 열기

:tabnew b.txt

b.txt가 존재하면 열고, 없으면 새로 만들어서 연다

:tabf b.txt

b.txt가 존재하면 열고, 없으면 에러 발생

:tabn[n]

다음 탭을 열기,n 붙이면 n번 만큼 이동

:tabp[n]

이전 탭을 열기,n 붙이면 n번 만큼 이동

:tabs

탭 목록 보기

:tabclose

탭을 닫기

:tabfirst

첫번째 탭을 열기

:tablast

마지만 탭을 열기

:tabdo %s/old/new/g

모든 탭에 적용을 원할 때 (모든탭에서 old new로 변경)

 


13. 다중 창 관련 명령

명령모드

ex모드

결과

창생성

CTRL-W s

:[N]sp[plit]

현재 파일을 두 개의 수평 창으로 나눔

CTRL-W v

:[N]vs[plit]

현재 파일을 두 개의 수직 창으로 나눔

CTRL-W n

:new

새로운 수평 창 생성

CTRL-W ^ 또는 CTRL-W CTRL-^

 

수평 창으로 나누고 이전 파일의 오픈

CTRL-W f

 

창을 수평으로 나누고 커서 위치의 파일 오픈

CTRL-W i

 

커서 위치의 단어가 정의된 파일을 오픈

창삭제

CTRL-W q

:q[uit]!

현재 커서의 창을 종료

CTRL-W c

:close

현재 커서의 창 닫기

CTRL-W o

:on[ly]

현재 커서의 창만 남기고 모든 창 삭제

창이동

CTRL-W h

 

왼쪽 창으로 커서 이동

CTRL-W j

 

아래쪽 창으로 커서 이동

CTRL-W k

 

위쪽 창으로 커서 이동

CTRL-W l

 

오른쪽 창으로 커서 이동

CTRL-W w

 

창을 순차적으로 이동

CTRL-W p

 

가장 최근에 이동한 방향으로 이동

CTRL-W t

 

최상위 창으로 이동

CTRL-W b

 

최하위 창으로 이동

창이동

CTRL-W r

 

순착으로 창의 위치를 순환

CTRL-W x

 

이전 창과 위치를 바꿈

CTRL-W H

 

현재창을 왼쪽 큰화면으로 이동

CTRL-W J

 

현재창을 아래쪽 큰화면으로 이동

CTRL-W K

 

현재창을 위쪽 큰화면으로 이동

CTRL-W L

 

현재창을 오른쪽 큰화면으로 이동

창 크기 조정

CTRL-W =

 

창의 크기를 모두 균등하게 함

CTRL-W _

 

수평 분할에서 창의 크기를 최대화

CTRL-W |

 

수직 분할에서 창의 크기를 최대화

CTRL-W [N]+

:res[ize] +N

창의 크기를 N행 만큼 증가

CTRL-W [N]-

:res[ize] -N

창의 크기를 N행 만큼 감소

CTRL-W [N]>

 

창의 크기를 오른쪽으로 N칸 만큼 증가

CTRL-W [N]<

 

창의 크기를 오른쪽으로 N칸 만큼 감소

다중창 사용의 경우 대부분 붙여서 사용하는 명령어는 CTRL을 같이 눌러도 똑같은 역활을 하는 경우가 많다.
) CTRL-W j CTRL-W CTRL-J와 같다.

 

 

14. 마킹마킹위치로 이동

m[a-z0-9]

파일내에서 마킹, 현재 버퍼내에서만 이동 함 예)ma

m[A-Z]

전체영역에서 마킹, 다른 파일로도 이동 함.

`[A-Za-z0-9]

마킹된 위치로 돌아감 예)`a

’[A-Za-z0-9]

마킹된 행의 처으으로 이동함. )‘a

직전에 커서가 위치하던 행의 처음

``

직전의 커서 위치로 이동

’”

이전에 vim으로 현재 파일을 닫았을 때 커서가 있던 행의 처음으로 이동

`"

이전에 vim으로 현재 파일을 닫았을 때 커서가 있던 위치로 이동 


반응형
반응형



참고 : http://webinformation.tistory.com/86



해당 오류는 패키지 dependency 에 대해 지우고 다시 설치하면 됩니다.


[root@localhost ~]# yum shell > remove [지워야 하는 패키지명] > install [다시 설치해야 하는 패키지명] > run



반응형
반응형

참고 : https://m.blog.naver.com/PostView.nhn?blogId=idrukawa&logNo=220870293694&proxyReferer=https:%2F%2Fwww.google.co.kr%2F


Yum update errno 14 http error 404: not found


yum 으로 update를 진행하다보면 해당 에러가 발생할 수 있는데 이런 경우에 아래의 경로에 모든 파일을 다른곳으로 이동하고
daum 의 repository 정보로 변경하여 진행하면 됩니다.

root]# cd /etc/yum.repos.d

root]# vi daum.repo


[base]

name=CentOS-$releasever - Base

baseurl=http://ftp.daum.net/centos/7/os/$basearch/

gpgcheck=1

gpgkey=http://ftp.daum.net/centos/RPM-GPG-KEY-CentOS-7

 

[updates]

name=CentOS-$releasever - Updates

baseurl=http://ftp.daum.net/centos/7/updates/$basearch/

gpgcheck=1

gpgkey=http://ftp.daum.net/centos/RPM-GPG-KEY-CentOS-7

 

[extras]

name=CentOS-$releasever - Extras

baseurl=http://ftp.daum.net/centos/7/extras/$basearch/

gpgcheck=1

gpgkey=http://ftp.daum.net/centos/RPM-GPG-KEY-CentOS-7

 

[centosplus]

name=CentOS-$releasever - Plus

baseurl=http://ftp.daum.net/centos/7/centosplus/$basearch/

gpgcheck=1

gpgkey=http://ftp.daum.net/centos/RPM-GPG-KEY-CentOS-7


// repo clear 후 로딩

// base, centosplus, extras, updates 가 출력되면 정상

// 위 리스트가 출력되지 않으면 daum.repo 만 남겨놓고 다른 repo는 삭제해보길 바람.


root]# yum clean all

...

Cleaning repos: base centosplus extras updates

Cleaning up everything


root]# ]# yum repolist all

반응형
반응형



1. 설치된 내역 확인 

# rpm -qa | grep ssh

openssh-server-6.61p1-31.el7.x86_64

openssh-client-6.61p1-31.el7.x86_64



2. 설치가 안되어 있으면 yum install 이나 rpm 으로 설치 진행

# yum install openssh*



3. ssh 설정 파일 수정 (해당 부분 주석 해제)

# vi /etc/ssh/sshd_config

Port 22 



4. ssh 서비스 구동

# systemctl start sshd.service



5. 방화벽 22번 포트 오픈 및 재시작

# firewall-cmd --zone=public -add-port=22/tcp --permanent

# firewall-cmd --reload

# systemctl restart firewalld.service



6. 포트 오픈 내역 확인

# netstat -na | grep tcp | grep 22






반응형
반응형


yum 을 통해 외부망에서 repository를 설정할 때.



1. yum 캐쉬 정보 삭제.

# yum clean all 


This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
>> 관련 내용 무시



2. EPEL repository 설정

wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm



rpm -ivh epel-release-latest-7.noarch.rpm 

> 경고 무시

> /etc/yum.repo.d 에 아래 내역들 추가 되었는지 확인

>> epel-testing.repo  epel.repo



3. REMI repository 설정

wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm


# rpm -Uvh remi-release-7.rpm 

/etc/yum.repo.d 

>> remi-php70.repo   remi-php71.repo  remi-safe.repo      remi.repo (파일 내역은 리눅스 버전에 따라 차이가 있는 것 같아 보임)



4. 필요한 패키지 yum install [패키지 명] 으로 설치 진행.





반응형
반응형

au-mallocdebug.zip


AIX에서 메모리 릭을 디텍팅 하기 위해서는 다양한 툴들을 사용할 수 있지만 아래의 방법도 있으니 참고.


출처 : http://ieggstory.blogspot.kr/2007/12/aix-memory-leak.html




환경변수를 이용하여 malloc, free 를 추적할 수 있다.


MALLOCDEBUG=report_allocations,output:/tmp/alloc_output.txt

ex)
export MALLOCTYPE=debug
export MALLOCDEBUG=report_allocations,stack_depth:5,output:/tmp/alloc_output.txt

프로그램이 실행되는 동안 위의 환경변수가 설정되어 있는 경우 /tmp/alloc_output.txt
파일로 alloc, free 되는 정보가 출력된다. 프로그램이 실행되는 동안은 alloc,
free 할때마다 그 기록을 메모리상에 가지고 있다가 종료가 되면 해제되지
않은 alloc 정보만 /tmp/alloc_output.txt 로 출력된다.

더 자세한 사항은 AIX 5L Version 5.3 General Programming Concepts: Writing
and Debugging Programs 에 나와 있다. 





출처 : http://www.ibm.com/developerworks/aix/library/au-mallocdebug.html#list3


useage : format_mallocdebug_op.sh alloc_output.txt


위에서 출력된 출력 파일을 사용하여 memory leak에 대해서 일반 사용자가 보기 편하게 변경해주는 쉘 스크립트가 있다(다운로드 가능)

또한 OS 환경 변수 뿐만 아니라 관련 옵션들에 대해서도 위의 출처에 자세히 나와있음.


사용해보도록 하자.

반응형
반응형



출처 : http://blog.naver.com/PostView.nhn?blogId=bumsukoh&logNo=110114643995






안녕하세요!  이번에는 IBM AIX의 내부적인 Memory 관리 방법(Not human but machine doing that)과 모니터링 방법(Not machine but human doing that)에 대해 설명드리도록 하겠습니다.

 

저의 경우 매번 시스템 튜닝이나 모니터링하면서도 항상 헤갈리게 하는 Memory Management 부분입니다. 아마 모니터링 툴마다 보여주는 수치들의 의미때문에 그런것 같은데요.

전혀 모르것도 아닌것이 그렇다고 아는것도 아닌것이 아무 애매모호한 경우가 너무 많아서 이번에 쫑찍어 볼려고 합니다.

위에서 제가 설명드린 "모니터링 툴마다 보여주는 수치들의 의미가 다른부분"의 대표적인 예가 이런 경우일 것 같은데요.

vmstat의 page in, page out과 topas의 page in, page out  부분일텐데요. 잘 아실지 모르지만 이 두개가 의미하는 내용은 완전히 다릅니다. 모르셨던 분들은 황당하실텐데요.

 vmstat의 page in, page out은 말그대로 paging space(swap이라 보시면 되겠네요)로의 page in, page out 입니다. 즉 프로세스의 메모리 정보가 file system으로 내려가거나, 다시 메모리로 로딩하거나 하는 것이지요.

그런데 topas의 page in, page out은 file system과 매핑된 즉 file과 매핑된 page들에 대한 page in, page out에 대한 부분입니다. 이 중에는 paging space로의 page in, page out은 제외됩니다.

즉 Memory segment중 Persistent segment와 Client segment에 대한 page들이 in, out 되는 것이죠.

이렇게 다른 내용을 같은 내용으로 이해 한다면 잘못된 판단을 할 수 있겠죠.

이러한 이유로 AIX 내부적으로 Memory를 관리하는 방법과 모니터링에 대한 정확한 방법을 한번 정리하는 것이 이번 포스트의 목적입니다.

 

자, 그럼 AIX 내부적인 Memory 관리 방법에 설명하도록 하겠습니다.

 

AIX 뿐 아니라 모든 Unix 시스템은 가용한 Physical memory 보다 더 많은 양의 메모리 address를 효율적으로 할당할 수 있도록 하기 위해 Virtual memory를 사용합니다. 이건 Unix 시스템에 대해 아시는 분들은 모두 아시는 부분입니다. 문제의 발단은 여기서 부터 시작됩니다.

Virtual Memory를 사용함으로써 이를 효과적으로 관리하기 위한 각종 효율적인 방법들이 필요하게 됩니다. 그러면서 굉장히 복잡한 Memory management mechanism이 생겨나게 됩니다.

 

AIX 내부적인 Memory를 관리하는 방법을 알기 위해서는 다음의 기능들에 대해 숙지하고 있어야 합니다.

- Paging space 란 ?

paging space는 현재 Virtual memory상에는 존재하지만 현재 access 되고 있지 않는 데이터를 저장하기 위해 disk 공간으로 할당된 logical volume입니다.

이러한 logical volume을 일반적으로 우리는 paging space또는 swap space라고 부릅니다.

paging space라는 용어의 개념을 잘 알고 있어야 뒤에 나오는 각종 내용들에 대해 혼란이 없게됩니다.

System의 Physical memory(RAM)의 여유공간의 양이 적으면, 현재 사용하고 있지 않은 프로그램(프로세스)이나 데이터는 다른 activity 작업에게 메모리를 할당하기 위해 paging space로 이동되게 됩니다. 뭐.. 이 내용은 우리가 잘 알고 있는 swap에 대한 내용이라 할 수 있습니다. 그래서 paging space가 작게되면 프로세스들이 유실되게 되고, 이렇게 되면 paging space가 고갈되게 되면 System panic이 발생할 수 있습니다.

 

이러한 문제를 감지하게 되면 새로운 volume을 생성하여 paging space로 할당하거나 기존의 volume의 크기를 증가시켜 문제를 해결 할 수 있습니다.

 

이러한 paging space에는 paging space를 할당하기 위한 다양한 정책(Late, Early, Deferred등)과 paging space의 부족 시 paging space회수를 위한 다양한 방법(npswarn, npskill,nokilluid등)등이 있습니다. 해당 부분들은 필요 시 설명하도록 하겠습니다.

 

예전에는 작은 Physical memory 시스템에 과중한 업무들을 구동하다 보니 paging space에 대한 page in, page out이 지속적으로 발생하여 성능상의 문제가 빈번히 있었지만,

 

요즘은 Physical memory의 가격보다 안정적인 시스템 운영을 위해 충분한 Physical memory를 처음부터 확보하고 시스템을 운영하기 때문에 Paging space가 부족하여 System에 크게 문제되는 경우는 별로 없는것 같습니다. 

 

- Memory Segments 란 ?

AIX는 다양한 memory segment간에 기능들이 구분됩니다.

AIX의 VMM을 이해하기 위해 persistent,working 그리고 client segment들간의 차이점을 이해하는 것이 중요합니다.

 

Persistent segment는 disk상의 permanent storage location을 가집니다. Data나 execution programe의 file은 persistent segment에 매핑됩니다.

 

Working segment는 프로세스가 사용하는 동알 존재하는 일시적인 것입니다. 따라서 permanent disk storage에 위치를 가지지 않습니다.

프로세스 stack, data 영역이 working segment와 shared library text segment에 매핑됩니다.

 

Client segment는 remote file system에 위치한 permanent location을 네트워크를 통해 저장됩니다. CD-ROM과 JFS2 page들은 client segment 입니다.

 

Memory segment는 private으로 유지되거나 processor간 공유될 수 있습니다.

 

- VMM(Virtual memory manager) 란 ?

VMM은 System과 application에서 요청하는 메모리를 할당하는 역할을 담당합니다.

VMM을 통해 할당되는 segment들의 단위를 page라고 부릅니다. 이러한 page는 Physical memory나 필요 시까지 disk(paging space)에 위치됩니다.

이렇게 Physical memory와 disk(paging space)에 저장된 page들을 VMM이 관리하게 됩니다. 그래서 virtual memory의 양은 system의 실제 physical 메모리의 크기를 넘어 사용될 수 있습니다.

VMM의 기능 측면에서는 다음과 같은 성능 향상을 위한 기능을 가지고 있습니다.

- paging으로 인한 processor의 사용과 disk bandwidth를 최소화함

- 프로세스의 paging으로 인한 응답속도 저하를 최소화홤

 

AIX에서는 virtual memory segment는 4kb(4096byte) 단위로 page로 나뉘어집니다.

VMM은 이러한 page들에 대해 사용가능한 free list를 유지 관리합니다.

VMM은 현재 Physical memory상의 page들에 대해 free list로 재할당해야 할 page들을 찾기 위해 page-replacement algorithm을 사용합니다.

이 page-replacement algorithm은 virtual memory segment인 working segment와 비교하여 persistent 존재 비율 및 repaging등을 고려해야 합니다.

 

VMM은 할당되지 않은 page들의 최소양을 유지하기 위해 free 공간을 늘리고 이 공간을 free list에 할당하기 위한 page out과 page steal을 사용합니다.

 

- Paging mechanism 에 대해

Paging mechanism은 VMM이 page들의 free list를 관리하기 위한 방법입니다. 위에서 VMM이 page-replacement algorithm을 사용한다고 말씀드렸는데요. 이러한 것들이 Paging mechanism에 대한 하나의 방법입니다.

 

VMM은 안정적인 page fault를 가능하게 하기 위해 free memory page frame의 리스트를 유지합니다. 이러한 list가 바로 우리가 알고 있는 free list입니다.

 

여기서 page fault는 virtual memory address는 가지고 있으나 현재 physical memory에 해당 page가 없을 경우 발생합니다.

이러한 page fault는 보통 다음과 같이 두가지 유형이 있습니다.

new page fault로써 가장 처음 참조되어 physical memory가 없는 경우와 이미 page out된 page인 경우입니다. 즉 page fault가 발생하면 free list가 있는 경우 자동으로 해당 page에 대한 메모리를 바로 할당하게 됩니다.

 

메모리의 free list page의 수가 작게되면 page stealer가 발생하게 됩니다.

이 page stealer는 steal 대상 page들을 찾기 위해 Page Frame Table(이하 PFT) entry들을 평가하는 mechanism입니다.

PFT는 참고되고 있는 page와 수정된 page들을 표시하는 flag들을 가지고 있습니다.

Page stealer가 참고되고 있는 PFT내의 page를 찾게되면 해당 page를 steal하지 않습니다.

오히려 참조 flag를 reset 하게 됩니다. 다음번에 page stealer가 PFT에서 해당 page를 검사할때 해당 page가 참조되고 있지 않다면 해당 page는 steal 됩니다.

그리고 page stealer가 처음부터 참조되지 않은 page들 만나게 되면 바로 해당 page들을 steal됩니다.

 

PFT의 page가 참조되지 않고 modify flag가 설정되어있으면 해당 page가 memory에 위치한 후 변경되었다는것을 page stealer에게 표시하게 됩니다. 이러한 경우는 page steal 전에 page out이 호출됩니다. Page들이 working segment의 부분이라면 pageing space에 기록될것이며, 반면에 persistent segment의 부분이라면 disk의 permanent 위치에 기록될것입니다.

 

프로세스가 종료하게 되면 해당 프로세스의 working storage는 해제되며 메모리의 page들도 free가 되어 free list로 반환됩니다.  반면 프로세스에 의해 open된 file들은 메모리에 남아 있을수 있습니다.

 

- Memory load control mechanism 에 대해

Active virtual memory page들의 수가 Physical memory page들을 초과할 경우, Physical memory page내에 위치할 수 없는 page들을 위해 paging space를 사용하게 됩니다.

Application이 page out된 page들에 접근하게 되면 VMM은 paging space로 부터 해당 page들을 physical memory page로 로드하게 됩니다.

이때, free physical memory page의 수가 작게되면 paging space에서 page로 로딩하기 전에 physical memory내의 다른 또 다른 free page가 필요하게 됩니다.

VMM이 free 대상의 page를 찾게되면, 강제로 해당 page들은 paging space로 page out됩니다.

심각한 경우 VMM은 paging space로 부터 physical memory로 다른 page를 로딩하기 전에 항상 paging space로 page를 page out 해야 할 필요가 생길 수 있습니다.

이러한 경우는 thrashing이라 부릅니다. 이러한 thrashing 상황에서는 프로세스들은 dispatche(run queue에서 cpu 할당받음)되자 마자 page fault에 직면하게 됩니다. 그러면 프로세스들은 어떠한 중요 절차도 수행하지 못하게 될뿐 아니라 system의 성능은 떨어지게 됩니다.

 

OS들은 이러한 thrashing 상황이 시작되는지에 대한 감지하기 위한 memory load control mechanism을 가지고 있습니다.

thrashing이 감지되면 system은 active process들을 suspend 시키기 시작하고 새로운 process의 시작을 지연시키게 되며 이러한 작용이 thrashing으로 빠지는 것을 방지합니다.

 

- Page replacement algorithm 에 대해

free list 상의 page 수가 설정한 값(minfree)보다 낮을 경우 page replacement algorithm은 physical memory page를 free 시키기를 시도하게 됩니다. 이러한 page replacement algorithm은 free list상의 page수의 최대값(maxfree+8)보다 free list의 page가 확보될때까지 지속됩니다.

이러한 page replacement algorithm 수행의 기준이 되는 minfree, maxfree값을 적정하게 설정하는 것을 중요한 부분입니다.

보통 minfree값의 기본은 maxfree보다 8 작은 값이거나 memory pool(보통 CPU당 메모리 SUBPOOL이 있음)만큼 작은 값입니다.

maxfree는 VMM의 page steal을 중지시키게 하는 기준을 결정합니다. maxfree값은 다음과 같이 결정될 수 있습니다.

maxfree = lesser of (number of memory pages / 128) --> IBM 가이드 문서 내용입니다.

이러한 값들은 system의 memory를 모니터링하여 필요 시 재설정을 통해 성능을 튜닝할 필요가 있습니다.

 

 

그럼 AIX 내부적인 Memory 관리 방법이 이해되었다면, 이제는 모니터링 하는 방법에 대해 알아보겠습니다.

주요하게 사용하는 Memory 모니터링 툴에 대해 설명하겠습니다.

 

- vmstat

가장 효과적으로 사용할 수 있는 툴입니다.

vmstat에 나오는 메모리 항목을 위주로 설명드리도록 하겠습니다.

vmstat은 크게 kthr, memory, page, faults, cpu 이렇게 5개 대항목으로 정보를 출력합니다.

이 중 Memory 관리와 관련된 부분은 memory, page 부분이 되겠습니다.

 


 

각 항목에 대해 알아보겠습니다.

- avm

Active Virtual Memory를 말하며, access되는 virtual page의 수를 표시합니다. 때로 이것을 available memory로 잘못알고 있는 경우가 있습니다.

- fre

free list의 크기를 표시합니다. 실제 physical memory의 많은 영역이 file system data를 cache로 활용되기도 합니다. 따라서 해당 크기가 작다고 해서 꼭 비정상적인 것은 아닙니다.

- re

초당 reclaime한 page수입니다. 해당 page가 free list에 있고, 재할당되지 않았을때 해당 page에 page fault는 초기화를 위한 신규 I/O request가 없으므로 reclaim이 고려됩니다.

- pi

page in request의 수입니다. page fault에 의해 paging space에 paging되어 있는 page가 physical memory로 page되는 수입니다.

초당 지속적으로 5 page 보다 많은 값이 보이면 문제가 있는 것으로 볼 수 있습니다.

- po

page out 처리된 page 수입니다. 초당 paging space로 이동한 page들의 수를 말합니다. 해당 page는 더 많은 memory가 요청될때 VMM에 의해 paging space로 page out됩니다. page out된 page는 필요 시 page in 될 수 있습니다.

page out이 높으면 minfree와 maxfree 값과 paging allocation policy값인 PSALLOC값을 확인할 필요가 있습니다.

- fr

free 된 page의 수입니다. VMM이 메모리를 필요로 할때 VMM의 page-replacement algorithm이 steal 대상 page들을 결정하기 위해 PFT(Page Frame Table)을 scan하도록 합니다. page들이 마지막 scan이후에 참조되지 않았다면 해당 page는 steal될 수 있습니다.

- sr

page-replacement algorithm에 의해 scan이 이루어진 page를 표시합니다. page steal이 발생하면 physical memory내의 page들은 page steal 대상인지를 확인하기 위해 scan됩니다.

메모리의 과대사용을 확인하기 위해 fr에 비해 sr의 비율이 높은지 확인해야함 높은 비율은 page stealer가 steal하기 위한 메모리를 찾기위해 작업 부하가 발생하는 것을 의미합니다.

- cy

page-replacement algorithm이 steal할 page를 찾기위해 PFT(Page Frame Table) 전체를 scan한 횟수를 나타냅니다.

이 값이 0 보다 크면 심각한 memory부족을 의미합니다.

page-replacement algorithm은 maxfree+8 값 만큼 free list page를 확보하기 위해 PFT를 scan하게 되는데 이때 PFT를 한번 모두 scan한 후에도 free list page의 수가 maxfree+8 보다 작으면 계속적으로 page steal을 위한 scan이 발생하며 이미 scan한 page들을 만나게 되면 cy값은 1씩 증가하게 됩니다.

이렇게 scan하다 maxfree+8 값만큼 steal이 발생하여 free list가 증가하면 그때 비로서 cy값이 0으로 설정됩니다.

 

- topas

topas도 시스템 모니터링을 위해 유용한 툴입니다. 처음에 말씀드렸듯이 memory에 관련해서는 그 항목이 vmstat과 조금 달라 확실히 의미하는 바를 알고 있어야 혼선이 없습니다.

 


 

항목 중 paging, memory, paging space 부분이 memory와 관련된 항목입니다.

- Faults

faults 수입니다.

- Steals

초당 VMM에 의해 steal된 page수입니다.

- PgspIn

Paging space로 부터 읽어들인 page 수입니다. (일반적인 page in 입니다.)

- PgspOut

Paging space로 write된 page 수입니다. (일반적인 page out 입니다.)

- PageIn

초당 읽어들인 page 수입니다. (paging space로부터의 page in을 제외한 모든 page in, persistent segment에 대한 page in이 포함됩니다.)

- PageOut

초당 write 된 page 수입니다. (paging space로 page out을 제외한 모든 page out, ㅔpersistent segment에 대한 page out이 포함됩니다.)

- Sios

VMM에 의해 발생된(page stealer) 초당 input/output page의 요청 수입니다.

- Real

Megabyte 단위의 실제 physical memory의 크기를 보여줍니다.

- %Comp

Computational page에 할당된 real memory 량입니다.

- %Noncomp

Non-computational page에 할당된 real memory 량입니다.

- %Client

Remotely mounted file들에 대해 cache로 사용되는 메모리의 량입니다.

 

- svmon

svmon은 현재 시스템의 메모리 사용량에 대한 정보를 상세하게 확인할 수 있습니다.



 

위와 같이 전체 시스템의 memory 사용율이 나오게 됩니다.

 

- memory
     real memory의 사용율에 대한 정보 출력됩니다.
     - size : real memory frame의 수입니다.
     - inuse : page들을 포함하고 있는 frame의 수입니다.
     - free : 모든 memory pool의 free frame의 수입니다.
     - pin : pinned page를 포함하고 있는 frame의 수입니다.
     - virtual : working segment를 위한 system virtual space에 할당된 page 수입니다.
   - pg space
     쉽게 swap 대한 정보 출력합니다.
     - size : 전체 paging 공간 크기입니다.
     - inuse : 사용중인 paging 공간입니다.
   - pin
     고정 영역에 대한 상세 정보입니다.
     - work : 작업 세그먼트 페이지를 포함하는 page 수입니다.
     - pers : 영구 세그먼트 페이지를 포함하는 page 수입니다.
     - clnt : 클라이언트 세그먼트 페이지를 포함하는 page 수입니다.

- vmo

vmo는 memory관련된 튜닝을 위해 VMM setting을 위한 툴입니다.

vmo 에는 다양한 tuning parameter가 있으며, 이러한 parameter를 활용하여 시스템에서 메모리 관리에 관련된 효과적인 방법을 제시할 수 있습니다.

 

 

굉장히 다양한 parameter들이 있습니다. 흔히 접하게 되는 값들로는 maxclient%, maxfree, maxperm%, maxpin%, minfree, minperm%등이 있습니다.

vmo의 값 parameter에 대해서는 따로 포스팅하기로 하겠습니다.

 

이렇게 기본적으로 AIX에서 내부적인 memory관리를 이해하기 위한 내용과 모니터링 부분에 대해 알아보았습니다.

위의 내용을 기본으로 좀더 세부적인 메카니즘을 알아가 보시는 것도 재미있을것 같습니다.

반응형
반응형


참조    : http://ssambback.tistory.com/entry/Unix-or-Linux-%EB%B3%84-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%97%90%EB%9F%AC%EB%93%A4


프로그램 에러 시그널들 (SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGABRT) - 매우 중요 ★★★★★

다음의 시그널들은 심각한 프로그램의 에러가 운영체제나 컴퓨터 자체에 의해 검출되었을 때 발생 된다. 
일반적으로, 이들 시그널 모두는 당신의 프로그램이 심각하게 깨져있고, 에러가 포함된 그 실행을 계속할 아무런 방법이 없음을 지적한다.


어 떤 프로그램들은 프로그램의 에러 시그널로 인해서 종료되기전에 그들을 깨끗하게 처리한다. 예를 들어, 터미널 입력의 반향을 끈(tnun off) 프로그램들은 다시 반향을 켤 목적으로 프로그램 에러 시그널들을 처리할 것이다. 핸들러는 시그널을 위한 디폴트 동작을 정하고 그 동작을 함으로써 끝날 것이다. 만일 프로그램이 시그널 핸들러를 가지지 않았다면, 프로그램은 그 시그널로 인해서 종료될 것이다.

SIGFPE 시그널은 심각한 산술적 에러를 보고한다. 그 이름이 "floating-point exception"에서 유래된것이라 할지라도, 이 시그널은 실제로는 모든 산술적 에러들에 작용한다. 만일 어떤 프로그램이 어떤 위치에 정수 데이터를 저장하고 그 데이터에 플로팅-포인트 명령을 사용한다면, 이것은 그 프로세서가 데이터를 플로팅-포인트 수로써 인식할 수 없기 때문에 종종 "유용하지 않은 연산"의 원인이 된다.

SIGILL 시그널의 이름은 "비합법적인 명령(illegal instruction)"에서 유래되었다
그것은 쓸모없거나 특권이 부여된 명령어를 실행하려 했다는 의미이다. 
오직 유용한 명령어만이 발생된 C 컴파일러에서, SIGILL은 전형적으로 실행 가능 파일이 훼손되었거나, 당신이 데이터를 실행하려 시도했다는 것을 지적한다. 

후자의 상황이 발생되는 일반적 상황으로는 함수를 위한 포인터가 있을 것이라고 예상된 곳에서 유용하지 않은 오브젝트를 파싱하거나, 자동 배열의 끝을 넘어서 기록을 하고( 또는 자동 변수를 위한 포인터와 유사한 문제들) 스택 프레임의 반환 어드레스 처럼 스택에서 
다른 데이터의 훼손과 같은 문제들이 있다.

SIGSEGV 시그널은 할당된 메모리의 범위를 벗어나는곳에서 읽거나, 쓰기를 시도할 때 발생 된다. 
(실제로, 그 시그널들은 프로그램이 충분한 영역을 할당받지 못할 때 시스템 메모리 보호 메커니즘에 의해서 발생한다.) 

그 이름은 "segmentation violation"의 약자이다. 
SIGSEGV 상황이 발생되는 가장 일반적인 방법은 비참조 되는 널( defeferencing a null) 이나 초기화되지 않은 포인터에 의한 것이다. 

널 포인터는 주소 0으로 참조되고, 대부분의 운영체제는 이 주소가 정확하게 유용하지 않음을 확실히 
하기 때문에 
비참조 널 포인터는 SIGSEGV가 발생될 것이다. 
(어떤 운영체제는 주소가 0인 메모리도 유용하고, 비참조 널 포인터는 그들 시스템상에서는 시그널을 발생하지 않는다.) 
비초기화된 포인터에서는, 유용하지 않거나, 유용하더라도 임의의 주소들을 갖게된다. 
SIGSEGV 상황이 얻어지는 다른 일반적 방법은 배열에 포인터를 사용했을 때 그 배열의 끝을 체크하기를 실패했을 때이다. 

SIGBUS 시그널은 유용하지 않은 포인터가 비참조되었을 때 발생 된다. 
SIGSEGV 처럼, 이 시그널은 초기화되지 않은 포인터를 비참조 한 것의 결과이다. 
두 시그널의 차이점은 SIGSEGV는 유용한 메모리에서 유용하지못한 억세스를 지적하고, 
SIGBUS는 유용하지못한 주소를 억세스 하는 것을 지적한다. 
특별하게, SIGBUS 시그널은 4개로 나누어지지 않은 주소에 4-단어 정수로 참조하는것처럼, 
부적당한 포인터가 비참조 됨으로써 발생한다. 
(각종 시스템은 주소 정렬은 위한 자신만의 필요조건을 갖는다.) 이 시그널의 이름은 "bus error"의 약자이다.

SIGABRT 시그널은 프로그램 그 자체와 abort가 호출되었음을 보고함으로써 발생되는 에러를 지적한다.

2. 종료 시그널 (SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGKILL) - 중요 ★★★★

이들 시그널들은 이런 저런 방법으로 프로세스를 종료함을 알리기위해 사용된다. 
그들은 완전히 다른 목적을 위해 사용되기 때문에 다른 이름을 가졌고, 프로그램은 그들은 다르게 취급하기를 원할 것이다.
이들 시그널들은 처리하기 위한 이유는 보통 당신의 프로그램이 실제로 종료되기전에 적당하게 처리할 수 있도록 하기 위한 것이다. 
예를 들어, 당신은 상황정보를 저장하고, 임시 파일들을 지우고, 이전의 터미널 모드를 반환하기를 원할수도 있다. 
그와 같이 핸들러(handler)는 발생된 시그널을 위한 디폴트 동작을 지정하고 그리고 그 시그널을 다시 발생시킴으로써 종료할 것이다. 
이것은 만일 프로그램이 핸들러를 가지지 않았더라도, 그 시그널로 인해서 프로그램이 종료될 것이다.

SIGHUP ("hang-up") 시그널은 사용자 터미널의 단절을 보고하기 위해 사용되어지는데, 
아마도 네트웍이나 전화선 연결이 끊어졌기 때문이다. 

SIGINT("program interrupt") 시그널은 사용자가 INTR 문자를 (보통 C-c)을 입력했을 때 보내어진다. 

SIGQUIT 시 그널은 다른 키_QUIT 문자, 보통 C-\_에 의해서 제어된다는 것을 제외하고는 SIGINT와 유사하고, 그 프로세스가 종료 될 때 프로그램 에러 시그널처럼 코어 파일을 작성한다. 당신은 사용자에 의해 "검출된" 프로그램 에러 상황으로 이들을 생각할 수 있다. 

SIGTERM 시그널은 프로그램을 종료하는데 사용하는 포괄적인 시그널이다. SIGKILL과 달리, 이 신호는 블록되어진고, 처리되어지고 무시되어질 수 있다.

SIGKILL 시그널은 즉각적인 프로그램 종료를 일으키기 위해서 사용되어진다. 이 시그널은 처리되거나, 무시되거나 할 수 없고, 그 결과는 항상 치명적이 된다. 이 시그널은 블록하는것도 불가능하다. 

3. 알람 시그널 (SIGALRM, SIGVTALRM, SIGPROF) - 알아도 그만.. 몰라도 그만..  ^^;;

그들 시그널은 타이머의 경과를 지적하는데 사용되어진다. 
그들 시그널을 위한 디폴트 동작은 프로그램을 종료를 일으키는 것이다. 
이 디폴트 동작은 거의 유용하지 않다. 
그 들 시그널을 사용하는 대부분의 방법은 어느 경우에 맞는 핸들러 함수들을 요구하는 것이다.

SIGALRM 시그널은 전형적으로 실제또는 클럭 시간을 계산한 타이머의 경과를 지적한다. 
예를 들어 alarm 함수에의해 사용되어진다. 

SIGVTALRM 시그널은 전형적으로 현재 프로세스에 의해 사용된 CPU시간을 계산하는 타이머의 경과를 지적한다. 
그 이름은 "virtual time alarm"의 약자이다.

SIGPROF 시그널은 현재의 프로세스에 의해 사용된 CPU 시간과, 
프로세스를 대신하여 시스템에의해 사용된 CPU시간의 둘을 계산한 타이머의 경과를 지적하는데 사용된다. 
타이머가 자원의 프로파일링을 위한 도구로써 사용되어지므로, 시그널의 이름이 SIGPROF이다.

4. 비동기 입/출력 시그널 (SIGIO, SIGURG)

이 절에 설명된 시그널들은 비동기 입/출력 도구들과 함께 사용되어진다. 
당신은 어떤 특정한 파일 기술자가 그들 시그널을 발생시키도록 하기 위해서 fcntl을 호출함으로써 명백한 동작을 취하도록 해야한다.

SIGIO 시그널은 파일기술자가 입력 또는 출력을 수행할 준비가 되어있을 때 보내어진다. 
대부분의 운영체제에서, 터미널과 소켓만이 SIGIO를 발생시킬 수 있다. 
보통의 파일들을 포함한 다른 종류들은 당신이 그들에게 요청했을지라도 SIGIO신호를 발생시키지 않는다.

SIGURG 시그널은 소켓에 도착한 데이터가 "긴급"하거나 범위를 벗어 났을 때 보내어진다.

5. 작업 제어 시그널 (SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU) - 중요 ★★★★

이들 시그널은 작업 제어를 지원하기 위해서 사용되어진다. 
만일 당신의 시스템이 작업 제어를 지원하지 않는다면 시그널들은 발생되어지거나, 처리될 수는 없지만 매크로들은 정의되어있다. 
당신이 실제로 작업이 어떻게 제어되는지를 이해할 수 없다면 그들 시그널을 그대로 방치할 것이다.

SIGCHLD 시그널은 자식 프로세스들중의 하나라도 종료되거나 멈출 때마다 부모 프로세스에게 보내어진다. 
이 시그널을 위한 디폴트 동작은 그것을 무시하는 것이다. 

당신은 프로세스가 계속되도록 하기 위해서 SIGCONT 신호를 보낼 것이다.
SIGCONT 시그널을 위한 디폴트 동작은 만일 그 프로세스가 멈추었다면 그 프로세스를 계속하도록 만드는 것이고 
그렇지 않다면 그것을 무시하는 것이다. 
대부분의 프로그램에서는 SIGCONT를 처리할 아무런 이유가 없다. 
그들은 전에 멈추었었음을 인식함이 없이 계속 실행되고 있다고 가정한다. 

SIGSTOP 시그널은 프로세스를 멈춘다. 그것은 처리되거나, 무시되거나 블록될 수 없다.

SIGTSTP 시그널은 상호 작용하는 멈춤 신호이다. SIGSTOP와는 달리 이 신호는 처리되거나 무시되어질 수 있다. 
당신의 프로그램에서 프로세스가 멈추었을 때 파일이나 시스템 테이블을 안전한 상황으로 만들어놓을 특별한 필요가 있다면 
이 신호를 처리할 수 있다.

한 프로세스가 배경 작업으로써 실행되고 있는 동안 사용자의 터미널로부터 읽을 수 없다. 
배경 작업에 속한 어느 프로세스가 터미널로부터 읽으려 시도할 때, 그 작업에 속한 모든 프로세스는 SIGTTIN 신호를 받는다. 
이 시그널을 위한 디폴트 동작은 그 프로세스를 멈추는 것이다. 

SIGTTOU 시그널은 배경 작업에 속한 프로세스가 터미널에 출력하려 시도하거나 그 터미널 모드를 설정하려 시도할 때 발생 된다. 
다시 말하면 디폴트 동작은 그 프로세스를 멈추는 것이다. 
프로세스가 멈추어있을 동안, SIGKILL 시그널과 SIGCONT시그널을 제외하고는 어느 다른 시그널들은 배달되어질 수 없다.

SIGKILL 시그널은 항상 프로세스의 종료를 유발하고 블록되거나 무시될 수 없다. 
당신이 SIGCONT 시그널을 무시하거나 블록할 수 있지만, 그것은 만일 그 프로세스가 멈추어져있다면 프로세스가 계속되도록 한다. 
프로세스에게 보낸 SIGCONT 시그널은 아직 미해결인채로 남아있는 멈춤 시그널을 프로세스가 버리도록 한다. 
이와 비슷하게, 어떤 프로세스에서 아직 미해결인채로 남아있는 SIGCONT 시그널은 멈춤 시그널이 도착했을 때 버려진다. 
고아가 되어버린 프로세스 그룹에 있는 한 프로세스에게 SIGTSTP, SIGTTIN, 또는 SIGTTOU 시그널을 보내면 그것은 처리되지도 않고, 
그 프로세스는 멈추어 지지도 않는다. 
그것을 계속할 아무런 방법이 없는 부당하게 되어버린 프로세스를 멈추게 하라. 
운영체제에 의존하지 말고당신이 무언가를 사용해서 멈추게 하라. 어떤 시스템은 아무런 일도 하지 않을 것이다. 
다른 시스템들은 대신에 SIGKILL 또는 SIGHUP와 같은 시그널들을 배달할 것이다. 

6. 잡다한 시그널 (SIGUSR1 ~ SIGUSR22)

그들 시그널은 다양한 다른 상활들을 보고하기 위해서 사용되어진다. 이들의 디폴트 동작은 프로세스가 종료되도록 하는 것이다.

SIGPIPE 시그널은 읽는 프로세스가 없는 상황에서의 PIPE에 대한 쓰기시 발생한다.

SIGUSR1 과 SIGUSR22 시그널들은 당신이 원하는 어떤 방법을 사용하지 못하도록 한다. 그들은 프로세스간 통신을 위해서 유용하다. 

그들 시그널을 보통 심각하기 때문에 당신은 그 시그널을 받은 프로그램에서 그들은 위한 시그널 처리를 해야할 것이다.



출처: http://ssambback.tistory.com/entry/Unix-or-Linux-별-프로그램-에러들 [Rehoboth.. 이곳에서 부터]

반응형
반응형


출처 : http://sthyun.tistory.com/entry/EWOULDBLOCKEAGAIN

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


넌블럭킹 소켓

socket() 으로 생성되는 소켓은 기본값으로 Blocking 소켓이다. 하지만 이미 생성된 소켓을 fcntl() 함수를 사용하여 nonblocking socket으로 변경 가능하다.

※ Blocking Socket(B)/Nonblocking Socket(N)

(여기서 errno는 errno.h를 인클루드해야 이용할수 있다.)

- read

  • B : read 버퍼가 비어있을때 block
  • N : read 버퍼가 비어있을때 -1 return, errno==EWOULDBLOCK/EAGAIN

* Blocking socket의 경우에 read 버퍼에 존재하는 데이터의 크기가 read시 요청한 데이터의 크기보다 작은 경우라도 read 버퍼에 존재하는 데이터만큼 리턴되며 block 되지 않음.

- write

  • B : write 버퍼가 꽉 차있을때 block
  • N : write 버퍼가 꽉 차있을때 -1 return, errno==EWOULDBLOCK/EAGAIN

- accept

  • B : backlog( 현재의 connection 요청 큐 )가 비어있을때 block
  • N : backlog( 현재의 connection 요청 큐 )가 비어있을때 -1 return, errno==EWOULDBLOCK/EAGAIN

- connect

  • B : connection이 완전히 이루어질때까지 block
  • N : connection이 완전히 이루어지 않더라도 곧바로 return. 나중에 getsockopt로 connection이 완전히 이루어졌는지 확인가능.

※ Nonblocking 소켓의 장점/단점

  • 장점 : 멀티스레드를 사용하지 않고도 다른 작업을 할 수 있다.
  • 단점 : 프로그램이 복잡해지며, CPU 사용량이 증가한다.
  • 멀티쓰레드 기반에서 nonblock socket 을 사용하면 cpu 사용량이 엄청나게 증가한다.
    이건 성능상의 차이는 확연한데 gprof 를 돌려도 안나온다. 멀티쓰레드 환경에서는 절대 사용하지말것.

※ Nonblocking 소켓으로 만드는 방법 : fcntl()함수를 이용한다.

int flag;

flag = fcntl( sock_fd, F_GETFL, 0 );

fcntl( sock_fd, F_SETFL, flag | O_NONBLOCK );

파일 입력과 출력
이 절은 파일 기술자상의 기본 입력과 출력 명령을 수행하기 위한 함수들을 설명하고 있다:

read, write, 그리고 lseek. 이들 함수들은 헤더파일 'unistd. h'에 선언되어 있다.

read함수는 기술자 filedes의 파일로부터 size 바이트를 읽고, 그 결과를 버퍼에 저장한다. (이것은 문자 스트링이 필요하지 않고 그곳에는 부가된 널 종료문자가 없다)


ssize_t read (int filedes, void *buffer, size_t size)


반환 값은 실제로 읽은 바이트의 수이다.

이것은 size보다 적을수도 있다


예를 들어, 만일 파일에 남겨진 바이트의 수가 적거나 즉시 유용한 바이트의 수가 적은 경우 등이 있다.

정확한 동작은 파일의 종류가 무엇인지에 따라 의존한다.

size 바이트보다 덜 읽는 것은 에러가 아님을 기억하라.

0의 값은 파일의 끝을 지적한다. ( 만일 size 인수의 값이 0인 경우를 제외하고. . ) 이것은 에러로 간주하지 않는다.


만일 당신이 파일의 끝인 상태에서 read를 호출하면, 그것은 0을 반환하는 것 외에 아무 일도 하지 않는다.

만일 read가 적어도 한 문자를 반환한다면, 당신이 파일의 끝에 도달했는지를 알 수 있는 아무런 방법이 없다.

그러나 만일 당신이 끝에 도달해 있었다면 다음 read의 호출은 0을 반환해서 파일의 끝임을 지적해줄 것이다.

에러가 발생한 경우에, read는 -1을 반환한다.


다음의 errno는 이 함수에서 정의된 에러의 상황이다.


EAGAIN 일반적으로, 즉시 유용한 입력이 없을 때, read는 입력을 기다린다.

그러나 만일 그 파일에서 O_NONBLOCK가 설정되면 read는 아무런 데이터도 기다리지 않고 즉시 반환하고, 이 에러를 보고한다.

호환성 노트 : BSD Unix의 대부분의 버전은 이것을 위한 다른 에러코드를 사용한다:

EWOULDBLOCK. GNU 라이브러리에서는, EWOULDBLOCK은 EAGAIN의 다른 이름이다. 그래서 당신이 어떤 이름을 사용해도 문제가 발생되지 않는다.


어떤 시스템들은, 특별한 문자 파일로부터 데이터의 큰 덩어리를 읽으려 할 때, 만일 커널(kernal)이 당신의 것을 담을 수 있는(to lock down the user's pages), 충분한 물리적 메모리를 얻을 수 없는 경우에 EAGAIN의 에러를 내고 실패했음을 지적한다.

디바이스가 사용자의 메모리 영역을 직접적으로 억세스 하는 것이 제한되어 있는 것은 그들은 커널내부의 분리된 버퍼를 사용하기 때문이다. 그것에 터미널들은 포함되지 않는다,


EBADF 

filedes 인수에 주어진 것이 유용한 파일 기술자가 아니다.


EINTR 

read가 입력을 기다리고 있는 동안 시그널에 의해 인터럽트 되어졌다.


EIO 

많은 디바이스들, 그리고 디스크 파일들을 위하여, 이 에러는 하드웨어 에러를 지적한다.

EIO는 또한 제어 중인 터미널로부터 배경 프로세스가 읽기를 시도하고, SIGTTIN의 신호가 아무런 동작도 하지 않고 보내짐에 의해 멈춘 프로세스의 일반적 동작에 대해 발생한다.


이것은 만약 신호가 블록되어지거나 무시되거나, 프로세스 그룹이 부모 프로세스를 잃어 버렸다면 발생되어질 것이다.


ssize_t write (int filedes, const void *buffer, size_t size)


write함수는 기술자 filedes 파일에 버퍼에 있는 size 바이트의 데이터를 쓰는 함수이다. 버퍼에 있는 데이터는 문자 스트링과 널 문자가 필요하지 않다. 반환 값은 실제로 쓰여진 바이트들의 개수이다.이것은 보통은 size와 같지만, 더 적을수도 있다 ( 예를 들어, 만일 물리적 매체가 채워져 있는 경우 ). 에러가 발생하면 write는 -1을 반환한다.


다음의 errno는 이 함수에서 정의한 에러상황이다.


EAGAIN 

일반적으로 write 명령하에서 블록 쓰기 동작은 완벽하다.

그러나 만일 그 파일에서 O_NONBLOCK 플래그가 설정되어 있다면, 그것은 어떤 데이터도 쓰지 않고 곧바로 반환하고, 에러를 발생한다.

그 상황에 대한 하나의 예는 프로세스가 출력하려는 블록을 STOP 문자를 받아들임으로 인해 출력이 일시 중단되고, 흐름제어를 지원하는 터미널 디바이스에 쓰기를 시도할 때 발생한다.


EWOULDBLOCK. 

GNU 라이브러리에서는, EWOULDBLOCK은 EAGAIN의 다른 이름이다.

그래서 당신이 어떤 이름을 사용해도 문제가 발생되지 않는다.

어떤 시스템들은, 특별한 문자 파일로부터 데이터의 큰 덩어리를 쓰려 할 때, 만일 커널(kernal)이 당신의 것을 담을 수 있는( to lock down the user's pages ), 충분한 물리적 메모리를 얻을 수 없는 경우에 EAGAIN의 에러를 내고 실패했음을 지적한다.

디바이스가 사용자의 메모리 영역을 직접적으로 억세스 하는 것이 제한되어 있는 것은 그들은 커널내부의 분리된 버퍼를 사용하기 때문이다. 그것에 터미널들은 포함되지 않는다,


EBADF 

filedes 인수는 유용한 파일 기술자가 아니다.


EFBIG 

파일의 크기가 그 실행에서 지원할 수 있는 것보다 크다.


EINTR 

write 오퍼레이션은 명령이 완전히 수행될 때까지 기다리는 동안 신호에 의해 인터럽트 되어졌다.


EIO 많은 디바이스들, 그리고 디스크 파일들을 위하여, 이 에러는 하드웨어 에러를 지적한다.

EIO는 또한 제어 중인 터미널로부터 배경 프로세스가 읽기를 시도하고, SIGTTIN의 신호가 아무런 동작도 하지 않고 보내짐에 의해 멈춘 프로세스의 일반적 동작에 대해 발생한다.

이것은 만약 신호가 블록되어지거나 무시되거나, 프로세스 그룹이 부모 프로세스를 잃어 버렸다면 발생되어질 것이다. ENOSPC 디바이스가 차 있다.


EPIPE

이 에러는 어느 프로세스에 의해서 읽기 위해 개방되지 않는 파이프나 FIFO에 쓰려 시도할 때 반환된다.

이것이 발생될 때, SIGPIPE 신호를 프로세스에 보낸다.


당신이 EINTR 실패를 방지하기 위해 조정하지 않았다면, 당신은 실패한 write의 호출에 대해서 errno를 체크해야할 것이다. 그리고 만일 errno가 EINTR 이라면, 그냥 간단하게 다시 호출해주면 된다.

이것을 하는 쉬운 방법으로 매크로 TEMP_FAILURE_RETRY 가 있다. 다음처럼:

nbytes = TEMP_FAILURE_RETRY (write (desc, buffer, ount));

write 함수는 fputc처럼 스트림에 쓰는 모든 함수들에 기본적으로 포함되어 있다. 

반응형
반응형

출처 : http://openhunis.tistory.com/


[Oracle] 오라클 Table/Index Analyze 통계 확인 및 실행방법

ANALYZE란?

ANALYZE는 인덱스, 테이블, 클러스터의 통계정보를 생성 한다.

ANALYZE가 생성한 통계정보들은 비용기준(Cost-based)의 옵티마이저가 가장 효율적인 실행계획을 수립하기 위해 최소비용을 계산할 때 사용 된다.

각 오브젝트의 구조를 확인하는 것과 체인(Chain) 생성 여부를 확인할 수 있으므로 시스템의 저장공간 관리를 도와준다.

ANALYZE는 다음과 같은 통계정보를 생성하여 데이터 사전에 저장 한다.
  • - 테이블 : 총 로우의수, 총 블럭의 수, 비어있는 블럭에 쓰여질 수 있는 빈 공간의 평군, 체인이 발생된 로우의 수, 로우의 평균 길이
  • - 인덱스 : 인덱스의 깊이(Depth), Leaf block의 개수, Distinct Key의 수, Leaf Blocks/Key의 평균, Data blocks/key의 평균, Clustering Factor, 가장 큰 key 값, 가장 작은 key 값
  • - 컬럼 : Distinct한 값의 수, 히스토그램 정보
  • - 클러스터 : Cluster Key당 길이의 평균
문법

object-clause : TABLE, INDEX, CLUSTER중에서 해당하는 오브젝트를 기술하고 처리할 오브젝트 명을 기술 한다.

operation : operation 옵션에는 다음 3가지중 한가지 기능을 선택할 수 있다.

  • COMPUTE : 각각의 값들을 정확하게 계산 한다. 가장 정확한 통계를 얻을 수 있지만 처리 속도가 가장 느리다.
                        (테이블에 저장되어 있는 모든 행을 대상으로 통계정보를 수집한다.)
  • ESTIMATE : 자료사전의 값과 데이터 견본을 가지고 검사해서 통계를 예상 한다. COMPUTE보다 덜 정확 하지만 처리속도가 훨씬 빠르다
                        (오라클 서버의 자동화 알고리즘에 의해 데이터를 추출하여 통계정보를 수집한다.)
  • DELETE : 테이블의 모든 통계 정보를 삭제 한다
                     (수집되어 있는 통계정보를 삭제한다.)

정보수집

주기적인 ANALYZE 작업을 수행 시켜 주어야 한다.

테이블을 재생성 하거나, 새로 클러스터링을 한 경우, 인덱스를 추가하거나 재생성한 경우, 다량의 데이터를 SQL이나 배치 애플리케이션을 통해 작업한 경우 ANALYZE를 수행 시켜 주는 것이 좋다.

사용자는 USER_TABLES, USER_COLUMNS, USER_INDEXS, USER_CLUSTER 등의 자료사전 뷰를 통해 정보를 확인할 수 있다

테이블을 ANALYZE 시킨다면 거기에 따르는 인덱스들도 같이 실시하는 것이 좋다.


오라클에서는 20,000건 이하의 행수를 가진 데이터에 대해서는 COMPUTE STATISTICS절의 사용을 권장하며 20,000건 이상되는 경우에는 ESTIMATE STATISTICS절의 사용을 권장하고 있다. 또한, 통계정보의 분석은비 일과시간에 수행하는게 원칙이며 일과 시간에 수행해야 하는 경우라면 ESTIMATE STATISTICS절의 사용을 권장한다.



테이블 정보수집 예제
SQL> ANALYZE TABLE emp COMPUTE STATISTICS ;

새로운 정보를 구하기 전에 기존 정보를 삭제
SQL> ANALYZE TABLE emp DELETE STATISTICS;


특정 column에 대한 data 분포 수집
SQL> ANALYZE TABLE emp COMPUTE STATISTICS FOR ALL INDEXED COLUMNS;


통계 정보의 확인 예제

 SQL> SELECT NUM_ROWS
, BLOCKS
, EMPTY_BLOCKS
, AVG_SPACE
, CHAIN_CNT
, AVG_ROW_LEN
, SAMPLE_SIZE
, LAST_ANALYZED
FROM USER_TABLES
WHERE TABLE_NAME = 'CMS_CATEGORY';
 
SQL> SELECT NUM_DISTINCT
, DENSITY
, LOW_VALUE
, HIGH_VALUE
, LAST_ANALYZED
, COLUMN_NAME
FROM USER_TAB_COL_STATISTICS
WHERE TABLE_NAME = 'CMS_CATEGORY';
 
오라클을 통해 작업할 시
insert시점에서 정상적으로 인덱싱이 되지 않는 경우가 발생한다.
특히 결합인덱스를 많이 사용하고 있는 경우 발생될 확률이 높다.
 
이런경우 오라클의 Analyzed를 통해서 해결이 가능하고
어느정도의 실행속도를 향상 시킬 수있다.
(실제 오라클사에서도 3개월에 한번씩은 Analyze를 실행하라 권고하고 있다.)
 

[Analyzed 확인 방법]
1-1. 테이블 통계정보
SELECT TABLE_NAME
     , BLOCKS  
-- 해당 데이터가 저장되어 있는 블록 수.
     , NUM_ROWS  -- 데이터 행 수.
     , AVG_ROW_LEN  -- 하나의 행의 평균 길이.
     , TO_CHAR( LAST_ANALYZED, 'YYYYMMDD' )
  FROM USER_TABLES
[WHERE TABLE_NAME = '테이블명']

1-2. 테이블 통계정보
SELECT TABLE_NAME
          , COLUMN_NAME  
-- 컬럼명
          , LOW_VALUE       -- 해당 컬럼에 저장되어 있는 최소값.
          , HIGH_VALUE      -- 해당 컬럼에 저장되어 있는 최대값.
          , NUM_DISTINCT   -- 유일한 값의 수. (히스토그램 기준)
  FROM USER_TAB_COLUMNS
[WHERE TABLE_NAME = '테이블명']

2. 인덱스 통계정보
SELECT INDEX_NAME
     , BLEVEL                        
-- 인덱스의 깊미(Depth)
     , LEAF_BLOCKS              -- 리프 블록의 수.
     , DISTINCT_KEYS            -- 인덱스 컬럼의 유일한 값의 수.
     , CLUSTERING_FACTOR  -- 조건을 만족하는 데이터를 검색할 때 인덱스 키 값이 각 블록에 얼마나 잘 분산 저장되어 있는지를 나타내는 정도.
     , NUM_ROWS                 -- 전체 행수.
     , TO_CHAR( LAST_ANALYZED, 'YYYYMMDD' )
  FROM USER_INDEXES
 
ex) SELECT TABLE_NAME
                , NUM_ROWS
                , TO_CHAR( LAST_ANALYZED, 'YYYYMMDD' )
      FROM USER_TABLES;
TABLE_NAME                       NUM_ROWS TO_CHAR(
------------------------------ ---------- --------
ABS_TYPE                               38 20040101
ANNIVERS                              183 20040101
APPRFLDRHISTORY                       570 20040101
APPRFOLDER                          16885 20040101
APPRFOLDER_ERR                       3670 20040101
APPRFORM                              359 20040101
.
.
.
USR_INFO_ADMIN                          0 20040101
VAR_DEPT_INFO                           0 20040101
VIEW_TYPE                               0 20040101
WASTEBOX                                0 20040101
ZIP_CODE                            44195 20040101
252 rows selected.
 
※ 참고 : desc user_tables 에서 보통 num_rows 로도 확인 가능
             
 
[특정 Table만 Analyze 하는 방법]
 
ANALYZE TABLE DOCUMENT COMPUTE STATISTICS
ex) DOCUMENT Table 만 Analyze
 
ANALYZE INDEX XPKDOCBOX COMPUTE STATISTICS
ex) XPKDOCBOX Index 만 Analyze
 
[전체 Table Analyze 하는 간단한 방법]
 
1. vi analyze_all.sql
    SELECT 'analyze table || table_name || estimate statistics;' FROM USER_TABLES
 
2. @analyze_all.sql
 
3. set heading off
     set echo off
     set feedback off
     set pagesize 300  (line 이 300 미만일 경우)
     spool analyze_table.sql
     /
     spool off
 
4. vi analyze_table.sql
    필요없는 Line 제거 및 정리
 
5. @analyze_table.sql
 

[전체 Index Analyze 하는 간단한 방법]
 
1. vi analyze_all.sql
    SELECT 'analyze index || index_name || estimate statistics;' FROM USER_INDEXES
 
2. @analyze_all.sql
 
3. set heading off
     set echo off
     set feedback off
     set pagesize 300  (line 이 300 미만일 경우)
     spool analyze_index.sql
     /
     spool off
 
4. vi analyze_index.sql
    필요없는 Line 제거 및 정리
 
5. @analyze_index.sql

 

===========================================================================================================================
===========================================================================================================================

 

출처 : http://blog.naver.com/vxxv122?Redirect=Log&logNo=130128144052

 

정의

→ 비용기반 옵티마이저에서 통계정보를 모아주기 위한 튜닝 도구.

    테이블과 인덱스, 클러스터, 컬럼에 대한 통계정보를 수집.

권한

→ Analyze Any 시스템 권한

통계 수집

1 . 테이블

→ Analyze Table 테이블명 Compute Statistics;

* Row수, 사용된 Block수, 사용안된 Block수, 사용가능한 평균공간, 변경된 Row수, 컬럼당 distinct 값수

  컬럼당 두번째로 가장 작은 값, 컬럼당 두번째로 가장 큰 값

   (질문) 왜 두번째로 가장 작은값과 큰값을 ?

2 . 인덱스

→ Analyze Index 인덱스명 Estimate Statistics;

* 인덱스 레벨, 레벨 Block수, distinct Key수, Key당 Leaf와 Data Block수 평균, Clustering Factor, 최소 Key 값

  최대 Key값

3 . 클러스터

→ Analyze Cluster 클러스터명 Delete Statistics;

* Cluster Key당 길이의 평균

4 . 컬럼

→ Analyze Table 테이블명 Compute Statistics For Table;

→ Analyze Table 테이블명 Compute Statistics For Columns 컬럼명 Size 75;

→ Analyze Table 테이블명 Compute Statistics For All Indexed Columns Size 75;

* 디볼트 버켓 수는 75개

  Distinct 한 값의 수, 히스토그램 정보

유효성 검사

→ Analyze Table 테이블명 Validate Structure [Cascade];

* 검사하려는 테이블과 관련된 모든 테이블을 검사하려면 Cascade 옵션 사용.

옵션

1 . Compute

→ 테이블에 저장되어 있는 모든 행을 대상으로 통계정보 수집

2 . Estimate

→ 오라클 서버의 자동화 알고리즘에 의해 데이터를 추출하여 통계정보를 수집

3 . Delete

→ 수집되어 있는 통계정보를 삭제

* 20,000건 이하의 Row는 Compute 권장, 이상은 Estimate 권장.

확인

1 . User_Tables

2 . User_Tab_Columns

3 . User_Indexes, Index_Stats, Index_histogram

4 . User_Cluster

5 . DBA_Analyze_Objects


 


===========================================================================================================================
===========================================================================================================================



출처 : http://www.oracleclub.com/article/23928

 

1. 개요
- TABLE, COLUMN, 그리고 INDEX 에 대한 통계 정보를 수집 하게 하는 PROCEDURE
 
2. SYNTAX 
DBMS_STATS.GATHER_TABLE_STATS (OWNNAME          VARCHAR2, 
                               TABNAME          VARCHAR2, 
                               PARTNAME         VARCHAR2 DEFAULT NULL,
                               ESTIMATE_PERCENT NUMBER   DEFAULT TO_ESTIMATE_PERCENT_TYPE 
                                                                            (GET_PARAM('ESTIMATE_PERCENT')), 
                               BLOCK_SAMPLE     BOOLEAN  DEFAULT FALSE,
                               METHOD_OPT       VARCHAR2 DEFAULT GET_PARAM('METHOD_OPT'),
                               DEGREE           NUMBER   DEFAULT TO_DEGREE_TYPE(GET_PARAM('DEGREE')),
                               GRANULARITY      VARCHAR2 DEFAULT GET_PARAM('GRANULARITY'), 
                               CASCADE          BOOLEAN  DEFAULT TO_CASCADE_TYPE(GET_PARAM('CASCADE')),
                               STATTAB          VARCHAR2 DEFAULT NULL, 
                               STATID           VARCHAR2 DEFAULT NULL,
                               STATOWN          VARCHAR2 DEFAULT NULL,
                               NO_INVALIDATE    BOOLEAN  DEFAULT  TO_NO_INVALIDATE_TYPE (
                                                                 GET_PARAM('NO_INVALIDATE')),
                               FORCE            BOOLEAN DEFAULT FALSE);

 

3. PARAMETER 설명
- DBMS_STATS.SET_PARAM 에 의해서 디폴트 파라미터 설정 변경이 가능하다.
- 수동 통계정보 생성 시에 저정을 하지 않았을 때 적용되는 DEFAULT 값에 영향을 미치는 값
    => CASCADE
    => DEGREE
    => ESTIMATE_PERCENT
    => METHOD_OPT
    => NO_INVALIDATE
    => GRANULARITY
- 자동 통계정보(GATHER_STATS_JOB) 시에만 영향을 미친다.
    => AUTOSTATS_TARGET [ AUTO   : ORACLE이 자동으로 대상 OBJECT 결정, 
                          ALL    : 대상 시스템의 모든 OBJECTS
                          ORACLE : SYS/SYSTEM OBJECT 만 ]

 

1) DEFAULT 값 확인
    SELECT DBMS_STATS.GET_PARAM('METHOD_OPT') FROM DUAL ;

 

    DBMS_STATS.GET_PARAM('METHOD_OPT')
    ----------------------------------
    FOR ALL COLUMNS SIZE AUTO
 
2) DEFAULT 값 변경 
    EXECUTE DBMS_STATS.SET_PARAM('METHOD_OPT', 'FOR ALL COLUMNS SIZE 1') ;

 

    PL/SQL PROCEDURE SUCCESSFULLY COMPLETED.
 
3) 변경된 DEFAULT 값 확인

    SELECT DBMS_STATS.GET_PARAM('METHOD_OPT') FROM DUAL ;

 

    DBMS_STATS.GET_PARAM('METHOD_OPT')
    ---------------------------------- 
    FOR ALL COLUMNS SIZE 1
    

Parameter

Description

OWNNAME분석할 테이블 소유자
TABNAME테이블 이름
PARTNAME파티션 이름, 지정 하지 않으면 NULL 값
ESTIMATE_PERCENT- 분석할 Row의 Percentage, NULL 이면 Compute(Row 전체)
- 유효값은 1/1000000 ~ 100 
- 디폴트로, DBMS_STATS.AUTO_SAMPLE_SIZE 에 의해서 최적의 값을 결정
BLOCK_SAMPLE- random block sampling or random row sampling 결정
- random block sampling 이 좀더 효과적이다.
- 데이터의 블록 별 분포도가 안좋을 시에는 부적절한 정보 생성
- 디폴트 값이 False로, random row sampling 을 수행한다.
METHOD_OPT- Histogram 생성시 사용하는 옵션
    * FOR ALL [ INDEXED | HIDDEN ] COLUMNS [ size_clause ]
    * FOR COLUMN [ size clause ] column | attribute [size clause] [, column|attribute [ size clause ]…]
- Size_clause := SIZE { integer | REPEAT | AUTO | SKEWONLY }
    => Integer  : Histogram Bucket 수, Max 는 1,254
    => REPEAT   : 이미 Histogram 이 있는 칼럼에 대해서만 생성
    => AUTO     : 데이터 분산도와 칼럼 부하 정도에 따라서 생성 결정
    => SKEWONLY : 데이터 분산도에 따라서 생성 결정
- 디폴트 값은 FOR ALL COLUMNS SIZE AUTO 이다.
- 즉, Histogram 의 생성여부를 Oracle 이 알아서 판단하게 된다.
- 이 경우 EX) method_opt => FOR ALL COLUMNS SIZE 1
    => 모든 컬럼에 대해서 Histogram bucket 의 수를 1로 한다.
      즉, 컬럼 내에 존재하는 여러 가지 값들의 cardinality 는 모두 동일한 값으로 간주한다. ( histogram 을 사용하지 않는다.) 
      이것은 where 조건에 들어오는 특정 컬럼에 대한 값의 변화에 따라서 PLAN 이 변경될 가능성을 없애고자 함이다.
- FOR ALL COLUMNS SIZE 1 로 설정하여 Histogram 정보가 생성되지 않도록 조치 한다.
DEGREE- 병렬처리 정도
- 디폴트 값은 NULL 이고, CREATE TABLE, ALTER TABLE 시 설정된 DEGREE 값에 의해 정해진다.
- AUTO_DEGREE 값은 병렬처리 정도를 자동으로 결정한다.
- 이것은 1 or DEFAULT_DEGREE [ Object Size 와 CPU Count 에 의해 결정 ]
GRANULARITY- Parition table 에 대한 분석시 사용
    => ‘ALL’                  : Global, Partition, Subpartition 통계정보 수집 – Parition Table 대상
    => ‘AUTO’                 : 디폴트 값으로 ,Partition Type 에 따라서 결정 – 일반 Table 대상
    => ‘DEFAULT’              : Global, Partition 통계정보 수집, Old Version 과 호환을 위해 사용
    => ‘GLOBAL’               : Global 통계정보 수집
    => ‘GLOBAL AND PARTITION’ : SubPartition 에 대한 통계정보는 수집되지 않는다.
    => ‘PARTITION’            : Partition 통계정보 수집
    => ‘SUBPARTITION’         : SubPartition 통계정보 수집
CASCADE- 대상 테이블의 인덱스에 대한 통계수집 여부
- 인덱스 통계정보는 병렬처리가 불가능하다.
- TRUE : 대상 테이블에 관련된 index 에 대해서 통계정보 수집
STATTAB- 통계수집을 통한 기존 통계정보 Update 전에, 기존에 존재하는 통계정보를 저장할 User Stat Table 을 지정
STATID- Stattab 와 연관된 구분자 값
STATOWN- Stattab 에 지정한 User Stat Table 의 소유자가 다를 경우 지정
NO_VALIDATE- 의존적인 Cursor를 Invalidate 할지 , 안할지 결정
    => True : 관련된 Cursor 를 invalidate 하지 않는다.
    => False : 디폴트 값으로, 관련된 Cursor 를 Invalidate 한다. 
- Default 로 DBMS_STATS.AUTO_INVALIDATE 값이고, 의미는 DBMS 가 의존적 Cursor를 언제 invalidate 할지 자동으로 결정
- 이때 작용하는 Parameter는 _OPTIMIZER_INVALIDATION_PERIOD 이고, Default 롤 18000 초(5시간) 이다.
- 즉, 통계 정보 수집에 의해 통계 정보가 변경된 후 약 5시간에 걸쳐 랜덤한 시점에 해당 Cursor가 실행될 때 invalidation이 발생한다. 
- 이것을 Auto Invalidation이라고 부른다.
- 일정 시간에 걸쳐 랜덤하게 Cursor를 Invalidation함으로써 특정 시점에 Hard Parse가 한꺼번에 몰리는 현상을 피할 수 있다. 
FORCE- Lock 걸린 Table 에 대해서도 강제로 통계정보 생성

 

4. Test
1) Mission
    CASCADE => TRUE
        E 인덱스에 대한 통계정보도수집하라.
    
    CASCADE => FALSE
        E 인덱스에 대한 통계정보도수집하라.
    
    METHOD_OPT =>'FOR ALL COLUMNS SIZE 1'
        E 칼럼(HIGH AND LOW COLUMN VALUE)에 대한 통계정보도 수집하라.
    
    METHOD_OPT =>'FOR COLUMNS'
        E컬럼에 대한통계정보를 수집하지 마라

 

2) Test 
2-1) 일반 테이블
    SHOW USER

 

    USER IS"SYS"

 

    ① SCOTT의 BIG_TABLE 의 전체 테이블과 모드 인덱스를 가지고, 테이블, 칼럼(HIGHAND LOW COLUMN VALUE), 
       연관 인덱스의 통계정보를 생성한다.( COMPUTE STATISTICS )

       EXECDBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT', 
                                         TABNAME    => 'BIG_TABLE', 
                                         CASCADE    => TRUE,
                                         METHOD_OPT => 'FOR ALL COLUMNS SIZE 1');
 
    ② SCOTT의 BIG_TABLE의 15% ROW를 가지고, 테이블, 칼럼, 연관인덱스의 통계정보를 생성한다.( SAMPLE 15 PERCENT )
       EXECDBMS_STATS.GATHER_TABLE_STATS(OWNNAME          => 'SCOTT',
                                         TABNAME          => 'BIG_TABLE', 
                                         CASCADE          => TRUE, 
                                         ESTIMATE_PERCENT => 15);

 

    ③ SCOTT의 BIG_TABLE의 전체 테이블과 모드 인덱스를 가지고, 테이블의 통계정보를 수집하라. 
       인덱스와 칼럼에 대한 통계정보는 제외
       EXECDBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT',
                                         TABNAME    => 'BIG_TABLE', 
                                         CASCADE    => FALSE, 
                                         METHOD_OPT => 'FOR COLUMNS');
 
    ④ SCOTT의 BIG_TABLE의 전체 테이블과 모드 인덱스를 가지고, 테이블과인덱스에 대한 통계정보를 수집하라. 
       칼럼에 대한 통계정보는 제외
       EXECDBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT',
                                         TABNAME    => 'BIG_TABLE', 
                                         CASCADE    => TRUE, 
                                         METHOD_OPT => 'FOR COLUMNS');

 

    ⑤ SCOTT의 BIG_TABLE의 전체 테이블과 모드 인덱스를 가지고, 테이블과 컬럼(NO HISTOGRAM),

       그리고 인덱스에 대한 통계정보를 수집하라.
       잠시 후에 인덱스 칼럼들의 HISTOGRAM 통계정보를 수집하라.

       EXECDBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'SCOTT',
                                         TABNAME => 'BIG_TABLE', 
                                         CASCADE => TRUE);

       잠시 후에..

       EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME    =>'SCOTT',
                                          TABNAME    => 'BIG_TABLE', 
                                          CASCADE    => TRUE,
                                          METHOD_OPT => 'FOR ALL INDEXED COLUMNSSIZE 1');

 

    ⑥ SCOTT 의 BIG_TABLE 의 전체 테이블과 모드 인덱스를 가지고, 테이블과 인덱스칼럼(ONLY HIGH AND LOW )에

       대한 통계정보를 수집하라. 인덱스에 대한 통계정보는수집하지 마라.
        EXECDBMS_STATS.GATHER_TABLE_STATS(OWNNAME    => 'SCOTT',
                                          TABNAME    => 'BIG_TABLE', 
                                          CASCADE    => FALSE,
                                          METHOD_OPT => 'FOR ALL INDEXED COLUMNSSIZE 1');
 
2-2) PARTITIONTABLE 의 경우 
    ① 추가적으로 GRANULARITY 정보를 'ALL', 'AUTO', 'PARITION', 'GLOBAL AND PARTITION', 'GLOBAL', 'SUBPARTITION'을

       통해서 통계수집 대상 TABLE SEGMENT를 선정 가능하다.
 
 
    ② 참고 
        - LOCK VS DBMS_STATS.GATHER_TABLE_STATS
            : DML 이 LOCK 이 발생 하여도 GATHER_TABLE_STATS 는 정상적으로 진행된다.

            BEGIN
                FOR I IN 1001 .. 5000 LOOP
                    INSERT INTO CHECK_LOCK VALUES ( I , I , 'LOCK');
                END LOOP ;
            END ;
            /

        - @CHECK_USER_LOCK.SQL
            ENTER VALUE FOR USER_NAME: SCOTT10
            OLD  46: AND B.USERNAME =UPPER('&USER_NAME')
            NEW  46: AND B.USERNAME =UPPER('SCOTT10')
            
            USERNAME    SID LOCK_TYPE       MODE_HELD   MODE_REQUE LOCK_ID1 LOCK_ID2
            ---------- ---- --------------- ----------- ---------- -------- --------
            SCOTT10     151 DML             ROW-X (SX)  NONE       51782    0
            SCOTT10     151 TRANSACTION     EXCLUSIVE   NONE       131077   307

        - EXECUTE DBMS_STATS.GATHER_TABLE_STATS(OWNNAME =>'SCOTT10',TABNAME => 'CHECK_LOCK');
        
        ==> DML LOCK 과는 무관하게 진행 된다.



반응형
반응형

 

출처 : http://zzznara2.tistory.com/228

 

 

 

이것저것 하다면 가끔은 DB를 삭제하고 다시 만들고 싶은 생각이 들 때가 듭니다.

그렇지만, DB를 다시 만든 내가 만든 테이블만 모두 삭제하면 더 편리하겠다는 생각이 들었죠.

 

 

SELECT  'DROP TABLE ' || object_name || ' CASCADE CONSTRAINTS;'
  FROM    user_objects
WHERE   object_type = 'TABLE';



생성된 쿼리를 긁어다가 [F5] 버튼 꾹~ 눌러주면 테이블이 다 삭제됩니다.


SELECT  'DROP VIEW ' || object_name
  FROM    user_objects
WHERE   object_type = 'VIEW';

View도 이렇게 쿼리를 만들어 같은 방법으로 삭제하시면 됩니다.

 

반응형
반응형

출처 : http://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&detail=1&pageno=0&wid=964&rssMode=1&wtype=0


정말로 좋은 글이 또 퍼옴. 두고두고 공부할 때 읽을 예정...





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

사실, 처음 이 질문에 의문을 느꼈을 때 제 심중의 대답은 Port 수 제한이었습니다. unsigned short(2byte) 이니까 65535 일 텐데 그나마 시스템에서 사용하는 포트를 제외해야 하니 약 60K 정도는 생성할 수 있을 것이라는 계산이었습니다.

서버 한대에 6만 개의 클라이언트라면 그다지 나쁘지 않은 연결 수인 것 같았지만, 최근의 64비트 다중 코어/소켓을 장착한 고성능 서버들이 출현하는 상황에서 거의 무한대에 가까운 16byte 주소값을 갖는 IPv6에서도 포트를 나타내는 타입이 USHORT 인 것을 보고는 다소 놀랬습니다.

=== ws2ipdef.h ===
typedef struct sockaddr_in6 {
    ADDRESS_FAMILY sin6_family; // AF_INET6.
    USHORT sin6_port;           // Transport level port number.
    ULONG  sin6_flowinfo;       // IPv6 flow information.
    IN6_ADDR sin6_addr;         // IPv6 address.
    union {
        ULONG sin6_scope_id;     // Set of interfaces for a scope.
        SCOPE_ID sin6_scope_struct; 
    };
} SOCKADDR_IN6_LH, *PSOCKADDR_IN6_LH, FAR *LPSOCKADDR_IN6_LH;


여전히 미래에도 60K 동시 연결만을 제공한다는 걸까요?

도저히 그럴 수는 없다는 상식으로, 검색을 해보았습니다. 그런데... 꽤나 의외더군요. 이에 대한 의문을 갖는 사람도 별로 없었을 뿐더러, 대부분은 답변까지 포함해서 틀린 답들 뿐이었습니다. (일부 답변은 Windows 2000만 있었을 당시라서 그 때에는 맞는 답일 수 있습니다.)

이론상 접속 가능한 최대 인원은? 
; http://www.gpgstudy.com/forum/viewtopic.php?topic=5370

다시한번질문드립니다.소켓의 한계...한피씨의 서버용량은???
; http://www.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_qna&no=15833

소켓 생성시 최대개수는..... 얼마나..  
; http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=50&MaeulNo=20&no=206826&ref=206665

Windows에서 열 수 있는 Socket 수 얻는 방법
; http://bspfp.pe.kr/63

WSAAsyncSelect 로가능한 소켓갯수는...
; http://www.tipssoft.com/bulletin/board.php?bo_table=QnA&wr_id=16299


사실, 검색도 힘들었는데... 디아블로인가... ^^; 게임에서 제공되는 소켓 아이템이라는 동일 이름 때문이었습니다. (다시 한번 놀랬지만, 게임 유저들의 그 참여/공유 정신은 대단한 것 같습니다. ^^)

다음 차례로, 외국 자료가 남았군요. 검색을 해보니, 진단이 딱 나옵니다.

One Million TCP Connections...
; http://www.serverframework.com/asynchronousevents/2010/12/one-million-tcp-connections.html


윈도우상에서의 이론 상 한계는 "16,777,214" 이라고 합니다. 이 정도면 결국 실질적인 한계가 문제인데, 아래의 글에서 잘 정리해 주고 있습니다.

How to support 10,000 or more concurrent TCP connections
; http://www.serverframework.com/asynchronousevents/2010/10/how-to-support-10000-concurrent-tcp-connections.html


  • Data copies
  • Context switches
  • Memory allocation
  • Lock contention


즉, 서버 스펙도 따라야 하고, 해당 응용 프로그램의 메모리/로직에 따라서 천차만별이 된다는 것이죠. ^^

참고로, 아래는 일반적인 threshold 값들인데 아마도 Windows Server 2003 기준의 값인 것 같습니다. 운영체제마다 변경된 부분이 있을 테니 적용할 때는 적절하게 감안을 해주셔야 할 것입니다.

Configure the max limit for concurrent TCP connections 
; http://smallvoid.com/article/winnt-tcpip-max-limit.html





그런데, 아직... 의문이 안 풀린 분들이 있을 텐데요. 과연 어떻게 포트 번호 범위를 넘어서는 16,777,214 값이 나오게 된 걸까요? 이에 대한 설명은 위에서 소개한 "One Million TCP Connections..." 글의 댓글에서 글쓴이가 쉽게 설명해 주고 있습니다.

No, that's a common misconception. You're limited to the local ports when making outbound connections as each connection consumes a local port and they are limited to 65535 as you point out and when you take into account the number of ports already in use for other services and any connections currently in TIME_WAIT the maximum number of outbound ports is usually at most 50k.

Inbound ports are identified by a tuple that consists of the local ip and port and the remote ip and port and so are not limited in the same way. I've run tests whereby a simple server on very modest hardware supported more than 70,000 concurrent active connections - the test server and client that I used can be found here: http://www.lenholgate.com/archives/000568.html



오~~~ 역시 머리 좋은 사람들은 다르군요. ^^ 어차피 내부에서 해당 소켓을 식별만 하면 되는데 굳이 2바이트 정수로 제한할 필요없이 구분키의 범위를 연결을 시도한 측의 IP/Port를 함께 포함하니 자연스럽게 65,535 개의 한계가 없어져버립니다. 실제로 글쓴이는 760 MB 메모리만을 가진 Windows Server 2003 시스템으로 7만 개의 동시 연결을 테스트했다고 합니다.

재미있군요. 직접 테스트 해보실 분들 계신가요? 700 MB 정도에 7만 개면, 24 GB 메모리면 테스트에 사용한 동일한 서버 프로그램으로 210만 개는 무난하게 나온다는 얘기가 되는 군요. 그럼, 서버는 그 정도 사양으로 한 대 준비하면 될 것 같고. 반면에 클라이언트는 제법 준비를 해둬야 합니다. 왜냐 하면 클라이언트 측은 여전히 65,535 포트 범위 제한이 있기 때문에, 100만개 연결 테스트만 해도 20 대 정도가 넘게 필요합니다. Virtual NIC의 특별한 제한이 없다면 가상 PC를 20개 정도 마련해야 겠군요. (참고로, 가정용 무선 Access Point로는 네트워크 연결 테스트하지 마세요. ^^ 제 경우에는 천 개만 넘어도 네트워크가 멈춰버렸습니다.)

혹시, 환경 구성해서 테스트 하시는 분이 계시면 결과 좀 공유 부탁드리겠습니다. ^^

반응형
반응형

출처 : http://d2.naver.com/helloworld/47667


정말로 좋은 글이 있어서 천천히 두고두고 공부할려고 파왔음...





TCP/IP 없는 인터넷 서비스는 상상할 수 없습니다. 우리가 개발하고 사용하는 모든 인터넷 서비스는 TCP/IP라는 튼튼한 토대에 기반하고 있습니다. 어떻게 네트워크를 통해 데이터가 오가는지를 이해하면, 튜닝 등을 통한 성능 개선이나 트러블 슈팅, 신기술 도입 등에 많은 도움이 됩니다.

이 글에서는 Linux 운영체제와 하드웨어 레이어에서의 데이터 흐름과 제어 흐름을 바탕으로 네트워크 스택에 대한 전반적인 작동 방식을 알아보겠습니다.

TCP/IP의 중요한 성질

데이터의 순서가 바뀌지 않으면서 데이터가 유실되지 않도록 가급적 빠르게 데이터를 보내려면 네트워크 프로토콜을 어떻게 설계해야 할까? TCP/IP는 이런 고민 아래 설계된 것이다. 다음은 스택을 이해하는데 필요한 TCP/IP의 중요한 성질이다.


TCP와 IP

엄밀히 말해 TCP와 IP는 서로 다른 레이어의 것이라 분리해서 이해하는 것이 옳지만, 이해의 편의상 여기서는이 장에서는 둘을 분리하지 않고 설명한다.


1. Connection oriented

두 개 엔드포인트(로컬, 리모트) 사이에 연결을 먼저 맺고 데이터를 주고받는다. 여기서 'TCP 연결 식별자'는 두 엔드포인트의 주소를 합친 것으로, <로컬 IP 주소, 로컬 포트번호, 리모트 IP 주소, 리모트 포트번호> 형태이다.

2. Bidirectional byte stream

양방향 데이터 통신을 하고, 바이트 스트림을 사용한다.

3. In-order delivery

송신자(sender)가 보낸 순서대로 수신자(receiver)가 데이터를 받는다. 이를 위해서는 데이터의 순서가 필요하다. 순서를 표시하기 위해 32-bit 정수 자료형을 사용한다.

4. Reliability through ACK

데이터를 송신하고 수신자로부터 ACK(데이터 받았음)를 받지 않으면, 송신자 TCP가 데이터를 재전송한다. 따라서 송신자 TCP는 수신자로부터 ACK를 받지 않은 데이터를 보관한다(buffer unacknowledged data).

5. Flow control

송신자는 수신자가 받을 수 있는 만큼 데이터를 전송한다. 수신자가 자신이 받을 수 있는 바이트 수 (사용하지 않은 버퍼 크기, receive window)를 송신자에게 전달한다. 송신자는 수신자 receive window가 허용하는 바이트 수만큼 데이터를 전송한다.

6. Congestion control

네트워크 정체를 방지하기 위해 receive window와 별도로 congestion window를 사용하는데 이는 네트워크에 유입되는 데이터양을 제한하기 위해서이다. Receive window와 마찬가지로 congestion window가 허용하는 바이트 수만큼 데이터를 전송하며 여기에는 TCP Vegas, Westwood, BIC, CUBIC 등 다양한 알고리즘이 있다. Flow control과 달리 송신자가 단독으로 구현한다.

데이터 전송

이름이 설명하듯, 네트워크 스택에는 여러 레이어(layer)가 있다. 어떤 레이어가 있는지는 그림 2에서 알 수 있다.

여러 레이어가 있지만, 크게 유저(user) 영역, 커널(kernel) 영역, 디바이스로(device) 영역으로 나눌 수 있다. 유저 영역과 커널 영역에서의 작업은 CPU가 수행한다. 이 유저 영역과 커널 영역은 디바이스 영역과 구별하기 위해 호스트(host)라고 부른다. 여기서 디바이스는 패킷을 송수신하는 NIC(Network Interface Card)이다. 흔히 부르는 랜카드보다 더 정확한 용어이다.

networkstack1

그림 1 데이터 전송 시 TCP/IP 네트워크 스택의 각 레이어 별 동작 과정

유저 영역부터 밑으로 내려가 보자. 우선 애플리케이션이 전송할 데이터를 생성하고(그림 1에서 User data 상자), write 시스템 콜을 호출해서 데이터를 보낸다. 소켓(그림 2에서 fd)은 이미 생성되어 연결되어 있다고 가정한다. 시스템 콜을 호출하면 커널 영역으로 전환된다.

Linux나 Unix를 포함한 POSIX 계열 운영체제는 소켓을 file descriptor로 애플리케이션에 노출한다. 이런 POSIX 계열의 운영체제에서 소켓은 파일의 한 종류다. 파일(file) 레이어는 단순한 검사만 하고 파일 구조체에 연결된 소켓 구조체를 사용해서 소켓 함수를 호출한다.

커널 소켓은 두 개의 버퍼를 가지고 있다. 송신용으로 준비한 send socket buffer, 수신용으로 준비한 receive socket buffer이다. Write 시스템 콜을 호출하면 유저 영역의 데이터가 커널 메모리로 복사되고, send socket buffer의 뒷부분에 추가된다. 순서대로 전송하기 위해서다. 그림에서 옅은 회식 상자가 이미 socket buffer에 존재하는 데이터를 의미한다. 이 다음으로 TCP를 호출한다.

소켓과 연결된 TCP Control Block(TCB) 구조체가 있다. TCB에는 TCP 연결 처리에 필요한 정보가 있다. TCB에 있는 데이터는 connection state(LISTEN, ESTABLISHED, TIME_WAIT 등), receive window, congestion window, sequence 번호, 재전송 타이머 등이다.

현재 TCP 상태가 데이터 전송을 허용하면 새로운 TCP segment, 즉 패킷을 생성한다. Flow control 같은 이유로 데이터 전송이 불가능하면 시스템 콜은 여기서 끝나고, 유저 모드로 돌아간다(즉, 애플리케이션으로 제어권이 넘어간다).

TCP segment에는 TCP 헤더와 페이로드(payload)가 있다. 페이로드에는 ACK를 받지 않은 send socket buffer에 있는 데이터가 담겨 있다. 페이로드의 최대 길이는 receive window, congestion window, MSS(Maximum Segment Size) 중 최대 값이다.

그리고 TCP checksum을 계산한다. 이 checksum 계산에는 pseudo 헤더 정보(IP 주소들, segment 길이, 프로토콜 번호)를 포함시킨다. 여기서 TCP 상태에 따라 패킷을 한 개 이상 전송할 수 있다.

사실 요즘의 네트워크 스택에서는 checksum offload 기술을 사용하기 때문에, 커널이 직접 TCP checksum을 계산하지 않고 대신 NIC가 checksum을 계산한다. 여기서는 설명의 편의를 위해 커널이 checksum을 계산한다고 가정한다.

생성된 TCP segment는 IP 레이어로 이동한다(내려 간다). IP 레이어에서는 TCP segment에 IP 헤더를 추가하고, IP routing을 한다. IP routing이란 목적지 IP 주소(destination IP)로 가기 위한 다음 장비의 IP 주소(next hop IP)를 찾는 과정을 말한다.

IP 레이어에서 IP 헤더 checksum을 계산하여 덧붙인 후, Ethernet 레이어로 데이터를 보낸다.

Ethernet 레이어는 ARP(Address Resolution Protocol)를 사용해서 next hop IP의 MAC 주소를 찾는다. 그리고 Ethernet 헤더를 패킷에 추가한다. Ethernet 헤더까지 붙으면 호스트의 패킷은 완성이다.

IP routing을 하면 그 결과물로 next hop IP와 해당 IP로 패킷 전송할 때 사용하는 인터페이스(transmit interface, 혹은 NIC)를 알게 된다. 따라서 transmit NIC의 드라이버를 호출한다.

만약 tcpdumpWireshark 같은 패킷 캡처 프로그램이 작동 중이면 커널은 패킷 데이터를 프로그램이 사용하는 메모리 버퍼에 복사한다. 수신도 마찬가지로 드라이버 바로 위에서 패킷을 캡처한다. 대개 traffic shaper 기능도 이 레이어에서 동작하도록 구현되어있다.

드라이버는 NIC 제조사가 정의한 드라이버-NIC 통신 규약에 따라 패킷 전송을 요청한다.

NIC는 패킷 전송 요청을 받고, 메인 메모리에 있는 패킷을 자신의 메모리로 복사하고, 네트워크 선으로 전송한다. 이때 Ethernet 표준에 따라 IFG(Inter-Frame Gap), preamble, 그리고 CRC를 패킷에 추가한다. IFG, preamble은 패킷의 시작을 판단하기 위해 사용하고(네트워킹 용어로는 framing), CRC는 데이터 보호를 위해 사용한다(TCP, IP checksum과 같은 용도이다). 패킷 전송은 Ethernet의 물리적 속도, 그리고 Ethernet flow control에 따라 전송할 수 있는 상황일 때 시작된다. 회의장에서 발언권을 얻고 말하는 것과 비슷하다.

NIC가 패킷을 전송할 때 NIC는 호스트 CPU에 인터럽트(interrupt)를 발생시킨다. 모든 인터럽트에는 인터럽트 번호가 있으며, 운영체제는 이 번호를 이용하여 이 인터럽트를 처리할 수 있는 적합한 드라이버를 찾는다. 드라이버는 인터럽트를 처리할 수 있는 함수(인터럽트 핸들러)를 드라이브가 가동되었을 때 운영체제에 등록해둔다. 운영체제가 핸들러를 호출하고, 핸들러는 전송된 패킷을 운영체제에 반환한다.

지금까지 설명한 것은 애플리케이션에서 쓰기를 하였을 때 데이터가 커널과 디바이스를 거쳐 전송되는 과정이다. 그런데 애플리케이션이 쓰기 요청을 직접적으로 하지 않아도 커널이 TCP를 호출해서 패킷을 전송하는 경우가 있다. 예를 들어 ACK을 받아 receive window가 늘어나면 socket buffer에 남아있는 데이터를 포함한 TCP segment를 생성하여 상대편에 전송한다.

데이터 수신

이제 어떻게 데이터를 수신하는지 알아보도록 하자. 패킷이 외부에서 도착했을 때 어떻게 작동하는지에 대한 것이다. 그림 3에서 네트워크 스택이 수신한 패킷을 처리하는 과정을 알 수 있다.

networkstack2

그림 2 데이터 수신 시 TCP/IP 네트워크 스택의 각 레이어 별 동작 과정

우선 NIC가 패킷을 자신의 메모리에 기록한다. CRC 검사로 패킷이 올바른지 검사하고, 호스트의 메모리버퍼로 전송한다. 이 버퍼는 드라이버가 커널에 요청하여 패킷 수신용으로 미리 할당한 메모리이고, 할당을 받은 후 드라이버는 NIC에 메모리 주소와 크기를 알려 준다. NIC가 패킷을 받았는데, 드라이버가 미리 할당해 놓은 호스트 메모리 버퍼가 없으면 NIC가 패킷을 버릴 수 있다 (packet drop).

패킷을 호스트 메모리로 전송한 후, NIC가 호스트운영체제에 인터럽트를 보낸다.

드라이버가 새로운 패킷을 보고 자신이 처리할 수 있는 패킷인지 검사한다. 여기까지는 제조사가 정의한 드라이버-NIC 통신 규약을 사용한다.

드라이버가 상위 레이어로 패킷을 전달하려면 운영체제가 이해할 수 있도록, 받은 패킷을 운영체제가 사용하는 패킷 구조체로 포장해야 한다. 예를 들어, Linux의 sk_buff, BSD 계열 커널의 mbuf, 그리고 Microsoft Windows의 NET_BUFFER_LIST가 운영체제의 패킷 구조체이다. 드라이버는 이렇게 포장한 패킷을 상위 레이어로 전달한다.

Ethernet 레이어에서도 패킷이 올바른지 검사하고, 상위 프로토콜(네트워크 프로토콜)을 찾는다(de-multiplex). 이때 Ethernet 헤더의 ethertype 값을 사용한다. IPv4 ethertype은 0x0800이다. Ethernet 헤더를 제거하고 IP 레이어로 패킷을 전달한다.

IP 레이어에서도 패킷이 올바른지 검사한다. IP 헤더 checksum을 확인하는 것이다. 논리적으로 여기서 IP routing을 해서 패킷을 로컬 장비가 처리해야 하는지, 아니면 다른 장비로 전달해야 하는지 판단한다. 로컬 장비가 처리해야 하는 패킷이면 IP 헤더의 proto 값을 보고 상위 프로토콜(트랜스포트 프로토콜)을 찾는다. TCP proto 값은 6이다. IP 헤더를 제거하고 TCP 레이어로 패킷을 전달한다.

하위 레이어에서와 마찬가지로 TCP 레이어에서도 패킷이 올바른지 검사한다. TCP checksum도 확인한다. 앞서 언급했듯이 요즘의 네트워크 스택에는 checksum offload 기술이 적용되어 있기 때문에 커널이 checksum을 직접 계산하지 않는다.

다음으로 패킷이 속하는 연결, 즉 TCP control block을 찾는다. 이때 패킷의 <소스 IP, 소스 port, 타깃 IP, 타깃 port>를 식별자로 사용한다. 연결을 찾으면 프로토콜을 수행해서 받은 패킷을 처리한다. 새로운 데이터를 받았다면, 데이터를 receive socket buffer에 추가한다. TCP 상태에 따라 새로운 TCP 패킷(예를 들어 ACK 패킷)을 전송할 수 있다. 여기까지 해서 TCP/IP 수신 패킷 처리 과정이 끝나게 된다.

Receive socket buffer 크기가 결국은 TCP의 receive window이다. 어느 지점까지는 receive window가 크면 TCP throughput이 증가한다. 예전에는 socket buffer 크기를 애플리케이션이나 운영체제 설정에서 조절하고는 했다. 최신 네트워크 스택은 receive socket buffer 크기, 즉 receive window를 자동으로 조절하는 기능을 가지고 있다.

이후 애플리케이션이 read 시스템 콜을 호출하면 커널 영역으로 전환되고, socket buffer에 있는 데이터를 유저 공간의 메모리로 복사해 간다. 복사한 데이터는 socket buffer에서 제거한다. 그리고 TCP를 호출한다. TCP는 socket buffer에 새로운 공간이 생겼기 때문에 receive window를 증가시킨다. 그리고 프로토콜 상태에 따라 패킷을 전송한다. 패킷 전송이 없으면 시스템 콜이 종료된다.

네트워크 스택 발전 방향

지금까지 설명한 네트워크 스택 레이어가 하는 일은 가장 기본적인 기능이다. 1990년대 초반의 네트워크 스택은 이보다 약간 더 기능이 많은 정도였다. 하지만 요즘의 최신 네트워크 스택은 더 많은 기능을 가지고 있고, 따라서 네트워크 스택 구현체의 복잡성도 증가했다.

최신의 네트워크 스택을 목적에 따라 구분해 보면 다음과 같다.

패킷 처리 과정 조작 기능

Netfilter(방화벽, NAT 등), traffic control 같은 기능이다. 기본 처리 흐름에 사용자가 제어할 수 있는 코드를 삽입해서 사용자 설정에 따라 다양한 효과를 낸다.

프로토콜 성능

주어진 네트워크 환경에서 TCP 프로토콜이 달성할 수 있는 throughput, latency, stability 등의 개선을 목표로 한다. 다양한 congestion control 알고리즘들과 SACK 같은 TCP 추가 기능이 대표적인 예이다. 프로토콜 개선 사항은 이 글의 범위 바깥이라 여기서는 다루지 않겠다.

패킷 처리 효율

한 장비가 패킷을 처리하는데 소요되는 CPU cycle, 메모리 사용량, 메모리 접근 수 등을 줄여서 초당 처리할 수 있는 최대 패킷 수를 개선하는 것을 목표로 한다. 장비 내부에서의 레이턴시(latency)를 줄이는 것을 포함한 여러 시도가 있었다. 스택 병렬처리, header prediction, zero-copy, single-copy, checksum offload, TSO, LRO, RSS 등 여러 가지가 있다.

스택 내부 제어 흐름(control flow)

이제 Linux 네트워크 스택의 내부 흐름을 좀더 깊게 살펴보자. 네트워크 스택이 아닌 서브시스템과 마찬가지로, 네트워크 스택은 기본적으로 이벤트 발생에 반응하는 event-driven 방식으로 작동한다. 따라서 스택 수행을 위한 별도 스레드는 없다. 그림 2와 그림 3은 제어 흐름을 매우 단순화한 것이고, 그림 4에서 좀 더 정확한 제어 흐름을 볼 수 있다.

networkstack3

그림 3 스택 내부 제어 흐름

그림 3의 (1)은 애플리케이션이 시스템 콜을 호출하여 TCP를 수행(사용)하는 경우다. 예를 들어, read 시스템 콜과 write 시스템 콜을 호출하고 TCP를 수행한다. 하지만 패킷 전송은 없다.

(2)는 (1)과 같은데, TCP 수행 결과 패킷 전송이 필요한 경우다. 패킷을 생성해서 드라이버로 패킷을 내려 보낸다. 드라이버의 앞 부분에는 큐(queue)가 있다. 패킷은 우선 큐에 들어가고, 큐 구현체가 패킷이 드라이버로 전달되는 시점을 결정한다. Linux의 qdisc(queue discipline)가 이것이다. Linux traffic control 기능은 qdisc를 조작하는 것이다. 기본으로 사용하는 qdisc는 단순한 FIFO(first-in-first-out) 큐이다. 다른 qdisc를 사용하면 인위적인 패킷 유실, 패킷 지연, 전송 속도 제한 등 여러 가지 효과를 달성할 수 있다. (1), (2)에서는 애플리케이션의 프로세스 스레드가 드라이버까지 실행한다.

(3) 흐름은 TCP가 사용하는 타이머가 만료된 경우다. 예를 들어, TIME_WAIT 타이머가 만료되면 TCP를 호출해서 연결을 삭제한다.

(4) 흐름은 (3)과 같이 TCP가 사용하는 타이머가 만료된 경우인데, TCP 수행 결과 패킷 전송이 필요한 경우다. 예를 들어 재전송 타이머(retransmit timer)가 만료되면, ACK를 받지 못한 패킷을 전송한다.

(3), (4) 흐름은 타이머 인터럽트를 처리한 softirq가 실행되는 과정이다.

NIC 드라이버가 인터럽트를 받으면 전송된 패킷을 반환한다(free). 대개 여기서 드라이버 실행이 끝난다. (5) 흐름은 transmit queue에 패킷이 적체된 경우다. 드라이버가 softirq를 요청하고, softirq 핸들러가 transmit queue를 실행해서 적체된 패킷을 드라이버로 보낸다.

NIC 드라이버가 인터럽트를 받고 새로 수신된 패킷을 발견하면 softirq를 요청한다. 수신 패킷을 처리하는 softirq가 드라이버를 호출해서 수신된 패킷을 상위 레이어로 전달한다. Linux는 이와 같이 수신 패킷을 처리하는 것을 NAPI(new API)라고 부른다. 드라이버가 상위 레이어로 직접 전달하지 않고, 상위 레이어가 직접 패킷을 가져가기 때문에 polling과 유사하다. 실제 코드는 NAPI poll 혹은 poll이라 부른다.

(6)은 TCP까지 수행한 경우, (7)은 추가 패킷 전송이 필요한 경우를 보여준다. (5), (6), (7) 모두 NIC 인터럽트를 처리한 softirq가 실행한다.

인터럽트와 수신 패킷 처리

인터럽트 처리는 복잡하지만 패킷 수신 처리에 따른 성능 문제를 이해하기 위해 필요하다. 그림 4에서 인터럽트 처리 과정을 볼 수 있다.

networkstack4

그림 4 인터럽트, softirq, 그리고 수신 패킷 처리

CPU 0이 애플리케이션 프로그램(user program)을 실행하고 있다고 가정하자. 이때 NIC가 패킷을 수신하고 CPU 0을 인터럽트한다. CPU는 커널 인터럽트(흔히 irq라고 부른다) 핸들러를 실행한다. 이 핸들러가 인터럽트 번호를 보고 드라이버 인터럽트 핸들러를 호출한다. 드라이버는 전송된 패킷은 반환하고, 수신된 패킷을 처리하기 위해 napi_schedule() 함수를 호출한다. 이 함수가 softirq(소프트웨어 인터럽트)를 요청한다.

드라이버 인터럽트 핸들러의 실행이 종료되면 커널 핸들러로 제어권이 돌아간다. 커널 핸들러가 softirq에 대한 인터럽트 핸들러를 실행시킨다.

Interrupt context가 실행되었으니 softirq context가 실행될 차례이다. Interrupt context와 softirq context가 실행되는 스레드는 같다. 하지만 스택이 서로 다르다. 그리고 interrupt context는 하드웨어 인터럽트를 차단하지만, softirq context는 하드웨어 인터럽트를 허용한다.

수신 패킷을 처리하는 softirq 핸들러는 net_rx_action() 함수이다. 이 함수는 드라이버의 poll() 함수를 호출한다. poll() 함수는 netif_receive_skb() 함수를 호출해서 수신 패킷을 한 개씩 상위 레이어로 보낸다. softirq 처리가 종료되면, 애플리케이션은 시스템 콜을 요청하기 위하여 중단했던 지점부터 다시 수행을 재개한다.

따라서 인터럽트를 받은 CPU가 수신 패킷을 처음부터 끝까지 처리한다. Linux, BSD, Microsoft Windows 모두 기본으로 이와 같이 작동한다.

패킷 수신을 많이 하는 서버 CPU 사용률을 보면 한 CPU만 열심히 softirq를 실행하는 현상을 종종 확인할 수 있다. 지금까지 설명한 수신 패킷 처리 방식 때문에 발생하는 현상이다. 이 문제를 풀기 위해 multi-queue NIC, RSS, RPS가 나왔다.

데이터 구조체

중요한 데이터 구조체 몇 개를 살펴보고 실제 코드를 따라가 보자.

sk_buff 구조체

첫째, 패킷을 의미하는 sk_buff 구조체 혹은 skb 구조체가 있다. 그림 5는 sk_buff 구조체의 일부를 보여준다. 기능이 발전되면서 이보다 더 복잡해졌지만 기본적으로 필요한 기능은 누구나 생각할 수 있는 것들이다.

networkstack5

그림 5 패킷 구조체 sk_buff

패킷 데이터, 메타 데이터 포함

패킷 데이터를 구조체가 직접 포함하고 있거나, 포인터를 사용해서 참조하고 있다. 그림 5에서는 패킷 일부는 (Ethernet부터 buffer까지) 데이터 포인터를 이용해 참조하고 있고, 추가 데이터(frags)는 실제 페이지를 참조하고 있다.

메타 데이터 영역에는 헤더, 페이로드 길이 등 필요한 정보를 저장한다. 예를 들어, 그림 5에서 볼 수 있는 mac_header에는 Ethernet 헤더, network_header에는 IP 헤더, transport_header에는 TCP 헤더 시작 위치를 가리키고 있는 포인터 데이터가 있다. 이런 방식은 TCP 프로토콜 매우 편리하게 처리할 수 있게 한다.

헤더 추가, 삭제

네트워크 스택의 각 레이어를 왔다갔다하며 헤더를 추가, 삭제한다. 효율적으로 처리하기 위해 포인터들을 사용한다. 예를 들어, Ethernet 헤더를 제거하려면, head 포인터를 증가하면 된다.

패킷 결합, 분리

Socket buffer에 패킷 페이로드 데이터를 추가, 삭제, 또는 패킷 체인 같은 작업을 효율적으로 수행하기 위해 linked list를 사용한다. next, prev 포인터가 이 용도로 사용된다.

빠른 할당(allocation)과 반환(free)

패킷을 생성할 때마다 구조체를 할당하기 때문에 빠른 allocator를 사용한다. 예를 들어, 10 Gigabit Ethernet 속도로 데이터를 전송하면 초당 1백만 패킷 이상을 생성, 제거해야 한다.

TCP control block

둘째, TCP 연결을 대표하는 구조체가 있다. 앞서 추상적으로 TCP control block이라 불렀는데, Linux는 tcp_sock을 사용한다. 그림 7에서 file, socket과 tcp_sock 등이 어떤 관계에 있는지 알 수 있다.

networkstack6

그림 6 TCP 연결 구조체

시스템 콜이 발생하면 시스템 콜을 호출한 애플리케이션이 사용하는 file descriptor에 있는 file을 찾는다. Unix 계열 운영체제에서는 socket, 저장을 위한 일반 files system 용 file, 디바이스 등 여러 가지를 모두 file로 추상화한다. 따라서 file 구조체는 최소한의 정보만 포함한다. Socket의 경우 별도 socket 구조체가 소켓 관련 정보를 저장하고, file은 socket을 포인터로 참조한다. Socket은 다시 tcp_sock을 참조한다. tcp_sock은 sock, inet_sock 등으로 세분화되어 있는데, TCP 외의 다양한 프로토콜을 지원하기 위해서다. 일종의 폴리모피즘과 비슷하다고 보면 되겠다.

tcp_sock에는 TCP 프로토콜이 사용하는 모든 상태 정보를 저장한다. 예를 들어, 시퀀스 번호, receive window, congestion control, 재전송 타이머 등 정보가 모두 모여 있다.

Send socket buffer와 receive socket buffer는 sk_buff 리스트이고, tcp_sock을 포함한다. IP routing 결과물인 dst_entry도 참조해서 매번 routing하지 않도록 한다. dst_entry를 사용해서 ARP 결과, 즉 목적지 MAC 주소도 쉽게 찾는다. dst_entry는 routing table의 일부이다. routing table의 구조는 상당히 복잡해서 이 글에서는 다루지 않겠다. dst_entry를 사용해서 패킷 송신에 사용해야 하는 NIC를 찾는다. NIC는 net_device 구조체로 표현한다.

따라서 file만 찾으면 TCP 연결을 처리하는데 필요한 모든 구조체(file부터 드라이버까지)를 포인터로 쉽게 찾을 수 있다. 이들 구조체의 크기가 TCP 연결 하나가 사용하는 메모리의 양이다. 메모리의 양은 수 KB 정도(패킷 데이터 제외)다. 메모리 사용량도 기능이 추가되면서 꾸준히 증가했다.

마지막으로 TCP 연결 lookup table이 있다. 해시 테이블(hash table)인데, 수신된 패킷이 속하는 TCP 연결을 찾는데 사용한다. 해시 값은 패킷의 <소스 IP, 타깃 IP, 소스 port, 타깃 port>를 입력 데이터로 하고, Jenkins hash 알고리즘을 사용해서 계산한다. 해시 함수는 해시 테이블 공격에 대한 방어를 고려해서 선택했다고 한다.

코드 따라가기: 데이터 전송

실제 Linux 커널 소스 코드를 따라가며 스택이 수행하는 주요 작업을 알아보자. 자주 사용하는 경로 두 개만 살펴보겠다.

우선 애플리케이션이 write 시스템 콜을 호출할 때 데이터가 전송되는 경로를 보자.

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, ...)  
{
struct file *file;  
[...]
file = fget_light(fd, &fput_needed);  
[...] ===>
ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);

struct file_operations {  
[...]
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, ...)  
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, ...)  
[...]
};

static const struct file_operations socket_file_ops = {  
[...]
.aio_read = sock_aio_read,
.aio_write = sock_aio_write,
[...]
};

write 시스템 콜을 호출하면 커널이 파일 레이어의 write() 함수를 수행한다. 우선 file descriptor fd의 실제 file 구조체를 가져온다. 그리고 aio_write를 호출한다. 이것은 함수 포인터이다. file 구조체를 보면 file_operations 구조체 포인터가 있는데, 이 구조체는 흔히 부르는 function table이고, aio_read, aio_write등 함수 포인터를 포함한다. 실제 소켓용 table은 socket_file_ops이다. 소켓이 사용하는 aio_write 함수는 sock_aio_write이다. Function table은 Java 인터페이스와 유사한 용도로 사용한다. 커널이 코드 추상화나 리팩토링(refactoring)을 할 때 흔히 사용한다.

static ssize_t sock_aio_write(struct kiocb *iocb, const struct iovec *iov, ..)  
{
[...]
struct socket *sock = file->private_data;  
[...] ===>
return sock->ops->sendmsg(iocb, sock, msg, size);

struct socket {  
[...]
struct file *file;  
struct sock *sk;  
const struct proto_ops *ops;  
};

const struct proto_ops inet_stream_ops = {  
.family = PF_INET,
[...]
.connect = inet_stream_connect,
.accept = inet_accept,
.listen = inet_listen, .sendmsg = tcp_sendmsg,
.recvmsg = inet_recvmsg,
[...]
};

struct proto_ops {  
[...]
int (*connect) (struct socket *sock, ...)  
int (*accept) (struct socket *sock, ...)  
int (*listen) (struct socket *sock, int len);  
int (*sendmsg) (struct kiocb *iocb, struct socket *sock, ...)  
int (*recvmsg) (struct kiocb *iocb, struct socket *sock, ...)  
[...]
};

sock_aio_write 함수는 file에서 socket 구조체를 가져오고 sendmsg를 호출한다. 이것도 함수 포인터이다. Socket 구조체는 proto_ops function table을 포함한다. IPv4 TCP가 구현한 proto_ops는 inet_stream_ops이고, sendmsg는 tcp_sendmsg가 구현하고 있다.

int tcp_sendmsg(struct kiocb *iocb, struct socket *sock,  
struct msghdr *msg, size_t size)  
{
struct sock *sk = sock->sk;  
struct iovec *iov;  
struct tcp_sock *tp = tcp_sk(sk);  
struct sk_buff *skb;  
[...]
mss_now = tcp_send_mss(sk, &size_goal, flags);

/* Ok commence sending. */
iovlen = msg->msg_iovlen;  
iov = msg->msg_iov;  
copied = 0;  
[...]
while (--iovlen >= 0) {  
int seglen = iov->iov_len;  
unsigned char __user *from = iov->iov_base;

iov++;  
while (seglen > 0) {  
int copy = 0;  
int max = size_goal;  
[...]
skb = sk_stream_alloc_skb(sk,  
select_size(sk, sg),  
sk->sk_allocation);  
if (!skb)  
goto wait_for_memory;  
/*
* Check whether we can use HW checksum.
*/
if (sk->sk_route_caps & NETIF_F_ALL_CSUM)  
skb->ip_summed = CHECKSUM_PARTIAL;  
[...]
skb_entail(sk, skb);  
[...]
/* Where to copy to? */
if (skb_tailroom(skb) > 0) {  
/* We have some space in skb head. Superb! */
if (copy > skb_tailroom(skb))  
copy = skb_tailroom(skb);  
if ((err = skb_add_data(skb, from, copy)) != 0)  
goto do_fault;  
[...]
if (copied)  
tcp_push(sk, flags, mss_now, tp->nonagle);  
[...]
}

tcp_sengmsg는 socket에서 tcp_sock, 즉 TCP control block을 가져오고, 애플리케이션이 전송 요청한 데이터를 send socket buffer로 복사한다. 데이터를 sk_buff로 복사할 때 sk_buff 하나가 몇 바이트를 포함해야 할까? 실제 패킷을 생성하는 코드를 돕기 위해 sk_buff 하나가 MSS(tcp_send_mss) 바이트가 포함되도록 복사한다. MSS는 'Maximum Segment Size'로 TCP 패킷 한 개가 포함하는 최대 페이로드 크기다. TSO, GSO를 사용하면 sk_buff 한 개가 MSS보다 더 많은 데이터를 저장하는데, 이 부분은 다음 기회에 설명하겠다.

sk_stream_alloc_skb 함수가 새로운 sk_buff를 생성하고, skb_entail이 send_socket_buffer 꼬리에 새로운 sk_buff를 추가한다. skb_add_data 함수가 실제 애플리케이션 데이터를 sk_buff의 데이터 버퍼로 복사한다. 이 과정(sk_buff 생성, send socket buffer에 추가)을 여러 번 반복해서 모든 데이터를 복사한다. 결국은 MSS 크기의 sk_buff들이 send socket buffer에 리스트로 묶여 있는 모양이 된다. 끝으로 tcp_push를 호출해서 지금 전송할 수 있는 데이터를 패킷으로 만들어서 전송한다.

static inline void tcp_push(struct sock *sk, int flags, int mss_now, ...)  
[...] ===>
static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, ...)  
int nonagle,  
{
struct tcp_sock *tp = tcp_sk(sk);  
struct sk_buff *skb;  
[...]
while ((skb = tcp_send_head(sk))) {  
[...]
cwnd_quota = tcp_cwnd_test(tp, skb);  
if (!cwnd_quota)  
break;

if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now)))  
break;  
[...]
if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))  
break;

/* Advance the send_head. This one is sent out.
* This call will increment packets_out.
*/
tcp_event_new_data_sent(sk, skb);  
[...]

tcp_push 함수는 TCP가 허용하는 만큼 send socket buffer의 sk_buff를 차례대로 전송한다. 우선 tcp_send_head 호출해서 socket buffer의 가장 앞에 있는 sk_buff를 가져오고, tcp_cwnd_test, tcp_snd_wnd_test로 congestion window과 수신 TCP의 receive window가 새로운 패킷 전송을 허용하는지 확인한다. 그리고 tcp_transmit_skb 함수를 호출해서 실제 패킷을 생성한다.

static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,  
int clone_it, gfp_t gfp_mask)  
{
const struct inet_connection_sock *icsk = inet_csk(sk);  
struct inet_sock *inet;  
struct tcp_sock *tp;  
[...]


if (likely(clone_it)) {  
if (unlikely(skb_cloned(skb)))  
skb = pskb_copy(skb, gfp_mask);  
else  
skb = skb_clone(skb, gfp_mask);  
if (unlikely(!skb))  
return -ENOBUFS;  
}

[...]
skb_push(skb, tcp_header_size);  
skb_reset_transport_header(skb);  
skb_set_owner_w(skb, sk);

/* Build TCP header and checksum it. */
th = tcp_hdr(skb);  
th->source = inet->inet_sport;  
th->dest = inet->inet_dport;  
th->seq = htonl(tcb->seq);  
th->ack_seq = htonl(tp->rcv_nxt);  
[...]
icsk->icsk_af_ops->send_check(sk, skb);  
[...]
err = icsk->icsk_af_ops->queue_xmit(skb);  
if (likely(err <= 0))  
return err;

tcp_enter_cwr(sk, 1);

return net_xmit_eval(err);  
}

tcp_transmit_skb은 주어진 sk_buff의 복사본(pskb_copy)을 만든다. 이때 애플리케이션 데이터 전체를 복사하지 않고, 메타데이터만 복사한다. 그리고 skb_push를 호출해서 헤더 영역을 확보하고, 헤더 필드 값을 기록한다. send_check은 TCP checksum을 계산한다. Checksum offload를 사용하면 페이로드 데이터는 계산하지 않는다. 마지막으로, queue_xmit를 호출해서 IP 레이어로 패킷을 보낸다. IPv4 용 queue_xmit은 ip_queue_xmit 함수가 구현한다.

int ip_queue_xmit(struct sk_buff *skb)  
[...]
rt = (struct rtable *)__sk_dst_check(sk, 0);  
[...]
/* OK, we know where to send it, allocate and build IP header. */
skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));  
skb_reset_network_header(skb);  
iph = ip_hdr(skb);  
*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
if (ip_dont_fragment(sk, &rt->dst) && !skb->local_df)  
iph->frag_off = htons(IP_DF);  
else  
iph->frag_off = 0;  
iph->ttl = ip_select_ttl(inet, &rt->dst);  
iph->protocol = sk->sk_protocol;  
iph->saddr = rt->rt_src;  
iph->daddr = rt->rt_dst;  
[...]
res = ip_local_out(skb);  
[...] ===>
int __ip_local_out(struct sk_buff *skb)  
[...]
ip_send_check(iph);  
return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,  
skb_dst(skb)->dev, dst_output);  
[...] ===>
int ip_output(struct sk_buff *skb)  
{
struct net_device *dev = skb_dst(skb)->dev;  
[...]
skb->dev = dev;  
skb->protocol = htons(ETH_P_IP);

return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,  
ip_finish_output,  
[...] ===>
static int ip_finish_output(struct sk_buff *skb)  
[...]
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))  
return ip_fragment(skb, ip_finish_output2);  
else  
return ip_finish_output2(skb);  

ip_queue_xmit 함수는 IP 레이어에서 필요한 작업을 한다. __sk_dst_check은 캐시(cache)한 route가 유효한지 확인한다. 캐시한 route가 없거나 유효하지 않으면 IP routing을 한다. 그리고 skb_push를 호출해서 IP 헤더 영역을 확보하고, IP 헤더 필드 값을 기록한다. 이후 함수 호출을 따라가면, ip_send_check가 IP 헤더 checksum을 계산하고, netfilter 함수도 호출한다. ip_finish_output 함수가 IP fragmentation이 필요하면 fragment를 만든다. TCP 사용시 fragmentation은 발생하지 않는다. 결국은 ip_finish_output2가 호출되고, 이것이 Ethernet 헤더를 추가한다. 이로써 패킷이 완성된다.

int dev_queue_xmit(struct sk_buff *skb)  
[...] ===>
static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, ...)  
[...]
if (...) {  
....
} else
if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&  
qdisc_run_begin(q)) {  
[...]
if (sch_direct_xmit(skb, q, dev, txq, root_lock)) {  
[...] ===>
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, ...)  
[...]
HARD_TX_LOCK(dev, txq, smp_processor_id());  
if (!netif_tx_queue_frozen_or_stopped(txq))  
ret = dev_hard_start_xmit(skb, dev, txq);

HARD_TX_UNLOCK(dev, txq);  
[...]
}

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, ...)  
[...]
if (!list_empty(&ptype_all))  
dev_queue_xmit_nit(skb, dev);  
[...]
rc = ops->ndo_start_xmit(skb, dev);  
[...]
}

완성된 패킷은 dev_queue_xmit 함수를 통해 전송된다. 먼저 qdisc를 거친다. 기본 qdisc를 사용하고 큐가 비어있으면 sch_direct_xmit 함수를 호출해서 큐를 거치지 않고 패킷을 바로 드라이버로 내려 보낸다. dev_hard_start_xmit 함수가 실제 드라이버를 호출하는데, 드라이버를 호출하기 전에 디바이스 TX 락을 잡는다. 여러 스레드가 동시에 디바이스 접근하는 것을 막기 위해서다. 커널이 락을 잡기 때문에, 드라이버 전송 코드는 별도 락이 필요 없다. 다음 기회에 설명할 병렬 처리와 밀접한 관계가 있다.

ndo_start_xmit 함수가 드라이버 코드를 호출한다. 바로 전에, ptype_all, dev_queue_xmit_nit가 보인다. ptype_all은 패킷 캡쳐 같은 모듈을 포함하는 리스트다. 캡쳐 프로그램이 작동 중이면, 여기서 해당 프로그램으로 패킷을 복사한다. 따라서 tcpdump가 보여 주는 패킷은 드라이버로 전달되는 패킷이다. Checksum offload, TSO 등을 사용하면 NIC가 패킷을 조작하기 때문에, tcpdump 패킷은 실제 네트워크 선으로 전송되는 패킷과 다르다. 패킷 전송이 완료되면 드라이버 인터럽트 핸들러가 sk_buff를 반환한다.

코드 따라가기: 데이터 수신

둘째로 흔히 수행하는 경로는 패킷을 수신해서 receive socket buffer에 데이터를 추가하는 작업이다. 드라이버 인터럽트 핸들러 수행 후 napi poll 핸들부터 따라가 보자.

static void net_rx_action(struct softirq_action *h)  
{
struct softnet_data *sd = &__get_cpu_var(softnet_data);  
unsigned long time_limit = jiffies + 2;  
int budget = netdev_budget;  
void *have;

local_irq_disable();

while (!list_empty(&sd->poll_list)) {  
struct napi_struct *n;  
[...]
n = list_first_entry(&sd->poll_list, struct napi_struct,  
poll_list);  
if (test_bit(NAPI_STATE_SCHED, &n->state)) {  
work = n->poll(n, weight);  
trace_napi_poll(n);  
}
[...]
}

int netif_receive_skb(struct sk_buff *skb)  
[...] ===>
static int __netif_receive_skb(struct sk_buff *skb)  
{
struct packet_type *ptype, *pt_prev;  
[...]
__be16 type;  
[...]
list_for_each_entry_rcu(ptype, &ptype_all, list) {  
if (!ptype->dev || ptype->dev == skb->dev) {  
if (pt_prev)  
ret = deliver_skb(skb, pt_prev, orig_dev);  
pt_prev = ptype;  
}
}
[...]
type = skb->protocol;  
list_for_each_entry_rcu(ptype,  
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
if (ptype->type == type &&  
(ptype->dev == null_or_dev || ptype->dev == skb->dev ||
ptype->dev == orig_dev)) {  
if (pt_prev)  
ret = deliver_skb(skb, pt_prev, orig_dev);  
pt_prev = ptype;  
}
}

if (pt_prev) {  
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);

static struct packet_type ip_packet_type __read_mostly = {  
.type = cpu_to_be16(ETH_P_IP),
.func = ip_rcv,
[...]
};

앞서 설명했듯 net_rx_action 함수는 패킷을 수신하는 softirq 핸들러다. napi poll을 요청한 드라이버를 poll_list에서 가져와서 드라이버의 poll 핸들러를 호출한다. 드라이버는 수신한 패킷을 sk_buff로 포장하고, netif_receive_skb를 호출한다.

netif_receive_skb는 모든 패킷을 원하는 모듈이 있으면 그 모듈로 패킷을 전달한다. 패킷 전송 때와 같이 ptype_all 리스트에 등록된 모듈로 패킷을 전달한다. 패킷 캡처 작업은 여기서 수행된다.

그리고 패킷 종류에 따라 상위 레이어를 찾아 패킷을 전달한다. Ethernet 패킷은 헤더에 2 바이트 ethertype 필드를 포함한다. 이 값이 패킷 종류를 나타낸다. 이 값은 드라이버가 sk_buff에 기록한다(skb->protocol). 각 프로토콜은 자신만의 packet_type 구조체를 가지고, ptype_base hash table에 이 구조체의 포인터를 등록한다. IPv4는 ip_packet_type을 사용한다. Type 필드 값이 IPv4 ethertype (ETH_P_IP) 값이다. 따라서 IPv4 패킷은 ip_rcv 함수를 호출한다.

int ip_rcv(struct sk_buff *skb, struct net_device *dev, ...)  
{
struct iphdr *iph;  
u32 len;  
[...]
iph = ip_hdr(skb);  
[...]
if (iph->ihl < 5 || iph->version != 4)  
goto inhdr_error;

if (!pskb_may_pull(skb, iph->ihl*4))  
goto inhdr_error;

iph = ip_hdr(skb);

if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))  
goto inhdr_error;

len = ntohs(iph->tot_len);  
if (skb->len < len) {  
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);  
goto drop;  
} else if (len < (iph->ihl*4))
goto inhdr_error;  
[...]
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,  
ip_rcv_finish);  
[...] ===>
int ip_local_deliver(struct sk_buff *skb)  
[...]
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {  
if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))  
return 0;  
}

return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,  
ip_local_deliver_finish);  
[...] ===>


static int ip_local_deliver_finish(struct sk_buff *skb)  
[...]
__skb_pull(skb, ip_hdrlen(skb));  
[...]
int protocol = ip_hdr(skb)->protocol;  
int hash, raw;  
const struct net_protocol *ipprot;  
[...]
hash = protocol & (MAX_INET_PROTOS - 1);  
ipprot = rcu_dereference(inet_protos[hash]);  
if (ipprot != NULL) {  
[...]
ret = ipprot->handler(skb);  
[...] ===>

static const struct net_protocol tcp_protocol = {  
.handler = tcp_v4_rcv,
[...]
};

ip_rcv 함수는 IP 레이어에서 필요한 일을 한다. 길이, 헤더 checksum 등 패킷 검사를 한다. netfilter 코드를 거치면 ip_local_deliver 함수를 수행한다. 여기서 필요하면 IP fragment들을 조립한다. 그리고 다시 netfilter 코드를 통해서 ip_local_deliver_finish를 호출한다. 이 함수는 __skb_pull을 사용해서 IP 헤더를 제거하고, IP 헤더의 protocol 값과 일치하는 상위 프로토콜을 찾는다. Ptype_base와 유사하게 각 트랜스포트 프로토콜은 inet_protos에 자신의 net_protocol 구조체를 등록한다. IPv4 TCP는 tcp_protocol을 사용하고, handler로 등록한 tcp_v4_rcv를 호출한다.

TCP 레이어로 들어오면 TCP 상태, 패킷 종류에 따라 패킷 처리 흐름이 다양하다. 여기서는 TCP 연결이 ESTABLISHED 상태에서 다음 예상하는 데이터 패킷을 받았을 때 처리 과정을 알아본다. 패킷 유실, 역전 현상이 없으면 데이터를 받는 서버가 자주 수행하는 경로다.

int tcp_v4_rcv(struct sk_buff *skb)  
{
const struct iphdr *iph;  
struct tcphdr *th;  
struct sock *sk;  
[...]
th = tcp_hdr(skb);

if (th->doff < sizeof(struct tcphdr) / 4)  
goto bad_packet;  
if (!pskb_may_pull(skb, th->doff * 4))  
goto discard_it;  
[...]
th = tcp_hdr(skb);  
iph = ip_hdr(skb);  
TCP_SKB_CB(skb)->seq = ntohl(th->seq);  
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +  
skb->len - th->doff * 4);  
TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);  
TCP_SKB_CB(skb)->when = 0;  
TCP_SKB_CB(skb)->flags = iph->tos;  
TCP_SKB_CB(skb)->sacked = 0;

sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);  
[...]
ret = tcp_v4_do_rcv(sk, skb);  

우선 tcp_v4_rcv 함수는 받은 패킷이 올바른지 검사한다. 예를 들어, 헤더 크기가 데이터 오프셋보다 크면(th->doff < sizeof(struct tcphdr) / 4) 헤더 오류이다. 그리고 __inet_lookup_skb를 호출해서 TCP 연결 해시 테이블에서 패킷이 속하는 연결을 찾는다. 찾은 sock 구조체로부터 tcp_sock, socket 등 모든 필요한 구조체를 가져올 수 있다.

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)  
[...]
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */  
sock_rps_save_rxhash(sk, skb->rxhash);  
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {  
[...] ===>
int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,  
[...]
/*
* Header prediction.
*/
if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&  
TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&  
!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))) {
[...]
if ((int)skb->truesize > sk->sk_forward_alloc)  
goto step5;

NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS);

/* Bulk data transfer: receiver */
__skb_pull(skb, tcp_header_len);  
__skb_queue_tail(&sk->sk_receive_queue, skb);  
skb_set_owner_r(skb, sk);  
tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;  
[...]
if (!copied_early || tp->rcv_nxt != tp->rcv_wup)  
__tcp_ack_snd_check(sk, 0);  
[...]
step5:  
if (th->ack && tcp_ack(sk, skb, FLAG_SLOWPATH) < 0)  
goto discard;

tcp_rcv_rtt_measure_ts(sk, skb);

/* Process urgent data. */
tcp_urg(sk, skb, th);

/* step 7: process the segment text */
tcp_data_queue(sk, skb);

tcp_data_snd_check(sk);  
tcp_ack_snd_check(sk);  
return 0;  
[...]
}

tcp_v4_do_rcv 함수부터 실제 프로토콜을 수행한다. ESTABLISHED 상태는 tcp_rcv_esablished를 호출한다. ESTABLISHED 상태가 가장 흔하기 때문에 이 상태 처리를 별도로 떼어 내서 최적화한다. tcp_rcv_established는 header prediction 코드를 먼저 수행한다. Header prediction도 흔한 경우를 감지해서 빨리 처리한다. 여기서 흔한 경우는 보낼 데이터는 없고, 받은 데이터 패킷이 다음에 받아야 하는 패킷인 경우, 즉 시퀀스 번호가 수신 TCP가 기대하는 시퀀스 번호인 경우다. 데이터를 소켓 버퍼에 추가하고 ACK를 전송하면 끝이다.

좀 더 따라가 보면 truesize와 sk_forward_alloc을 비교하는 문장이 보인다. Receive socket buffer에 새로운 패킷 데이터를 추가할 여유 공간이 있는지 확인한다. 공간이 있으면 header prediction은 "hit" (prediction 성공)이다. __skb_pull를 호출해서 TCP 헤더를 제거하고, __skb_queue_tail을 호출해서 패킷을 receive socket buffer에 추가한다. 마지막으로, __tcp_ack_snd_check를 호출해서 ACK 전송이 필요하면 전송한다. 이것이 패킷 처리의 끝이다.

만약 여유 공간이 부족하면 느린 경로를 수행한다. tcp_data_queue 함수는 버퍼 공간을 새로 할당하고 데이터 패킷을 소켓 버퍼에 추가한다. 이때 가능하면 receive socket buffer 크기를 자동으로 증가한다. 빠른 경로와 다르게, tcp_data_snd_check를 호출해서 새로운 데이터 패킷을 전송할 수 있으면 전송하고, 끝으로 tcp_ack_snd_check 호출해서 ACK 전송이 필요하면 ACK 패킷을 생성해서 전송한다.

지금 따라가 본 두 경로가 수행하는 코드의 양은 많지 않다. 자주 발생하는 경우(common case)를 최적화한 덕분이다. 바꿔 말하면 예상하지 않은 경우(uncommon case)의 처리 과정은 눈에 띄게 느리다는 뜻도 된다. 패킷 역전(out-of-order delivery) 현상이 대표적인 예다.

드라이버와 NIC의 통신

드라이버와 NIC 사이의 통신은 스택 가장 밑단이고, 대개 신경 쓰지 않는다. 하지만 성능 문제를 해결하기 위해 NIC가 수행하는 일이 많아졌다. 기본적인 작동 방식을 이해하면 추가 기술을 이해하는 데 도움이 된다.

드라이버와 NIC는 비동기 방식으로 통신한다. 먼저 드라이버가 패킷 전송을 요청하고(호출), CPU는 응답을 기다리지 않고 다른 작업을 수행한다. 이후 NIC가 패킷을 전송하고 CPU에 이 사실을 알리면 드라이버가 전송된 패킷을 반환한다(결과 리턴). 수신도 이와 같이 비동기 방식으로 이루어진다. 먼저 드라이버가 수신 요청을 하고 CPU는 다른 작업을 수행한다(호출). 이후 NIC가 패킷을 받으면 CPU에 이 사실을 알리고, 드라이버가 받은 패킷을 처리한다(결과 리턴).

따라서 요청, 응답을 저장하는 장소가 필요하다. 대개 NIC는 링(ring) 구조체를 사용한다. 링은 일반 큐 구조체와 유사하다. 고정된 수의 엔트리를 가지고, 한 엔트리가 한 요청 혹은 응답 데이테를 저장한다. 이들 엔트리들을 차례대로 돌아가며 사용한다. 돌아가며 고정된 엔트리들을 재사용하기 때문에 흔히 링이란 이름을 사용한다.

다음 그림의 패킷 전송 과정을 따라가며 링을 어떻게 사용하는지 알아보자.

networkstack7

그림 7 드라이버-NIC 통신: 패킷 전송

드라이버가 상위 레이어로부터 패킷을 받고, NIC가 이해하는 전송 요청(send descriptor)을 생성한다. send descriptor에는 기본적으로 패킷 크기, 메모리 주소를 포함하도록 한다. NIC는 메모리에 접근할 때 필요한 물리적 주소가 필요하다, 따라서 드라이버가 패킷의 가상 주소를 물리적 주소로 변경한다. 그리고 send descriptor를 TX ring에 추가한다(1). TX ring이 전송 요청 링이다.

그리고 NIC에 새로운 요청이 있다고 알린다(2). 특정 NIC 메모리 주소에 드라이버가 직접 데이터를 쓴다. 이와 같이 CPU가 디바이스에 직접 데이터를 전송하는 방식을 PIO(Programmed I/O)라고 한다.

연락을 받은 NIC는 TX ring의 send descriptor를 호스트 메모리에서 가져온다(3). CPU의 개입 없이 디바이스가 직접 메모리에 접근하기 때문에, 이와 같은 접근을 DMA(Direct Memory Access)라고 부른다.

Send descriptor를 가져와서 패킷 주소와 크기를 판단하고, 실제 패킷을 호스트 메모리에서 가져온다(4). Checksum offload 방식을 사용하면 메모리에서 패킷 데이터를 가져올 때 checksum을 NIC가 계산하도록 한다. 따라서 오버헤드는 거의 발생하지 않는다.

NIC가 패킷을 전송하고(5), 패킷을 몇 개 전송했는지 호스트의 메모리에 기록한다(6). 그리고 인터럽트를 보낸다(7). 드라이버는 전송된 패킷 수를 읽어 와서 현재까지 전송된 패킷을 반환한다.

다음 그림에서는 패킷 수신 과정을 볼 수 있다.

networkstack8

그림 8 드라이버-NIC 통신: 패킷 수신

우선 드라이버가 패킷 수신용 호스트 메모리 버퍼를 할당하고, receive descriptor를 생성한다. receive descriptor는 기본으로 버퍼의 크기, 주소를 포함한다. send descriptor와 같이 DMA가 사용하는 물리적 주소를 descriptor에 저장한다. 그리고 RX ring에 descriptor를 추가한다(1). 결국 이것이 수신 요청이고, RX ring은 수신 요청 링이다.

드라이버가 PIO를 통해서 NIC에 새로운 descriptor가 있다고 알린다(2). NIC는 RX ring의 새로운 descriptor를 가져온다. 그리고 descriptor에 포함된 버퍼의 크기, 위치를 NIC 메모리에 보관한다(3).

이후 패킷이 도착하면(4), NIC는 호스트 메모리 버퍼로 패킷을 전송한다(5). Checksum offload 기능이 있다면 NIC가 이때 checksum을 계산한다. 도착한 패킷의 실제 크기와 checksum 결과, 그 외 다른 정보는 별도의 링(receive return ring)에 기록한다(6). Receive return ring은 수신 요청 처리 결과, 즉 응답을 저장하는 링이다. 그리고 NIC가 인터럽트를 보낸다(7). 드라이버는 receive return ring에서 패킷 정보를 가져와서 받은 패킷을 처리한다. 필요에 따라 새로운 메모리 버퍼를 할당하고 (1)~(2) 단계를 반복한다.

스택 튜닝이라고 하면 흔히 ring, interrupt 설정을 조절해야 한다고 이야기한다. TX ring이 크면 한 번에 많은 수의 전송 요청을 할 수 있다. RX ring이 크면 한 번에 많은 수의 수신을 할 수 있다. 패킷 송신, 수신 burst가 많은 워크로드에는 큰 링이 도움이 된다. 그리고 CPU가 인터럽트를 처리하는 오버헤드가 크기 때문에, 대개 NIC은 인터럽트 회수를 줄이기 위해 타이머를 사용한다. 패킷을 전송하고 수신할 때 매번 인터럽트를 보내지 않고 주기적으로 모아서 보낸다(interrupt coalescing).

스택 내부 버퍼와 제어 흐름(flow control)

스택 내부의 여러 단에서 flow control을 수행한다.

그림 9는 데이터를 전송할 때 사용하는 버퍼를 보여 준다. 우선, 애플리케이션이 데이터를 생성하고 send socket buffer에 추가한다. 공간이 없으면 시스템 콜이 실패하거나, 애플리케이션 스레드에 블로킹이 발생한다. 따라서 커널로 유입되는 애플리케이션 데이터의 속도는 socket buffer 크기 제한을 통해 제어하도록 한다.

networkstack9

그림 9 패킷 전송에 관계된 버퍼들

TCP가 패킷을 생성해서 드라이버로 보낼 때는 transmit queue(qdisc)를 통하도록 하고 있다. 기본적인 FIFO 큐 형태이고, 큐의 최대 길이는 ifconfig 명령어를 실행할 때 확인할 수 있는 txqueuelen의 값이다. 보통 수 천 패킷 정도이다.

드라이버와 NIC 사이에는 TX ring이 있다. 앞서 설명했듯, 전송 요청 큐로 보면 된다. 큐 공간이 없으면 전송 요청을 못하고 패킷은 transmit queue에 적체된다. 너무 많이 적체되면 패킷 드롭을 한다.

NIC는 내부 버퍼에 전송할 패킷을 저장한다. 이 버퍼에서 패킷이 빠져나가는 속도는 우선 물리적 속도에 영향을 받는다(예: 1 Gb/s NIC가 10 Gb/s 성능을 낼 수는 없다). 그리고 Ethernet flow control을 사용하면 수신 NIC 버퍼에 공간이 없을 때는 전송이 멈춘다.

커널이 전송하는 패킷 속도가 NIC가 전송하는 속도보다 빠르면, 우선 NIC 내부 버퍼에 패킷이 적체된다. 버퍼에 공간이 없으면 TX ring의 전송 요청 처리를 멈춘다. TX ring에 점점 많은 요청이 적체되고, 결국은 큐 공간이 없어진다. 드라이버는 전송 요청을 못하고 패킷은 transmit queue에 적체된다. 이와 같이 여러 버퍼를 통해 backpressure가 밑에서 위로 올라간다.

그림 10은 반대로 수신한 패킷이 거쳐가는 버퍼를 보여 준다. 패킷은 NIC 내부 수신 버퍼에 저장된다. Flow control 관점에서 보면 드라이버와 NIC 사이의 RX ring를 패킷 버퍼로 생각하면 된다. RX ring에 들어간 패킷은 드라이버가 꺼내서 상위 레이어로 보낸다. 서버 장비가 사용하는 NIC 드라이버는 기본으로 NAPI를 사용하기 때문에 드라이버와 상위 레이어 사이에 버퍼는 없다. 상위 레이어가 RX ring에서 직접 패킷을 가져간다고 생각하면 된다. 그리고 패킷의 페이로드 데이터는 receive socket buffer에 들어간다. 이후 애플리케이션이 socket buffer에서 데이터를 가져간다.

networkstack10

그림 10 패킷 수신에 관계된 버퍼들

NAPI를 지원하지 않는 드라이버는 backlog queue에 패킷을 넣고, 후에 NAPI 핸들러가 패킷을 꺼내간다. 따라서, backlog queue는 상위 레이어와 드라이버 사이 버퍼로 보면 된다.

커널의 패킷 처리 속도가 NIC로 유입되는 패킷 속도보다 느리면 RX ring 공간이 없어진다. 그리고 NIC 내부 버퍼 공간도 없어진다. Ethernet flow control을 사용하면 NIC가 송신 NIC에 송신 정지 요청을 보내거나 패킷 드롭을 한다.

TCP는 end-to-end flow control을 지원하기 때문에, receive socket buffer 공간 부족으로 인한 패킷 드롭은 없다. 하지만 UDP는 flow control을 지원하지 않기 때문에, 애플리케이션 속도가 느리면 socket buffer 공간 부족으로 패킷 드롭이 발생한다.

그림 9와 그림 10에서 드라이버가 사용하는 TX ring의 크기와 RX ring의 크기가 ethtool이 보여 주는 링의 크기다. 대개 throughput을 중요시하는 워크로드에는 링의 크기, socket buffer 크기를 늘리면 도움이 된다. 많은 패킷을 빠른 속도로 전송, 수신할 때 버퍼 공간 부족으로 인한 실패 확률이 줄어들기 때문이다.

끝으로

네트워크 프로그램, 성능 실험, 트러블슈팅에 도움이 될만한 것들만 설명하려 했는데 정리하고 보니 내용이 적지 않게 되었다. 네트워크 애플리케이션을 개발하거나 성능 모니터링을 할 때 도움이 되었으면 한다. TCP/IP 프로토콜 자체는 상당히 복잡하고 예외 케이스도 많다. 하지만 성능 이해와 현상 분석을 하기 위해 운영체제의 TCP/IP 관련 코드 한 줄 한 줄을 모두 이해하고 있을 필요는 없다. 큰 흐름만 알고 있어도 많은 도움이 된다.

장비 성능과 운영체제의 네트워크 스택 구현이 꾸준히 발전해서, 최신 서버는 10-20 Gb/s 정도의 TCP throughput은 무리 없이 달성하고 있다. TSO, LRO, RSS, GSO, GRO, UFO, XPS, IOAT, DDIO, TOE 등 alphabet soup 같이 성능 관련한 기술 종류들이 많아서 오히려 혼란스러울 정도이다.

다음 글에서는 성능관점에서 스택을 살펴보고, 이 기술들이 해결하려는 문제와 효과를 설명하겠다.

반응형
반응형

출처 : http://wonmaum.net/bbs/board.php?bo_table=AIX&wr_id=2



CPU
. %sys + %user 가 80% 이상이면 병목으로 판단
- sys : kernel 부분이 수행되는 영역, 프로세스 스케줄링, I/O, drive, network과 관련
- wait : 디스크 I/O 작업이 끝날 때가지 기다리는 idle time
- user : 사용자 프로세스, application, database등
- idle
. 모니터링 명령어
# sar 1 5 : 1초 5회 평균 CPU 사용율
# sar -P ALL 1 5 : 각 Processor 별 사용율
# sar -q 1 5 : 시스템에서 사용한 평균 queue 및 process 표시
- runq-sz : 수행되고 있는 kernel thread의 평균 수
- %runocc : queue 가 실행된 시간의 백분율
- swpq-sz : wait 상태인 kernel thread의 평균 수
- swpocc : swap 상태인 시간의 백분율
# vmstat 1 5 : cpu 및 memory 사용율
- r : 수행중인 kernel thread 수
- b : swap에 대기중인 kernel thread 수
# ps aux
- %CPU : 프로세스가 수행된 후 사용한 CPU 시간을 전체 경과시간으로 나눈 백분율
- %MEM : 프로세스가 수행될 때 사용한 실 메모리 백분율
- SZ : 프로세스의 data 영역을 위해 할당된 virtual page size로 1Kbyte 단위
- RSS : 프로세스의 실 메모리 크기 1Kbyte 단위
Memory
. paging space In/Out이 10 이상이거나 page fault가 1000번 이상 발행하면 병목으로 판단
. memory 부족인지 user application이나 file cache에 의한 것인지 고려
- free : file cache나 프로세스가 사용하고 있지 않는 영역
- fie cache : 사용 가능한 메모리를 파일 작업시 I/O 성능 향상을 위해 cache로 사용
- paging(4KB) : 실 메모리가 부족한 경우 발생, 실 메모리에서 사용되지 않았던 영역을 디스크로 옮기고
그 부분을 free 영역으로 전환하여 process를 처리
- page fault : kernel이 필요한 data나 process의 페이지를 실 메모리에서 찾지 못하는 횟수
- page In : 필요한 page만큼 virtual memory manager(VMM)에 의해 paging space에서 read하는 초당 횟수
page out 값에 따라 발생하는 real I/O 값
- page Out : 실 메모리 free 영역 확보를 위해 VMM이 paging space에서 read out하는 초당 회수
page in 값에 따라 발생하는 real I/O 값
- page space In : paging 영역에서만 발생하는 page read
- page space out : paging 영역에서만 발생하는 page out
. 모니터링 명령어
# vmstat 1 5
- avm : 프로세스가 수행되는 동안 paging 공간에 할당된 active virtual page
- fre : VMM이 필요시 사용할 수 있는 memory free list
- re : page input/output list
- pi : paging space에서 read in 된 page
- po : paging space에서 read out 된 page
- fr : paged freed(page replacement)
- sr : page-replacement 알고리즘에 따라 page 찾기
- cy : page-replacement 알고리즘에 따른 clock cycle
# svmon -G
- memory : 실 메모리 사용 통계
. size
. inuse : 프로세스나 file cache로 사용되는 메모리
. free
. pin : pinned 영역에서 사용되는 메모리
. virtual : virtual 영역에 할당된 메모리
- pg sapce : paging 영역의 사용 통계
. size
. inuse
- pin : pinned 영역의 사용 통계
. work : working(프로세스) 메모리 중 pinned 영역에서 사용되는 메모리
. pers : persistent(file cache) 메모리 중 pinned 영역에서 사용되는 메모리
. clnt : client(NFS, cd) 메모리 중 pinned 영역에서 사용되는 메모리
- in use : 실 메모리 사용 통계
. work
. pers
. clnt
DISK I/O
. %iowait가 40% 이상이거나 사용자 application에 의해 특정 disk의 %tm_act가 70%를 초과하는 경우 병목으로 판단
- read : 초당 read 하는 데이터 크기 1Kbyte 단위
- write
- busy% : 디스크에 작업한 시간을 경과 시간으로 나눈 백분율
. 모니터링 명령어
# iostat
- %tm_act : 물리적 디스크에서 작업한 시간의 백분율
일반적으로 40%를 초과하게 되면 I/O를 완료하는 시간이 길어져 응답시간이 증가
- Kbps : 초당 디스크에 이동된 데이터 양 Kbype 단위
- tps : 초당 디바이스 드라이브 단계에서 해당 물리적 디스크로 I/O를 요청하는 횟수
- Kb_read : 물리적 디스크에 read한 양 Kbyte 단위
- Kb_wrtn : 물리적 디스크에 write한 양 Kbypte 단위
Network Traffic
. network bandwidth의 30% 이상을 사용하는 경우 병목으로 판단
- read
- write
. 모니터링 명령어
# netstat -m
# netstat -v
# netstat -ni
topas 명령어를 이용한 모니터링

1. CPU
. Kernel : CPU의 현재 사용량, 70이 넘어가면 점검 필요
. User : 각 프로세스들이 사용하는 CPU 사용량
. Wait : 처리 대기중인 프로세스의 양(10미만)
. Idle :
2. Memory
. Real,MB : 메모리의 실제 크기(MByte)
. %Comp : 현재 메모리 사용율이며, 높을수록 좋다
. %Noncomp : file cache로 할당된 영역
. %Client : aix의 file system을 위한 영역
3. Paging Space
. Size,MB : Paging Memory로 할당된 크기
. %Used : 사용중인 양, 낮을수록 좋다
. %Free
=================================================
# uname –amML : 서버 정보 확인
# uname -L : 서버 시리얼넘버 확인
# oslevel –s : OS버전확인(TL SP확인)
# instfix –i |grep ML(and SP) : OS버전 확인
# ls –al .*Com* : 패치 확인
# lsattr –El mem0 : 메모리 용량 확인
# topas : 시스템(프로세스, 메모리, 네트워크) 체크.
# lsdev –Cc disk : 디스크 확인
# lspv : 디스크 활성상태확인
# lsvg –l [볼륨그룹명] : 볼륩그룹 확인
# ifconfig – a : ip정보확인
# netstat –rn : 라우트 정보 확인 (디폴트게이트웨이 확인)
# ping [IP or Host Name] : ip 접근확인
# lsdev –Cc if : 네트워크 인터페이스 확인
# lsdev –Cc adapter : 네트워크 카드 확인
# lsdev –Cc pci : pci슬롯 확인.
# lscfg –v : FRU 확인. (lsdev –Cc, lsdev –P)
# lsconf : 장치정보
# df –k : 파일시스템 용량체크 [ -k (kilo bytes), -m (mega bytes), -g (giga bytes) ]
# lsdev –Cc processor : 프로세tm 수 확인
# ps –ef | grep [ Process Name or Pid] : 실행중인 프로세스 검색
# du –sk * : 하위 디렉토리 및 파일 용량정보 출력
=================================================
****************** IBM bundles install. ****************************
#vi /usr/sys/inst.data/user_bundles/MyBundle.bnd
I:sysmgt.websm.security. // write the file which you want to install.
#smitty easy_install
// select Mybundle which you've made and Enter.
lslpp -Lb Mybundle // verify whethere bundle was installed successfully.
smitty list_installed // verify what is installed in server.
******************** AIX 용량 확장하기 ******************************
lsvg rootvg
chfs -a size=+100M /
******************** AIX 네트워크 설정하기 **************************
smit chgenet
smitty mktcpip -> N/W interface 선택
******************** 파일셋 및 fix(AIX) 설치 여부 확인 **************
lslpp -l bos.adt.base // 파일 셋 인스톨 여부 체크
instfix -i -k "IY4390" // fix 설치 여부 체크

******************** AIX 데몬 재시작 ********************************
refresh -s inetd
startsrc -g inetd
IBM filesets 설치

#oslevel -r // OS 레벨 확인
#instfix -i|grep ML // 파일셋 버전별 설치 현황
#instfix -icqk 5300-02_AIX_ML|grep :-: // 업데이트가 필요한 filesets
download site.
www.ibm.com/servers/eserver/support/pseries/aixfixes.html
***************************** cd mount ************************************
#mount -vcdrfs -oro /dev/cd0 /mnt
#geninstall -d/dev/cd0 bos.alt_disk_install.rte // install fileset in cd.
lspv // see hard-drive information.
#bootlist -m normal hdisk1 hdisk13 // select which disk you want to boot
# bootlist -m normal -o // see which disk is on.
hdisk13

반응형
반응형

출처 : http://ttend.tistory.com/112



ㅇ 실제 메모리 용량 확인

서버가 사용하고 있는 물리적 메모리의 양은 다음 명령어로 확인할 수 있다.

# lsattr -El sys0 -a realmem

# lsattr -El sys0 -a realmem

realmem 31457280 Amount of usable physical memory in Kbytes False

31457280은 KB이며 /1024/1024 해보면 30G임을 알 수 있다

제일 뒤 "false"는 명령을 통해 변경될 수 없다는 의미이다. 


ㅇ paging space

paging space는 물리적 RAM의 추가설치에 대한 경제적 대안이다. 즉 디스크의 일부분을 RAM처럼 사용하면서 프로그램의 활성부분은 RAM에 위치시키고 비활성 부분은 paging space에 위치시켜서 프로그램이 사용하는 RAM의 용량을 늘릴 수 있기 때문이다. 

RAM과 paging space는 페이지 프레임이라는 4KB 섹션으로 나뉘어 있다. RAM에 있는 모든 페이지 프레임에 대해, paging space의 하나 이상의 페이지 프레임이 하드 디스크 상에 존재한다. 시스템이 더 많은 RAM을 필요로 할 때, 정보의 페이지 프레임은 RAM에서 나와 하드 디스크로 이동한다. 이를 페이지 아웃(paging out)이라고 한다. 반대로 디스크에 있는 정보가 RAM으로 이동하는 것을 페이지 인(paging in)이라고 한다


ㅇ 가상 메모리 관리자(VMM)

가상 메모리 관리자(VMM)이 페이지의 이동을 관리한다. VMM은 최근에 사용되지 않은 페이지 프레임을 선택해서 디스크의 paging space으로 옮긴다


ㅇ 스래싱(thrashing)

VMM이 막 사용되려던 페이지 프레임을 페이지 아웃한 경우, VMM은 이 페이지 프레임을 곧바로 페이지 인해야 한다. 시스템이 하고자 하는 작업을 수행하는 대신 RAM에 페이지 프레임을 넣고 꺼내는 데(shuffling in/out) 더 많은 시간을 소비할 때, 이 시스템은 스래싱 중이라고 말한다. 물리 메모리의 양이 적으면 스래싱을 자주 일으킬 수 있다. (시스템이 스래싱 중이라면 시스템의 응답이 느려지고 디스크의 불이 끊임없이 반짝거릴 것이다)


ㅇ 사용가능한 paging space

시스템은 사용 가능한 paging space량을 모니터링하고 있다. 사용 가능한 paging space량이 임계값(paging space 경고 레벨이라고 함) 아래로 떨어지면, 시스템은 모든 프로세스(커널 프로세스 제외)에 SIGDANGER 신호를 보내고 이 신호는 프로세스에게 정상적으로 종결하도록 지시한다.

빈 paging space량이 두 번째 임계값(paging space kill 레벨) 아래로 더 떨어지면, 시스템은 대부분의 paging space을 사용 중인 프로세스에 SIGKILL 신호를 보내고, 이 신호는 프로세스에게 강제적으로 종결하도록 지시한다.


ㅇ 기본 paging space

AIX를 설치하면, 설치 디스크(일반적으로 하드 디스크 hdisk0) 상에 paging space을 자동으로 작성한다. 

이 paging space의 이름은 항상 hd6이다. 

더 많은 paging space가 필요한 경우, hd6의 크기를 증가시키는 대신 새로운 paging space을 작성해야 한다. 


ㅇ paging space 확인

명령어 lsps로 확인할 수 있다

# lsps -a

Page Space      Physical Volume   Volume Group    Size %Used Active  Auto  Type

hd6             hdisk0            rootvg       10240MB     2   yes   yes    lv

위 그림에서 paging space는 hdisk0에 위치하고 현재 2%를 사용하고 있음을 알 수 있다.

만약 paging space을 두 개 이상 사용하고 있다면 lsps -s 명령어로 전체 paging space의 크기와 사용률을 알 수 있다


ㅇ paging space 경고

페이지 공간이 계속 70% 이상을 사용하고 메모리가 부족하면 단말에 다음과 같은 메시지가 표시된다. 

- INIT: paging space가 부족함 

- 메모리가 충분하지 않음 

- Fork 기능 실패 

- Fork() 시스템 호출 실패 

- Fork할 수 없습니다. 프로세스가 너무 많습니다. 

- Fork 실패 사용할 수 있는 메모리가 충분하지 않음 

- Fork 기능을 지원하지 않음. 사용할 수 있는 메모리가 충분하지 않음 

- Fork할 수 없음. 공간이 충분하지 않음


ㅇ paging space 설정 팁

- 디스크 당 둘 이상의 paging space을 갖지 않는 것이 좋다. paging space는 라운드 로빈(round-robin) 방식으로 할당되어 모든 페이징 영역을 동등하게 사용하기 때문에 하나의 디스크에 두 개의 페이징 영역이 있으면, 더 이상 활동을 여러 디스크에 전개할 수 없다. 

- paging space에는 많은 활동을 하지 않는 디스크를 사용하는 것이 유리하다. paging space는 다른 활동들과 경쟁하지 않을 때에 최상의 성능을 발휘할 수 있다. 

- paging space는 대략 같은 크기여야 한다. 라운드 로빈(Round-robin) 기법을 사용하므로 같은 크기가 아닐 경우, 사용의 균형이 맞지 않게 된다

- paging space을 여러 실제 볼륨으로 확장하지 않는 것이 좋다. 페이징 영역(일반 논리 볼륨처럼)을 여러 디스크에 걸쳐 전개할 수 있더라도, 라운드 로빈 (round-robin) 기법은 이 페이징 영역을 단일 페이징 영역으로 취급하므로, 이 활동은 디스크에 고르게 전개되지 않기 때문이다. 


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

출처 : http://blog.boxcorea.com/wp/archives/1458



1.현재 paging device 확인



2. swap 사용량확인




3. swap 추가하기
여기서는 foxvg에 1GB swap을 추가한다. foxvg의 PP size는 256MB 이다. 따라서 1GB 추가하려면, 4개의 LP를 사용한다.



4. 추가한 swap 사용하기
paging space를 사용하려면, swapon 커맨드로 swap을 활성화해준다.



5. swap 삭제하기
삭제전, swap 을 사용하지 않도록 설정한다.


6. 기타
chps : paging space의 속성을 변경할 때 사용한다.
예) 재부팅했을때, ps 사용하기



paging space 관련 화일: /etc/swapspaces






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

pageSpace 확장법 : http://serendipity777.tistory.com/155

Physical 메모리 100% 사용안하는 경우 : http://cafe.naver.com/aix/13491

실제 메모리를 많이 쓰는것 같지 않은데 메모리 사용률이 높게 나올때 : http://blog.naver.com/lyg1019?Redirect=Log&logNo=90126726851






반응형
반응형

해당 내역은 차후 지속적으로 추가하기로 한다...

일단 본인이 알아볼 수 있을 정도로 적어놓고 포스팅 내용은 늘리는걸로..


참고 : https://access.redhat.com/documentation/ko-KR/Red_Hat_Enterprise_Linux/6/html/Logical_Volume_Manager_Administration/lv_extend.html

참고 : http://pinglish.tistory.com/entry/LVM-%EB%85%BC%EB%A6%AC%EB%B3%BC%EB%A5%A8-%ED%99%95%EC%9E%A5



1. PV

1) pvdisplay -> 현재 디스크 용량 및 종류 확인.

-> 새로운 디크크가 추가되었을 때 확인. 


pvcreate /dev/sdb1 등으로 새로운 디스크를 물리 볼륨에 추가한다.

-> 가상머신의 경우 각 툴에 맞는 Disk Extend를 진행하여 디스크 크기 확장.



2. VG - GParted를 사용

1) vgdisplay -v 

현재 GParted를 사용하여 해당 볼륨 그룹의 크기에 대해 간단하게 확장 진행.

GParted로 볼륨그룹 크기 늘린후 FREE PE / SIZE 확인.



3. LV

1) lvdisplay

-> 현재 논리 볼륨 확인.


논리 뷸륨의 크기를 확장하려면 lvextend 명령을 사용.

lvextend -l 인수를 사용하여 논리 볼륨 크기를 확장할 익스텐드 수를 지정.

불륨 그릅의 퍼센트나 블륨 그룹에 남아 있는 여유 공간의 퍼센트도 지정할 수 있음.


ex) lvextend -l +100%FREE /dev/vg_9rh656/lv_root


4. 논리 볼륨 확장 후 해당 파일 시스템의 크기를 확장

ex) resize2fs /dev/vg_9rh656/lv_root


4-1. lvm 파일 시스템(/etc/fstab에서 확인할 시 xfs)의 경우 resize2fs 가 아닌 xfs_growfs 를 사용하도록 한다.



5. 이후 df -h 등으로 용량 변화된것 확인.











반응형
반응형

출처 : http://www.sysnet.pe.kr/Default.aspx?mode=0&sub=0&pageno=7&detail=1&wid=269



영문 윈도우를 설치하는 분들이 가끔 겪는 문제이죠. 어떤 경우에는 한글 윈도우도 초기 윈도우 셋업시에 키보드 설정을 호기심때문에 다른 것으로 선택한 경우에도 이런 문제가 나타나곤 하는데요.

이번엔 Windows 7에서 XP 모드 VPC를 설치한 경우에 동일한 현상이 발생했습니다. Windows 7에서 구동시킬 XP 모드 VPC를 역시 영문으로 다운로드 받아서 사용하는데 우측 ALT키(한/영변환키)로 한영 전환이 안되는 문제가 발생하였습니다.

물론, 웹을 검색해 보면 해답이 나오지요. ^^

windows xp 한영 변환 오류
; http://www.ntech.in/v2/bbs/board.php?bo_table=2_window2003&wr_id=15&sfl=&stx=&sst=wr_datetime&sod=desc&sop=and&page=1


정리해보면 레지스트리 편집기에서 값을 다음과 같이 바꿔주고 재부팅하시면 됩니다.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters]
"LayerDriver KOR"="KBD101A.DLL"
"OverrideKeyboardIdentifier"="PCAT_101AKEY"
"OverrideKeyboardType"=dword:00000008
"OverrideKeyboardSubtype"=dword:00000003


반응형
반응형

 

출처 : 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

 

 

 

 

1. lshw 를 사용하여 하드웨어 상세 정보 확인(링크 참조)

- lshw 는 rpm 패키지를 별도로 설치해야됨

참고글 : http://jonnychoe.egloos.com/5549359

2. dmidecode 
- 설치할 필요없음
- dmidecode 명령은 서버 하드웨어, 바이오스 정보를 system DMI 테이블에서 읽어온다.
시스템의 현재 configuration 상태뿐만 아니라 최대 지원가능한 설정 정보까지도 불러온다.
예를 들어, 시스템의 현재 RAM 정보와 최대 확장가능한 RAM 정보까지도 확인할 수 있다.

dmidecode 의 output 형태는 다음과 같다.
Record Header: Handle {record id}, DMI type {dmi type id}, {record size} bytes
Record Value: {multi line record value}
  • record id: Unique identifier for every record in the DMI table.
  • dmi type id: Type of the record. i.e BIOS, Memory etc.,
  • record size: Size of the record in the DMI table.
  • multi line record values: Multi line record value for that specific DMI type.

dmidecode 명령의 output 예 :

# dmidecode | head -15

# dmidecode 2.9
SMBIOS 2.3 present.
56 structures occupying 1977 bytes.
Table at 0x000FB320.

Handle 0xDA00, DMI type 218, 11 bytes
OEM-specific Type
        Header and Data:
                DA 0B 00 DA B0 00 17 03 08 28 00

Handle 0x0000, DMI type 0, 20 bytes
BIOS Information
        Vendor: Dell Computer Corporation
        Version: A07
        Release Date: 01/13/2004

DMI 테이블의 레코드 갯수를 보기위해서는 

# dmidecode | grep ^Handle | wc -l
56

(or)

# dmidecode | grep structures
56 structures occupying 1977 bytes.

DMI Types

시스템의 CPU 정보를 보기위해서는 type id 4 옵션을 dmidecode 명령과 함께 준다.

# dmidecode -t 4
# dmidecode 2.9
SMBIOS 2.3 present.

Handle 0x0400, DMI type 4, 35 bytes
Processor Information
        Socket Designation: Processor 1
        Type: Central Processor
        Family: Xeon
        Manufacturer: Intel
        ID: 29 0F 00 00 FF FB EB BF
        Signature: Type 0, Family 15, Model 2, Stepping 9
        Flags:
                FPU (Floating-point unit on-chip)
                VME (Virtual mode extension)
                DE (Debugging extension)
                PSE (Page size extension)
                TSC (Time stamp counter)
                MSR (Model specific registers)

DMI type 은 아래와 같다.

Type   Information
       ----------------------------------------
          0   BIOS
          1   System
          2   Base Board
          3   Chassis
          4   Processor
          5   Memory Controller
          6   Memory Module
          7   Cache
          8   Port Connector
          9   System Slots
         10   On Board Devices
         11   OEM Strings
         12   System Configuration Options
         13   BIOS Language
         14   Group Associations
         15   System Event Log
         16   Physical Memory Array
         17   Memory Device
         18   32-bit Memory Error
         19   Memory Array Mapped Address
         20   Memory Device Mapped Address
         21   Built-in Pointing Device
         22   Portable Battery
         23   System Reset
         24   Hardware Security
         25   System Power Controls
         26   Voltage Probe
         27   Cooling Device
         28   Temperature Probe
         29   Electrical Current Probe
         30   Out-of-band Remote Access
         31   Boot Integrity Services
         32   System Boot
         33   64-bit Memory Error
         34   Management Device
         35   Management Device Component
         36   Management Device Threshold Data
         37   Memory Channel
         38   IPMI Device
         39   Power Supply

또한, type id 를 입력하는 대신에 키워드를 사용할 수도 있다.

다음은 사용가능한 키워드이다.

       Keyword     Types
       ------------------------------
       bios        0, 13
       system      1, 12, 15, 23, 32
       baseboard   2, 10
       chassis     3
       processor   4
       memory      5, 6, 16, 17
       cache       7
       connector   8
       slot        9

type id 2 와 10 에 대한 정보를 보기 위해 키워드 baseboard 를 사용한 예

# dmidecode -t baseboard
# dmidecode 2.9
SMBIOS 2.3 present.

Handle 0x0200, DMI type 2, 9 bytes
Base Board Information
        Manufacturer: Dell Computer Corporation
        Product Name: 123456
        Version: A05
        Serial Number: ..CN123456789098.

Handle 0x0A00, DMI type 10, 14 bytes
On Board Device 1 Information
        Type: SCSI Controller
        Status: Enabled
        Description: LSI Logic 53C1030 Ultra 320 SCSI
On Board Device 2 Information
        Type: SCSI Controller
        Status: Enabled
        Description: LSI Logic 53C1030 Ultra 320 SCSI
On Board Device 3 Information
        Type: Video
        Status: Enabled
        Description: ATI Rage XL PCI Video
On Board Device 4 Information
        Type: Ethernet
        Status: Enabled
        Description: Broadcom Gigabit Ethernet 1
On Board Device 5 Information
        Type: Ethernet
        Status: Enabled
        Description: Broadcom Gigabit Ethernet 2


dmicode 를 사용하여 RAM(Physical Memory) 에 대한 정보 확인하기

시스템에서 지원가능한 최대 RAM 크기는 얼마인가? 
아래 예는 최대 RAM 크기가 8GB 임을 보여준다.

# dmidecode -t 16
# dmidecode 2.9
SMBIOS 2.3 present.

Handle 0x1000, DMI type 16, 15 bytes
Physical Memory Array
        Location: System Board Or Motherboard
        Use: System Memory
        Error Correction Type: Multi-bit ECC
        Maximum Capacity: 8 GB
        Error Information Handle: Not Provided
        Number Of Devices: 4

메모리를 얼마나 확장할 수 있는지 를 확인하기 위해 /proc/meminfo 명령을 사용하여 현재 메모리 총량을 확인할 수 있다.

# grep MemTotal /proc/meminfo
MemTotal:      1034644 kB

위 예는 현재 시스템이 1 GB 메모리를 가지고 있음을 확인할 수 있지만, 이것이 1GB 1개인지, 512MB 2개인지, 256MB 4개인지는 알 수가 없다.

dmidecode 명령을 사용하여 memory 슬롯에서 각각 사용되고 있는 RAM  크기를 알 수 있다.

아래 예는 memory slot  이 4개이며, 현재 슬롯 1,2 에 512 MB RAM 이 각각 장착되어 있음을 확인할 수 있다.

# dmidecode -t 17
# dmidecode 2.9
SMBIOS 2.3 present.

Handle 0x1100, DMI type 17, 23 bytes
Memory Device
        Array Handle: 0x1000
        Error Information Handle: Not Provided
        Total Width: 72 bits
        Data Width: 64 bits
        Size: 512 MB  [Note: Slot1 has 512 MB RAM]
        Form Factor: DIMM
        Set: 1
        Locator: DIMM_1A
        Bank Locator: Not Specified
        Type: DDR
        Type Detail: Synchronous
        Speed: 266 MHz (3.8 ns)

Handle 0x1101, DMI type 17, 23 bytes
Memory Device
        Array Handle: 0x1000
        Error Information Handle: Not Provided
        Total Width: 72 bits
        Data Width: 64 bits
        Size: 512 MB [Note: Slot2 has 512 MB RAM]
        Form Factor: DIMM
        Set: 1
        Locator: DIMM_1B
        Bank Locator: Not Specified
        Type: DDR
        Type Detail: Synchronous
        Speed: 266 MHz (3.8 ns)

Handle 0x1102, DMI type 17, 23 bytes
Memory Device
        Array Handle: 0x1000
        Error Information Handle: Not Provided
        Total Width: 72 bits
        Data Width: 64 bits
        Size: No Module Installed [Note: Slot3 is empty]
        Form Factor: DIMM
        Set: 2
        Locator: DIMM_2A
        Bank Locator: Not Specified
        Type: DDR
        Type Detail: Synchronous
        Speed: 266 MHz (3.8 ns)

Handle 0x1103, DMI type 17, 23 bytes
Memory Device
        Array Handle: 0x1000

        Error Information Handle: Not Provided
        Total Width: 72 bits
        Data Width: 64 bits
        Size: No Module Installed [Note: Slot4 is empty]
        Form Factor: DIMM
        Set: 2
        Locator: DIMM_2B
        Bank Locator: Not Specified
        Type: DDR
        Type Detail: Synchronous
        Speed: 266 MHz (3.8 ns)


BIOS information 확인하기

# dmidecode -t bios
# dmidecode 2.9
SMBIOS 2.3 present.

Handle 0x0000, DMI type 0, 20 bytes
BIOS Information
        Vendor: Dell Computer Corporation
        Version: A07
        Release Date: 01/13/2004
        Address: 0xF0000
        Runtime Size: 64 kB
        ROM Size: 4096 kB
        Characteristics:
                ISA is supported
                PCI is supported
                PNP is supported
                BIOS is upgradeable
                BIOS shadowing is allowed
                ESCD support is available
                Boot from CD is supported
                Selectable boot is supported
                EDD is supported
                Japanese floppy for Toshiba 1.2 MB is supported (int 13h)
                5.25"/360 KB floppy services are supported (int 13h)
                5.25"/1.2 MB floppy services are supported (int 13h)
                3.5"/720 KB floppy services are supported (int 13h)
                8042 keyboard services are supported (int 9h)
                Serial services are supported (int 14h)
                CGA/mono video services are supported (int 10h)
                ACPI is supported
                USB legacy is supported
                LS-120 boot is supported
                BIOS boot specification is supported
                Function key-initiated network boot is supported

Handle 0x0D00, DMI type 13, 22 bytes
BIOS Language Information
        Installable Languages: 1
                en|US|iso8859-1
        Currently Installed Language: en|US|iso8859-1


장비의 Manufacturer, Model 과 Serial number 확인하기

# dmidecode -t system
# dmidecode 2.9
SMBIOS 2.3 present.

Handle 0x0100, DMI type 1, 25 bytes
System Information
        Manufacturer: Dell Computer Corporation
        Product Name: PowerEdge 1750
        Version: Not Specified
        Serial Number: 1234567
        UUID: 4123454C-4123-1123-8123-12345603431
        Wake-up Type: Power Switch

Handle 0x0C00, DMI type 12, 5 bytes
System Configuration Options
        Option 1: NVRAM_CLR:  Clear user settable NVRAM areas and set defaults
        Option 2: PASSWD:  Close to enable password

Handle 0x2000, DMI type 32, 11 bytes
System Boot Information
        Status: No errors detected

 

반응형
반응형
처리되지 않은 예외: System.InvalidOperationException: 이 구현은 Windows 플랫폼의 FIPS 유효성을 검사한 암호화 알고리즘의 일부가 아닙니다.

메모

  • 사용 하거나 사용 하지 않도록 설정 된 시스템 암호화: 암호화, 해시, 서명에 사용 FIPS 호환 알고리즘 보안 설정, 다시 시작 해야 응용 프로그램을 Internet Explorer 같은 새 설정이 적용 되려면.
  • 이 보안 설정은 Windows Server 2008 및 Windows Vista에서 다음 레지스트리 값:
    HKLM\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled
    이 레지스트리 값은 현재 FIPS 설정을 반영합니다. 이 설정을 사용 하는 값은 1입니다. 이 설정을 사용 하지 않으면 값은 0입니다.
  • 이 보안 설정은 Windows Server 2003에서 및 Windows XP에서 다음 레지스트리 값:
    HKLM\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy
    이 레지스트리 값은 현재 FIPS 설정을 반영합니다. 이 설정을 사용 하는 값은 1입니다. 이 설정을 사용 하지 않으면 값은 0입니다.


반응형
반응형


- 현재 연결되어 있는 모든 세션을 볼 수 있음.

SELECT * FROM v$session



-- 관련 파라미터 --> 최대 세션수는 (Process*1.1) + 5

show parameter processes ;

show parameter sessions ;


-- 현재 세션 확인

select count(*) from v$session ;

select * from v$session where status = 'ACTIVE'



- 상태별로 모든 세션 상태 보는 쿼리.

SELECT COUNT (*) total_cnt,
       COUNT (DECODE (server, 'DEDICATED', 1, NULL)) dedicated_cnt,
       COUNT (DECODE (TYPE, 'BACKGROUND', 1, NULL)) background_cnt,
       COUNT (DECODE (status, 'ACTIVE', 1, NULL)) active_cnt
  FROM v$session



- 현재 active 상태인 것과 inactive 상태인것을 확인할 때. 

SELECT status,COUNT(*) FROM v$session
WHERE username='유저명
GROUP BY status



- 세션 kill

alter system kill  session 'SID, Serial' ;

ex) alter system kill  session '1352,28337' ;

위 명령으로 kill 불가시 spid 를 콘솔에서 kill -9 로 제거



출처 : http://hoonsik69.tistory.com/106

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

DB를 사용하다 보면 batch작업이나 long query를 사용하는 경우가 있는데, 이럴때 장시간 반응이 없어서 pc의 전원을 강제로 꺼버리거나 작업도중 정전이 발생할 수도 있는데, 이럴 경우 세션은 비정상적으로 종료가 되어 세션이 정리가 되지 않고 남아 있게된다.


만약 이러한 세션이 다수가 되면 정상적인 다른 세션들에게 자원배분(메모리등)이 정상적으로 되지 않거나 프로세스 초과 에러 메세지가 발생할 수 있다. 이렇게 실제 사용하지 않은 세션들을

DEAD SESSION(죽은 세션)이라고 하는데 이들을 정리할 필요가 있다.

 

KeepAlive 란 Idle TCP/IP Connection 의 Time out 기능을 말한다.

TCP/IP 기반으로 remote 로 연결된 상태에서 클라이언트의 갑작스런 리부팅이나 스위치 Off 의 경우 해당 연결을 서버에서 Closing 하지 못하게 된다.


이것은 TCP/IP의 제한인 것이지 오라클의 문제는 아니다.
이 때 KeepAlive가 정확하게 configure되어 있다면 좀 더 빨리 Dead connection을 발견하고 빨리 Closing할 것이다.
TCP/IP 에 관련된 것이므로 KeepAlive를 각 플랫폼 별로 지정하는 방법이 모두 틀리다.

예를들면 유저가 PC 에서 Unix Oracle Server로 연결 후 실수로 다음 쿼리 문을 실행했다.

 

SQL> select * from largetable, greatetable, verybigtable;

 

적게는 수십분, 많게는 몇시간이 걸릴 작업이라면 유저는 쿼리를 인터럽트하기 보다 reset 버튼을 누르게 될 것이다. 이때 클라이언트 프로세스는 없어졌지만 백그라운드 서버 프로세스는 계속

running 중일것이다.
이런 현상은 수시로 일어날 수 있다. 또한 당연한 것이다.

유저가 사용을 잘못하는 것이기 때문이다.

하지만 SQL*NET 2.1 이상에서 Dead-Connection-Detection 이라 불리는 기능을 지원한다.

이것은 곧 KeepAlive 를 지정할 수 있는 기능이다 

반드시 서버 사이드의 $ORACLE_HOME/network/admin/sqlnet.ora 파일에 다음의 파라미터

지정해야 한다. 클라이언트에는 지정해봐야 전혀 소용이 없다.


sqlnet 설정 속성 값 설명 : http://docs.oracle.com/cd/B28359_01/network.111/b28317/sqlnet.htm#BIIEEGJH


설정 파일 : $ORACLE_HOME/network/admin/sqlnet.ora


각 세션의 INACTIVE 상태가 길게 유지될 때 세션 강제 종료 시간 설정은 아래의 설정 값을 설정.


만약 설정 값이 없을 경우 각 OS의 TCPKeepAliveTime을 따른다. (일반적으로 2시간, 종종 30분 단위로)

ex ) sqlnet.expire_time = 10         

설정 값 단위는 '분'

default = 0

mimimum Value : 0

recommended Value : 10



Netware를 제외한 모든 Platform, Protocol에서 지원이 된다.

# Windows NT 에서 KeepAlive 를 지정하는 방법(타 플랫폼은 제외)

Regedt32 를 실행한다.

레지스트리에서

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters

까지 이동한다.
여기서 키 추가가 아닌 값 추가를 한다.

파라미터는  KeepAliveTime (REG_DWORD) 로 입력한다.

값은 Decimal  -> 180000 (for example = 3 minutes) 으로 지정한다.

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


KeepAliveTime (윈도우의 경우)

KeepAliveTime 값은 TCP/IP가 대기 연결이 계속 원래 상태를 유지하는지 확인을 시도하는 빈도를 제어합니다. 이 시간 동안 활동이 없었을 경우 활성화 상태 지속 전송이 전송됩니다. 네트워크가 원래 상태를 유지하며 상대가 활성 상태일 경우 상대가 응답합니다. 손실된 상대에 대해 민감하게 되려는 경우(즉, 상대가 손실되었음을 더 빨리 인식해야할 경우) 이 값을 줄여 보십시오. 오랜 기간 동안 비활성 상태를 유지하는 연결은 공통이고 손실된 상대는 공통이 아닐 경우 이 값을 늘려 오버헤드를 줄여도 됩니다.

7,200,000밀리초(2시간) 동안 대기 연결이 비활성 상태일 경우 Windows에서는 활성화 상태 지속 메시지를 전송하는 것이 기본값입니다. 종종 값에 1,800,000밀리초를 선택하여 반쯤 닫힌 연결을 30분마다 감지합니다.

다음 프로시저를 사용하여 값을 보거나 사용자 정의하십시오.

  1. regedit 명령을 시작하여 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 레지스트리 하위 키를 탐색한 후 이름이 KeepAliveTime인 새 REG_DWORD 값을 작성하거나 설정하십시오.
  2. 이 값을 적절한 밀리초로 설정하십시오.
  3. 레지스트리 편집기를 닫으십시오.
  4. 시스템을 중지한 후 다시 시작하십시오. 




출처 : http://nearby2.tistory.com/18

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

Oracle 세션(Session) 관리

 

오라클은 데이터베이스 접속이 이루어지면 세션을 시작한다.세션은 사용자가 데이터베이스에 연결되어 있는 동안 계속 유지된다.
세션이 시작되면 오라클은 해당 세션에 세션 ID(SID)를 할당한다. 사용자 세션을 출력하려면 v$session 뷰에 쿼리를 수행하면 된다.
v$session에서 각 세션은 고유한 SID(Session identifier)와 SERIAL#(serial number)를 갖는다.
시리얼 번호는 세션이 종료되었으나 다른 세션이 동일한 SID를 갖고 시작되었 때, 세션 명령들이 정확한 세션 객체에 적용될 수 있도

록 보장해준다.

 

v$session 뷰는 사용자 이름, 장치 이름, 프로그램 이름, 상캐 및 로그인 시각 등 세션에 대한 많은 유욯한 정보들을 갖고 있다.

 

(ex)
select username, program from v$session;


-> 어떤 사용자들이 DB에 접속하였고 어떤 프로그램들을 수행하고 있는지 확인하는 쿼리

 

 

세션 강제 종료

 

세션을 강제 종료해야할때는 alter system 명령어를 사용하는데, 사용자 세션을 강제 종료하기 위해서는 v$session 뷰의 SID와

SERIAL# 값이 필요하다.

 

상황 # 사용자 SUNNY에 의해 생성된 세션을 종료할때..

 

SQL> select username, sid, serial#, status
from v$session
where username = 'SUNNY';


USERNAME                SID         SERIAL#       STATUS
--------------------- -------------- -------------- ----------------
SUNNY                       10               3             INACTIVE


SQL> alter system kill session '10, 3';


System altered.


SQL> select username, sid, serial#, status
from v$session
where username = 'SUNNY';


USERNAME                SID         SERIAL#       STATUS
--------------------- -------------- -------------- ----------------
SUNNY                       10               3              KILLED


 

- 오라클이 세션을 강제 종료할 떄, 해당 세션은 더 이상의 SQL문을 실행하는 것을 막는다. 만일 강제 종료하는 시점에 SQL문을 실행

중이었다면, 해당 SQL문은 종료되고 모든 변경사항들은 롤백된다. 또한 해당 세션에 의해 사용되던 잠금(LOCK) 및 기타 자원들도 해

제된다.

 

 - 오라클이 INACTIVE 세션을 강제 종료하는 경우에는 세션을 종료시키고, 상태를 KILLED로 표시한다. 사용자가 계속하여 해당 세션을

사용하려고 시도하면 에러 메세지가 반환되며, 세션 정보는 v$session에서 제거된다.

 

 - ACTIVE 세션을 강제 종료하는 경우에는 오라클이 세션을 종료시키고, 해당 세션이 종료되었다는 에러 메시지를 사용자에게 즉시 발생 시킨다. 만일 오라클이 세션에 의해 사용되고 있던 자원을 60초 이내에 해제할 수 없을 때에는 해당 세션이 강제 종료될 것이라는

메시지를 사용자에게 발생시킨다. v$session 뷰의 상태는 다시 KILLED로 조회될 것이다.

 

- 만일 사용자가 현대 트랜잭션을 완료하고 사용 중이던 세션을 종료하려고 하면 DISCONNECT SESSION 옵션을 사용하면 된다.

 

alter system disconnect '10', '3' post_transation;

 

immediate 옵션과 사용하면 진행중이던 트랜잭션을 롤백하고 모든 세션의 잠금을 해제하며 전체 세션 상태를 복수한 후 사용자에게

즉시 제어 권한을 넘긴다.

 

alter system disconnect session '10', '3' immediate;
alter system kill session '10', '3' immediate;

 

 

+)

 

Q. SID는 세션식별자로 유니크한 값을 가지는데 SERIAL#값까지 갖는 이유는 무엇일까?

 

A. SID는 사용자별로 할당되는 값이 아니라 세션마다 할당되기 때문에 재사용됩니다. 만약 10, 12번 세션을 확인 후 KILL SESSION명령을 하기전에 10번 세션이 로그아웃을 하고 바로 다른 세션이 연결되어서 SID가 10번으로 할당되는 경우가 있다고 해봅시다. 이럴경우 SID만 가지고 세션을 KILL시키면 내가 원하는 세션이 아닌 다른 세션을 KILL 시키는 문제가 발생할 수 있습니다. 그래서 SERIAL#값을 하나 더 두어 세션을 구분하는 것입니다.




참고 : http://dblab.changwon.ac.kr/corenet/education/oraclelec/admin/chap4-6.html

참고 : http://database.sarang.net/?inc=read&aid=27232&criteria=oracle

참고 : http://tip.daum.net/question/62996686

참고 : https://community.oracle.com/thread/468729?start=0&tstart=0

참고 : http://database.sarang.net/?inc=read&aid=27232&criteria=oracle

반응형
반응형

출처 : http://faq.hostway.co.kr/Linux_ETC/4258




프로세스의 PID를 확인 후

# cat /proc/PID/status | grep VmSize

VmSize:     7224 kB

 

/proc/PID/status 만 입력 시 항목 설명

 

VmSize: 전체 할당된 가상 메모리의 크기
VmRSS:
실제 물리적 메모리가 할당된 크기
VmLck:
스왑아웃 될수 없는 메모리의 크기

VmData: Heap 영역
VmStk: Stack
영역
VmExe:
실행코드 영역 (전역변수 및 실행코드)
VmLib:
동적으로 연결된 라이브러리 영역

반응형

+ Recent posts