IP header에서 TOS(Type Of Service)필드는 packet의 우선순위를 나타낸다.

 

다음은 TOS필드에 대한 설명이다.

 

Type of Service - is how the datagram should be used, e.g. delay, precedence, reliability, minimum cost, throughput etc. This TOS field is now used by Differentiated Services and is called the Diff Serv Code Point (DSCP).

 

The following diagram illustrates the TOS field in detail:


 
Precedence - The following table details the precedence bits and their possible values:
  • 000 (0) - Routine
  • 001 (1) - Priority
  • 010 (2) - Immediate
  • 011 (3) - Flash
  • 100 (4) - Flash Override
  • 101 (5) - Critical
  • 110 (6) - Internetwork Control
  • 111 (7) - Network Control
Now the TOS bits themselves:
  • Delay - when set to '1' the packet requests low delay.
  • Throughout - when set to '1' the packet requests high throughput.
  • Reliability - when set to '1' the packet requests high reliability.
  • Cost - when set to '1' the packet has a low cost.
  • MBZ - checking bit.

 

TOS 필드 값을 설정하는 방법은 여러가지가 있는 것 같은데 내가 테스트 해서 얻은 방법은 다음과 같다.

 

unsigned int tos = 0x80; // precedence를 4로 설정

setsockopt(sock, IPPROTO_IP, IP_TOS, (const void *)&tos, sizeof(tos));

 

TOS의 precedence 값은 0-7까지 지정할 수 있으며, 주의할 점은 최상위 3bit를 값으로 사용한다는 것이다.

그래서 지정하려는 precedence값과 setsockopt 함수에 넣어주는 값을 잘 매칭시켜야 한다.

이를 정리하면 다음과 같다. 여기서 tos값이 실제로 setsockopt 함수에 넣어줘야 할 값이다.

 

level   tos       binary
 0     0x00  (000)0 0000
 1     0x20  (001)0 0000
 2     0x40  (010)0 0000
 3     0x60  (011)0 0000
 4     0x80  (100)0 0000
 ---------------------------- 아래부터 root 권한 필요, 혹은 권한 설정 필요
 5     0xa0  (101)0 0000
 6     0xc0  (110)0 0000
 7     0xe0  (111)0 0000

 

[ 권한에 대하여 ]

 

나는 일반계정으로 Linux에 들어가서 테스트 했는데 5이상의 값을 설정하려고 하면 계속 다음과 같은 에러가 떴다.

 

Operation not permitted

 

다음은 에러에 대한 설명을 찾은 것이다.

 

[EPERM] Operation not permitted.
 

The executing user profile must have *IOSYSCFG special authority to set options when the level parameter specifies IPPROTO_IP and the option_value parameter is IP_OPTIONS .

 

출처: http://publib.boulder.ibm.com/html/as400/v5r1/ic2962/index.htm?info/apis/ssocko.htm

 

[ 관련 링크 ]

 

1. socket 옵션에 대한 설명이 잘 되어있다. http://linux.die.net/man/7/socket

 

2. IP datagram에 대한 설명: http://www.rhyshaden.com/ipdgram.htm

 

3. setsockopt 함수에 대한 설명

http://publib.boulder.ibm.com/html/as400/v5r1/ic2962/index.htm?info/apis/ssocko.htm

 

4. IPv4/6에 설정하는 방법에 대한 설명: http://adioshun.springnote.com/pages/289965


출처 : http://blog.naver.com/57gate?Redirect=Log&logNo=60127366508


매크로 함수

#1. 매크로 함수란?

#2. 괄호 속에 있는 이유는?

#3. 매크로와 함수 및 템플릿의 비교

#4. 인라인 함수란?

#5. 문자열 조작

#6. 문자열화 연산자

#7. 결합연산자

#8. 내장매크로

#9. assert()

 

#1. 매크로 함수란? (a)

  - 매크로 함수는 #define을 사용하여 만들어지는 기호이다. 이 기호는 함수처럼 하나의 인수를 가진다.

  - 전처리기는 특정 인수가 무엇이든 상관없이 치환 문자열로 대치한다.

  1.        #define TWICE(x) ( (x) * 2 )

  - 매크로는 하나 이상의 매개 변수를 가질 수 있고, 반복해서 사용할 수 있다.

  1.        #define MAX(x,y) ( (x) > (y) ? (x) : (y) )
  2.        #define MIN(x,y) ( (x) < (y) ? (x) : (y) )

  [주의] 매크로 함수 정의에서 매개 변수 목록에 대한 여는 괄호가 매크로 이름 바로 다음에 빈칸 없이 곧바로 이어져야 한다.

 

#2. 괄호 속에 있는 이유는? (b)

  - 전처리기는 치환 문자열의 인수 주위에 괄호가 있을 것을 요구하지 않지만, 괄호는 복잡한 값을 매크로에 전달할 경우 원하지 않는 결과가 나오는 것을 방지하는데 도움을 준다.

#3. 매크로와 함수 및 템플릿의 비교 (c)

  - 문제점

    ① 매크로가 커질 경우 혼란스러워질 수 있다.

    ② 사용한 곳에서 곧바로 인라인으로 확장된다.

    ③ 매크로가 컴파일러에서 사용하는 중간 소스 코드에 나타나지 않는다. 대부분의 대버거에서 사용할 수 없다.

    ④ 형에 안전하지 않다.

#4. 인라인 함수란? (d)

  - 확장인라인 : 함수의 내용이 함수가 호출된 코드에 삽입되는 것을 말한다.

  1.     inline unsigned long Square(unsigned long a) { return a * a; }

#5. 문자열 조작 (e)

  - 문자열화 연산자(#)는 이 연산자 다음에 나오는 것은 무엇이든 간에 겹따옴표 속에 넣어 문자열로 만든다.

#6. 문자열화 연산자 (f)

  - 문자열화 연산자는 그 연산자 다음에 나오는 어떤 글자들이라도 다음 공백 문자까지 겹따옴표로 둘러싸게 된다.

  1.        #define WRITERSTRING(x) count << #x
  2.        WRITERSTRING(This is a string);    =     count << "This is a string";

#7. 결합연산자 (g)

  - 결합연산자는 하나 이상의 용어를 합쳐서 새로운 단어로 만들어 내는 것이다.

  - 새 단어란 실제로 클래스 이름, 변수 이름, 배열에 대한 오프셋, 또는 어떤 것이든 나타날 수 있는 문자열로 사용할 수 있다.

  1.         #define fPrint(x) f ## x ## print
  2.         #define Listof(Type) class Type##List \
  3.          { \
  4.              public : \
  5.              Type##List(){} \
  6.              private : \
  7.              int itsLength; \
  8.          };

#8. 내장매크로 (h)

  - 많은 컴파일러들은 몇가지 유용한 매크로들을 내장하고 있다.

  - _DATE_ : 현재 날짜

     _TIME_ : 현재 시간

     _LINE_ : 소스코드 행 번호

     _FILE_ : 소스코드 파일 이름


  - 프로그램에서 사용하는 이름들과 겹칠 가능성을 줄이기 위해 두 개의 밑줄로 둘러 싸여 있다.

#9. assert() (i)

  - 매개 변수가 참으로 평가될 경우 참을 반환하고, 거짓으로 평가될 경우 몇 가지 종류의 동작을 한다.

  - 많은 컴파일러들이 assert()가 실패할 경우 프로그램을 강제로 종료한다. 그밖의 컴파일러들은 예외를 발생시킨다.

  - DEBUG가 정의되지 않을 경우 전처리기가 이 매크로를 무시한다. 수행성의 낭비나 실행 버전 프로그램의 크기의 증가가 없다.

  1.         #define DEBUG
  2.         #ifdef DEBUG
  3.              #define ASSERT(x)
  4.         #else
  5.              #define ASSERT(x) \
  6.                      if( !(x) ) \
  7.                      { \
  8.                            cout << "ERROR!! Assert " << #x << " falied\n"; \
  9.                            cout << "On line " << _LINE_ << "\n"; \
  10.                            cout << "in file" << _FILE_ << "\n"; \
  11.                       }
  12.          #endif

출처 : http://lonelysm.springnote.com/pages/4011761?print=1#h

윈도우 7

 

사용자 변수

%USERPROFILE%\AppData\Local\Temp
%USERPROFILE%\AppData\Local\Temp

 

시스템 변수

%SystemRoot%\TEMP

%SystemRoot%\TEMP

 

/proc/iomem - 등록된 I/O 메모리 영역을 확인할 수 있다.

/proc/ioports - 등록된 I/O 영역을 확인할 수 있다.




커널은 디바이스 파일에 기록된 디바이스 타입주 번호를 이용해 커널 내에 등록된 디바이스 드라이버 함수를 연결한다. 문자 디바이스 드라이버의 경우, 커널 2.6에서는 fs/char_dev.c에 chrdevs라는 전역 변수를 다음과 같이 정의 한다.

static struct char_device_struct {

    struct char_device_struct *next;

    unsigned int major;

    unsigned int baseminor;

    int minorct;

    char name[64];

    struct cdev *cdev;      /* will die */

} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];



이 전역 변수는 struct file_operations *ops; 라는 필드(struct cdev  필드에 있음)를 포함한 문자 디바이스 드라이버를 관리하는 구조체다.

응용 프로그램에서 open() 함수로 디바이스 파일을 열어 타입 정보와 주 번호를 얻고, 이 정보를 이용하여 chrdevs 배열에 등록된 디바이스 드라이버의 인덱스를 얻고, 여기서 얻은 인덱스 값으로 chrdevs 변수에 등록된 file_operations 구조체 주소를 얻는다. 결국 디바이스 파일 타입 정보와 주 번호를 이용해 커널 내의 디바이스 드라이버를 찾는다.


 
kmalloc() 함수는 할당 크기가 제한되어 있지만, vmalloc() 함수는 가상 공간이 허용하는 한 크기 제한 없이 할당받을 수 있다. 그래서 큰 메모리 공간을 할당할 때 주로 사용한다.

vmalloc() 함수에서 할당받은 주소와 kmalloc() 함수에서 할당받은 주소는 디바이스 드라이버에서 사용할 때 차이가 없다. 하지만 해당 주소의 실제(물리) 주소를 얻고자 한다면 vmalloc() 함수는 가상 주소 공간에서 할당받기 때문에 해당 주소의 영역이 하드디스크에 있을 수도 있어 실패할 수 있다.

vmalloc() 함수는 커다란 연속 공간을 할당하기 위해 가상 메모리 관리 루틴이 수행되기 때문에 kmalloc() 함수보다 할당 속도가 매우 느리다. 또한 vmalloc() 함수는 인터럽트 서비스 함수 안에서는 사용할 수 없다.

 
이 두 함수는 할당 속도가 빠르고 사용법이 간단해 디바이스 드라이버에서 가장 많이 사용된다.

단, kmalloc() 함수를 사용할 때는 할당 가능한 크기가 32 x PAGE_SIZE라는 점을 주의해야 한다. 그리고 메모리의 특성을 주거나 메모리 할당 시점에 처리 방식을 다음과 같은 매개 변수 값으로 줄 수 있다.

GFP_KERNEL
kmalloc() 함수에 사용하는 대표적인 인자값으로, 동적 메모리 할당이 항상 성공하도록 요구한다. 커널이 관리하는 메모리가 충분치 않을 경우에는 디바이스 드라이버를 호출한 프로세스가 수행을 멈추고, 동적 메모리를 할당할 수 있는 상태가 될 때까지 잠든다. 그러다가 다른 프로세스에서 메모리를 반환해 커널이 동적 메모리를 할당할 수 있는 상태가 되면 깨어 난다.

GFP_ATOMIC
커널에 할당 가능한 메모리가 있으면 무조건 할당하고, 없으면 즉시 NULL을 반환한다.

GFP_DMA
연속된 물리 메모리를 할당받을 때 사용한다. 디바이스 드라이버가 작동하는 메모리 공간은 물리적인 메모리가 아닌 가상 주소 메모리다. 프로세스 입장에서 보면 가상 주소 공간이 연속적으로 보여도 실제 물리적 공간은 분할되어 있을 수 있다.

 
리눅스 커널에서 사용하는 모듈은 어떤 기능을 구현하는 커널 부분을 의미하며 ELF 형식의 객체 파일을 의미한다.

리눅스 커널에서는 모듈을 다루기 위해 내부적으로 struct module 형식의 구조체를 선언하고, 리스트 형태로 모듈을 관리한다. 이 구주체는 include/linux/module.h에 선언되어 있다.

모듈 적재 과정
커널에 모듈을 적재하려면 insmod 명령을 실행해야 한다. 이 insmod는 다음과 같은 과정을 실행한다. 

1. 주어진 모듈명이 ".o" 나 ".ko"가 포함되면 파일명에서 모듈명을 얻는다. 그렇지 않다면 모듈명으로 간주하고, /lib/module/의 하부 디렉토리에서 해당 모듈에 해당하는 파일명을 찾아 파일을 읽는다.

2. 읽어온 파일의 코드와 모듈명 그리고 module 구조체를 저장하는데 필요한 메모리 영역의 크기를 구한다.

3. 이 정보를 이용해 create_module() 함수를 호출한다. 이 함수는 모듈을 처리할 수 있는 권한이 있는지 검사하고, find_module() 함수를 이용해 이미 적재된 모듈인가를 검사한다. 만약 커널에 적재되지 않았다면 vmalloc() 함수를 호출해 새로운 모듈을 위한 메모리 영역을 할당한다. 할당받은 메모리 영역 중 module 구조체의 내용을 초기하고 모듈명을 그뒤에 복사한다. 이후 모듈을 관리하는 커널 모듈 리스트에 적재한다. 마지막으로 모듈에 할당된 메모리의 시작 주소를 돌려준다.

4.  query_module() 함수를 이용해 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. 이때 query_module() 함수에 QM_MODULES, QM_INFO, QM_SYMBOL 값을 지정하여 필요한 정보를 가져오는데, QM_MODULES는 커널에 포함된 모듈명을 얻어올 때 사용하며, QM_INFO는 각 모듈의 시작 주소와 크기를 얻기 위해 사용한다. 앞에서 구한 모듈 정보를 통해 QM_SYMBOL로 실질적인 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다.

5.  커널 심볼 테이블, 모듈 심볼 테이블 그리고 커널에 적재하려는 현재 모듈의 메모리 시작 번지를 이용해 읽어온 모듈의 프로그램 코드 주소를 재배치한다. 이때 모듈에서 참조하는 외부 함수나 변수의 외부 심볼과 전역 심볼에 대응하는 논리 주소 오프셋으로 바뀐다.

6. 사용자 모드 주소 공간에 메모리 영역을 할당하고, 이곳에 모듈 구조체의 내용과 모듈명 그리고 앞으로 재배치된 모듈 코드를 복사한다.

7. 모듈 구조체의 init 필드의 함수 주소와 exit 필드의 함수 주소를 할당한다.

8. 사용자 모드 주소 공간에 할당된 메모리 주소를 이용해 init_module() 함수를 호출한다. 이 함수는 create_module() 함수와 유사한 행동을 반복한다. 모듈 처리를 할 수 있는 권한이 있는지 검사하고, find_module() 함수와 create_module() 함수를 사용해 추가된 위치를 찾고, 사용자 모드에 설정된 모듈 구조체의 내용을 덮어쓴다. 이 후 module 구조체에 있는 주소들이 올바른지 검사한다. 이 후 모듈에 할당된 메모리에 나머지 내용을 모두 복사한다. 마지막으로 init 필드에 선언된 주소를 이용하여 모듈에 포함된 모듈 초기화 함수를 호출한다. 이후 사용자 모드 메모리를 해제하고 종료한다.



모듈이 커널에서 제거되는 과정
커널에서 모듈을 제거하려면 rmmod() 함수를 실행해야 하는데, 그 실행 과정은 다음과 같다.

1. 주어진 모듈명이 "*.o"나 "*.ko"가 포함되면 파일명에서 모듈명을 얻는다. 그렇지 않다면 모듈명으로 간주한다.

2. query_module() 함수를 이용해 모듈에 대한 정보와 사용되는 커널 심볼 테이블과 모듈 심볼 테이블을 얻는다.

3. 제거해야 할 모듈 목록을 이용하여 delete_module() 함수를 호출한다. 이 함수는 시스템을 이용하여 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. 이때 모듈을 제거할 수 있는 권한이 있는가를 검사하고, find_module() 함수를 통해 이미 적재된 모듈인지 검사한다. 해당 모듈을 다른 모듈에서 참조하고 있는가를 검사한다. 참조하지 않고 있으면 혹시 사용되고 있는지를 검사한 후 사용중이 아니라면 module 구조체의 exit 필드에 정의된 함수를 호출한다. 이후 커널 내의 모듈 관리 리스트에서 해당 모듈을 제거하고, vfree() 함수를 이용하여 메모리를 해제한다.

4. 제거해야 할 모듈 목록을 모두 처리하면 종료한다. 



 
디바이스 드라이버 같은 커널 라이브러리를 객체(object) 형태로 만들어서 시스템 콜을 통해 리눅스 커널에 적재 요청을 하면, 커널은 해당 객체를 커널에 동적으로 링크 시킨다.

이 개념은 동적 라이브러리의 동작과 유사하다. 커널은 이미 링크가 끝난 상태기 때문에 심블릭 테이블이 제거된 상태이므로 그 자체로는 링크 처리를 할 수 없다. 그래서 커널은 내부적으로 심볼 테이블을 가지고 있다. 심볼 테이블은 커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블이다. 이 심볼 테이블을 이용하면 객체 형태로 작성된 커널 모듈 루틴이 참조할 커널 내부의 함수나 변수에 연결되어 동적으로 링크된다.

1. 커널에 외부 참조가 선언된 심볼 선언을 심볼 테이블에 등록한다.
2. 모듈은 커널에 적재될 때 커널 내의 심볼 테이블을 참고하여 참조 주소를 얻는다.
3. 모듈에 외부 참조된 심볼 선언은 객체와 테이블을 이용해 관리한다.
4. 모듈의 외부 참조가 선언된 심볼들을 커널 내의 심볼 테이블에 등록한다.
5. 모듈 코드에서 선언되지 않았던 주소를 모두 선언하면 커널에 등록된다. 



커널에서 제공하는 심볼 테이블은 /proc/ksyms 또는 /proc/kallsyms 를 통해 알 수 있다. (리눅스 커널 버젼에 따라 ksyms나 kallsyms가 다르다)


 

커널에서 메시지가 발생할 때마다 출력하고 싶다면


# cat /proc/kmsg


명령을 실행한다.~

+ Recent posts