우선 예전 32bit x86 시스템에서의 TSS(Task State Segment)에 대해 알아 보겠습니다.

대부분의 최신 운영체제는 선점형 태스크 스위칭을 지원합니다. 선점형 태스크 스위칭은 태스크의 작업이 완료되지 않아도, CPU를 선점 할 수 있는것을 말합니다. 그러기 위해서는 태스트 스위칭이 일어날 때, 기존에 실행중인 태스크의 상태를 보존하고 있다가, 이후에 CPU를 다시 선점하게 되었을 때 보존된 태스크의 상태를 복귀 할 필요가 있습니다. 이때 RAM상에 태스크의 상태를 보존 할 영역, 즉 모든 레지스터 값들이 보존될 영역을 TSS(Task State Segment)라고 합니다.

그림 x86 아키텍처에서 TSS의 생김새를 보겠습니다.


사용자 삽입 이미지

< 그림1. x86 아키텍처 TSS >


 x86 아키텍처 TSS는 <그림1>과 같이 거의 모든 레지스터들을 저장할 수 있습니다. 각 태스크가 사용하는 모든 레지스터를 포함하도록 되어있습니다.


  • 범용 레지스터 필드들

EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI등 태스크 스위칭 이전의 범용 레지스터들의 값들이 저장되어 있습니다.

  • 세그먼트 셀렉터 레지스터 필드

ES, CS, SS, DS, FS, GS등 태스크 스위칭 이전의 세그먼트 셀렉터 레지스터의 값들이 저장되어 있습니다.

  • EFLAGS 레지스터 필드

테스크 스위칭 이전의 EFLAGS 레지스터의 상태를 저장하고 있습니다.

  • EIP 필드

태스크 스위칭 이전의 EIP가 들어있습니다. 즉, 다음에 이 태스크가 CPU를 다시 선점하였을때 수행 해야하는 코드의 주소가 들어있습니다.

  • 이전 태스크 링크 필드

 이전 태스크의 TSS 세그먼트 셀렉터 값이 들어있습니다. TSS 영역은 GDT에 있는 TSS 디스크립터에서 지정 합니다. 이 디스크립터를 셀렉터로 사용하여 JMP, CALL 명령어로 태스크 스위칭을 합니다. 이 필드는 CALL 명령어로 태스크 스위칭을 할때, 다음 태스크는 자신의 TSS 영역에 이전 태스크의 TSS 디스크립터의 셀럭터 값을 이 필드에 저장해 둡니다. 그리고 자신이 실행을 IRET로 마칠때, CPU는 현재 태스크 TSS 영역의 '이전 태스크 링크' 필드를 참조하여 이전 태스크로 되돌아 갑니다.

  • LDT 세크먼드 셀렉터 필드

태스크의 LDT(Local Descriptor Table)의 세그먼트 셀렉터 값이 들어있습니다.

  • CR3 컨트롤 레지스터 필드

3 단계 페이징을 할때, 첫번째 페이징 단계에서 참조하는 Page Directory의 물리 주소를 담고 있는 CR3 레지스터 값이 들어 있습니다. CR3 레지스터는 Page Directory Base Register(PDBR)이라고도 합니다.

  • 권한 레벨 0,1,2 스택 포인터(ESP0, SS0,...) 필드

CPU 에서 지원하는 4가지 권한 레벨(0,1,2,3)별로 스택을 담고 있습니다. 이렇게 권한 레벨별로 스택을 따로 둠으로서 상위 레벨의 자료를 보호 할 수 있습니다. 리눅스에서 유저모드는 권한 레벨 3을 사용하고, 커널모드는 권한 레벨 0을 사용합니다. 권한 레벨 3의 스택은 ESP, SS 필드에 저장 됩니다.

  • T(Debug Trap) 플래그

태스크를 디버깅할 때 한 스탭씩 진행(Step Into)하며 프로그램의 동작을 확인해야 할 경우가 있습니다. 이때 사용되는 것이 플래그 레지스터의 T 플래그를 1로 셋합니다. 태스크 스위칭 이전의 태스크의 플래그 레지스터 T 플래그 값이 들어 있습니다.

  • I/O 맵 기본 주소 필드

in, out 명령어를 사용하여 CPU의 port를 접근하는 것은 제한이 필요 합니다. 예를 들어, 권한 레벨3인 유저모드 태스크의 경우에는 많은 제약이 가해져야 합니다. 이런 port별 접근 권한을 비트맵으로 표시하게 됩니다. 이런 I/O 허가 비트맵이 있는 주소가 이 필드에 들어 있습니다.


그럼 리눅스  커널 소스상에서 구현된 x86 아키텍처 TSS를 보겠습니다.

348 struct tss_struct {
349   unsigned short  back_link,__blh;
350   unsigned long esp0;
351   unsigned short  ss0,__ss0h;
352   unsigned long esp1;
353   unsigned short  ss1,__ss1h; /* ss1 is used to cache MSR_IA32_SYSENTER_CS */
354   unsigned long esp2;
355   unsigned short  ss2,__ss2h;
356   unsigned long __cr3;
357   unsigned long eip;
358   unsigned long eflags;
359   unsigned long eax,ecx,edx,ebx;
360   unsigned long esp;
361   unsigned long ebp;
362   unsigned long esi;
363   unsigned long edi;
364   unsigned short  es, __esh;
365   unsigned short  cs, __csh;
366   unsigned short  ss, __ssh;
367   unsigned short  ds, __dsh;
368   unsigned short  fs, __fsh;
369   unsigned short  gs, __gsh;
370   unsigned short  ldt, __ldth;
371   unsigned short  trace, io_bitmap_base;
372   /*
373    * The extra 1 is there because the CPU will access an
374    * additional byte beyond the end of the IO permission
375    * bitmap. The extra byte must be all 1 bits, and must
376    * be within the limit.
377    */
378   unsigned long io_bitmap[IO_BITMAP_LONGS + 1];
379   /*
380    * Cache the current maximum and the last task that used the bitmap:
381    */
382   unsigned long io_bitmap_max;
383   struct thread_struct *io_bitmap_owner;
384   /*
385    * pads the TSS to be cacheline-aligned (size is 0x100)
386    */
387   unsigned long __cacheline_filler[35];
388   /*
389    * .. and then another 0x100 bytes for emergency kernel stack
390    */
391   unsigned long stack[64];
392 } __attribute__((packed));
< 코드1. /include/asm-i386/processor.h의 tss_struct >

 구 조체 내에서는 가장 먼저 선언된 멤버가 가장 이전 번지를 나타낸다는 것만 주의하면 <코드1>의 tss_struct는 <그림1>의 TSS와 생김새가 같습니다. 리눅스의 tss_struct는 기존 TSS 이후에 몇가지 관련 정보(io_bitmap, io_bitmap_max, io_bitmap_owner, __cacheline_filler, stack)를 덧붙여 놓았습니다. 이들에 대해서는 여기서 언급하지 않겠습니다.

그럼 GDT에 설정하는 TSS 디스크립터의 생김새를 보겠습니다.


사용자 삽입 이미지
< 그림 2. TSS 디스크립터 >
  • AVL     : 시스템 소프트웨어에서 사용됨
  • B         : Busy 플래그
  • BASE   : 세그먼트 시작 주소
  • DPL     : 권한 레벨
  • G        : 세그먼트의 크기 단위 지정(바이트, 4K) 플래그
  • LIMIT   : 세그먼트 크기
  • P         : 세그먼트의 메모리상에 존재 여부
  • TYPE    : 세그먼트 타입

 GDT에서 TSS 디스크립터를 참조하여 TSS 영역에 접근합니다. Task Register(TR)에 LTR 명령어를 써서 GDT의 TSS 디스크립터의 인덱스를 담아 둡니다. 이렇게 하면 현재 실행 중인 태스크가 스위칭이 될때 태스크의 상태를 이 TSS 영역에 저장 됩니다.

사용자 삽입 이미지
< 그림 3. TSS 영역 접근 >

  하지만 리눅스 커널에서는 x86에서 지원하는 하드웨어 태스크 스위칭을 사용하지 않습니다. 리눅스 커널에서는 CPU마다 하나의 TSS 영역을 가지고 있습니다. 하드웨어 태스크 스위칭에 사용하지는 않지만 다음과 같은 두가지 용도로 쓰기위함 입니다.

  1. x86 아키텍처에서 유저 모드에서 커널 모드로 전환할 때 TSS의 커널 모드 스택 주소를 가져옵니다.
  2. 유저 모드 프로세스가 in 이나 out 명령어를 사용하여 입출력 포트에 접근 할 때 CPU는 접근 권한 체크를 TSS에 들어 있는 I/O 허가 비트맵을 이용하려고 접근 할 수도 있습니다.
다음에는 64bit 롱 모드에서 TSS를 어떻게 관리 하는지 알아 보겠습니다.

< 참고자료 >
  • Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1
  • AMD64 Architecture Programmer’s Manual Volume 2: System Programming
  • 리눅스 커널의 이해 3판
  • 만들면서 배우는 OS 커널의 구조와 원리(김범준 저)


Posted by cyj4369
,


extern이라는 것은 저장공간에 대한 선언에 사용되는 키워드로서,
여러개의 소스파일에 대해서 작업할 때 필요한 것입니다.
무슨 얘긴고 하니... 쉽게 말해서 다른 파일에 선언된 변수가 있다
고 선언하는 겁니다.

[file1.cpp]
int x = 1;
int f() { /* do something */ }
int y = 2;
extern z;

[file2.cpp]
extern int x;
int f();
extern int y = 3;  // 링크 에러.
extern z;          // 링크 에러

void g() { x = f(); }

extern int x; 라는 줄은 정수 x가 여기서 정의(definition)되지
않는다는 것을 지시하며 단지 선언(declaration)을 해주는 일을 수
행합니다. 위에서 y는 링크시 에러가 나게 되는데, extern 키워드
를 사용하더라도 초기화를 하면 그 선언은 정의가 되기 때문입니다.
즉, y는 file1.cpp와 file2.cpp에서 모두 정의되게 됩니다. 반대
로 z는 두 파일에서 모두 선언만 되고 정의가 되지 않았기 때문에 역
시 링크에러가 나게 됩니다.

참고로 file2.c에서 f는 extern int f(); 라고 지정해도 상관없습
니다.



◆ const와 typedef

const와 typedef는 기본적으로 internal linkage입니다. 즉, 다
른 파일에서 영향을 받지 않는다는 것이죠. const에서 이미 살펴봤
죠? typedef도 마찬가지입니다.

const에서 다뤘던 포인터 예제는 적절하지 못한 예였습니다. -_-;
여기서 다시 보도록 하죠.

[t1.cpp]
typedef int T;
const char* pText0 = "SCV";
char* const pText1 = "marine";
extern char* const pText2;
extern char* const pText3 = "tank";
extern char* const pText4 = "zealot";

[t2.cpp]
typedef void T;
const char* pText0 = "SCV";
char* const pText1 = "lurker";
extern char* const pText2;
extern char* const pText3;
extern char* const pText4 = "dragoon";

T : t1.cpp에서는 int, t2.cpp에서는 void로 사용할 수 있음.
pText0 : 조심! const char*의 의미는 const char에 대한 const
가 아닌 포인터. 즉, const가 아님. 따라서 external linakage.
링크 에러.
pText1 : char* const의 의미는 char에 대한 const 포인터. 즉,
internal linkage. t1.cpp에서는 "marine", t2.cpp에서
는 "lurker"으로 잘 동작함.
pText2 : 양쪽 모두 선언만 되고 정의는 안함. 실제로 사용하게 되
면 링크 에러.
pText3 : t1.cpp에서 초기화를 했기 때문에 정의가 됨. "tank"
pText4 : 양쪽 모두 초기화를 했기 때문에 양쪽 모두 정의가 되서
중복 정의 에러가 생김.

◆ extern을 사용가능한 경우

그럼 마지막으로 어떤 경우에 static이, 어떤 경우에 extern이 사용
가능한지 정리한 표를 보죠. (from MSDN)

Construct                                static   extern
Function declarations within a block      No       Yes
Formal arguments to a function            No       No
Objects in a block                        Yes      Yes
Objects outside a block                   Yes      Yes
Functions                                 Yes      Yes
Class member functions                    Yes      No
Class member data                         Yes      No
typedef names                             No       No


2. 다른 언어로의 연결을 지시할 때

◆ 기본적인 얘기부터

가장 대표적인게
extern "C" int Calc(double, int, int);
이런 식으로 C의 함수를 선언하는 것입니다.

아시겠지만 C와 C++은 함수를 호출하는 방식이 다릅니다. 인자를 처
리하는 순서라거나 스택에서 관리하는 방법 같은게 다르죠. (자세한
건 다음 기회에...) 따라서 C++ 컴파일러에게 이 함수는 C++의 규칙
에 어긋나는 함수는 아니지만 사실은 C의 함수라고 알려줘야 C++ 컴
파일러가 실수하지 않고 처리할 수 있게 됩니다. (실제로 이 선언을
중요하게 처리해야 하는건 링커입니다.)
그리고 Fotran에서 만든 함수를 Visual C++에서 사용하기 위해서
도 extern "C"로 선언해야 하는 것 같습니다. VC++이 extern 다음
에 허용하는 문자열이 "C"와 "C++" 뿐이라네요. (제가 해본건 아니
라서...)

extern "C"
{
#include < string.h>               // C 헤더 파일을 포함
   int Calc(double, int, int);   // C 함수를 선언
   int g1;                       // 정의(definition)
   extern int g2;                // 선언(declaration,
not definition)
}

이렇게 해서 여러개의 선언을 묶어주기도 하죠.

참고로 아랫줄은 정의가 아니라 선언입니다.
extern "C" int g3;              // 선언(declaration, not
definition)

C코드를 만들면서 C와 C++에서 다 사용할 수 있도록 헤더 파일을 만
들기 위해서는 다음과 같은 방법을 사용합니다.

#ifdef __cplusplus
extern "C" {
#endif

// 실제 함수나 변수들의 선언

#ifdef __cplusplus
}
#endif

C++ 소스를 컴파일할 경우에는 __cplusplus 가 자동으로 정의되므
로 전처리기에 의해 extern "C" { } 선언이 들어가게 됩니다.

◆ 함수 포인터의 경우

case by case로 생각해보겠습니다. (책을 베끼겠습니다.)

typedef int (*FT)(const void* , const
void*);                // (1)

extern "C" {
   typedef int (*CFT)(const void* , const
void*);           // (2)
   void qsort(void* p, size_t n, size_t sz, CFT
cmp);       // (3)
}

void isort(void* p, size_t n, size_t sz, FT
cmp);            // (4)
void xsort(void* p, size_t n, size_t sz, CFT
cmp);           // (5)
extern "C" void ysort(void* p, size_t n, size_t sz, FT
cmp); // (6)

int compare(const void* , const
void*);                      // (7)
extern "C" int ccmp(const void* , const
void*);              // (8)

(1) FT는 (const void*, const void*)를 받고 int를 리턴하는
C++로 링크되는 함수.
(2) CFT는 (const void*, const void*)를 받고 int를 리턴하는
C로 링크되는 함수.
(3) cmp는 CFT 타입. 즉 C로 링크되는 함수. qsort도 C 함수.
(4) cmp는 FT 타입. 즉 C++로 링크되는 함수. isort도 C++ 함수.
(5) cmp는 CFT 타입. 즉 C로 링크되는 함수. xsort는 C++ 함수.
(6) cmp는 FT 타입. 즉 C++로 링크되는 함수. qsort도 C 함수.
(7) compare는 C++로 링크되는 함수.
(8) ccmp는 C로 링크되는 함수.

따라서... 다음과 같은 결과가 나옵니다.
void (char* v, int sz)
{
   qsort(v, sz, 1, &compare);      // error
   qsort(v, sz, 1, &ccmp);         // ok
   
   isort(v, sz, 1, &compare);      // ok
   isort(v, sz, 1, &ccmp);         // error
}

[The C++ Programming Language]의 설명에 따르면 C와 C++이 함
수를 호출하는 방법이 같을 경우에는 위의 에러들도 확장으로 인정해
줄 수 있다고 하네요. 참고하세요.

◆ 다른 언어와의 연결에 대한 조금 더 시시콜콜하고 Visual C++적
인 얘기

포트란이나 파스칼 함수에 대해서는 __stdcall 를 사용해서 선언해
줘야 합니다.
(모든 윈도우 API 함수들은 __stdcall을 사용해서 선언되어 있습니
다.)
함수의 선언 앞에 붙이는 PASCAL, WINAPI, CALLBACK이라는 선언자
는 모두 __stdacll이라는 의미를 가지고 있죠.
반면에 C나 C++의 함수는 기본적으로 __cdecl이 됩니다.

__cdecl로 선언된 함수의 경우 인자를 전달한 스택을 정리하는건 호
출한 함수(caller)입니다.
__stdcall로 선언된 함수의 경우 인자를 전달한 스택을 정리하는건
호출된 함수(callee)입니다.
양쪽다 인자는 오른쪽 인자부터 왼쪽으로 스택에 들어가지요.

그렇다면 스택을 정리하는게 누구인지가 왜 중요할까요?
다른 언어가 다 호출된 함수가 스택을 정리하니까 C나 C++도 똑같이
하면 편할 텐데요.

일단 치명적인 부분은 vararg를 사용하는 함수입니다. 가장 대표적
인게 printf죠.
printf 는 다음과 같은 형을 가집니다.
int printf(const char *format, ... );
... 는 인자의 수가 정해져 있지 않다는 뜻이죠? 하지만 인자의 갯수
나 종류같은게 정해져 있지 않기 때문에 호출된 함수가 스택을 정리하
는 __stdcall의 경우에는 제대로 청소를 할 수가 없게 됩니다. 따라
서 인자의 수가 정해져 있지 않은 함수는 __cdecl을 사용하게 됩니
다.

참고로 __cdecl의 경우에는 각 함수 호출마다 스택을 정리하는 부분
이 들어가야 하기 때문에 컴파일된 코드의 크기가 더 커질 수 있다고
합니다.

그리고 코드를 C++로 작성중이더라도 라이브러리 파일을 만들어서 다
른 언어에서도 사용할 수 있게 해주려면 extern "C"와 __stdcall
을 사용해서 선언해주는게 좋겠죠?

이런 부분에 대해 자세히 알고 싶으신 분은 MSDN에서 "Mixed-
Language Programming Topics"이라는 글을 참고하시기 바랍니다.

Posted by cyj4369
,

cp [옵션 ] source destination

-a : 소스파일의 구성과 속성도 함께 복사

-d : 심볼릭 링크 파일을 그대로 복사

-f : 강제로 복사

-I : 존재하는 파일을 덮어쓰기 전에 물어보는 옵션

-p : 소스 파일의 소유자, 그룹, 퍼미션 등을 유지

-r : 하위 디렉토리와 파일까지 복사하는 옵션

-s : 심볼릭 링크를 만듬

-u : 목적지에 있는 파일의 타임 스템프가 같거나 새로운 경우 복사하지 않음.

-v : 복사하기 전에 그 파일 명을 보여줌

-x : 다른 파일 시스템의 서브 디렉토리는 복사하지 않음.

Posted by cyj4369
,


우분투에서 cscope 설치 및 사용하기

1. cscope 설치

$sudo -s

$apt-get install cscope

2. /usr/local/bin 디렉토리리에 mkcscope.sh파일 생성 후 다음과 같이 입력(루트권한으로 생성)

#!/bin/sh

rm -rf cscope.files cscope.files

find . \( -name '*.c' -o -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.s' -o -name '*.S' \) -print > cscope.files

cscope -i cscope.files

3. 권한 설정

$chmod 755 mkcscope.sh

4. 분석하고자 하는 커널의 디렉토리로 가서 mkcscope.sh명령을 수행하면 심볼 데이터베이스가 만들어지는 것을 확인할 수 있다.

다 끝나면 컨트롤+d를 눌러 빠져나온다. 그리고 해당 폴더에는 cscope.out이 만들어져 있을 것이다.

5. ~/.vimrc를 수정해야 한다.

.vimrc의 cscope 설정 부분에 다음과 같은 내용을 추가한다.

set csprg=/usr/bin/cscope

set csto=0

set cst

set nocsverb

cs add (scope.out의 경로)

set csverb




'Embedded Lab > linux, x86' 카테고리의 다른 글

[extern 키워드]  (0) 2012.04.04
[cp 명령어옵션]  (0) 2012.04.03
[tar.bz2 압축풀기 명령어, tar.bz2 압축하기 방법; bzip2]  (0) 2012.04.01
[우분투 11.10 에서 nvidia 드라이버 설치하기]  (0) 2012.03.15
[VFS]  (0) 2012.02.15
Posted by cyj4369
,


".tar.bz2" 라는 이중 확장자를 가진 압축 파일은, "tar로 여러개의 파일을 하나로 묶은 후, bzip2 로 압축한 파일이라는 뜻입니다. bzip2 자체에는 파일들을 하나로 묶는 기능은 없습니다. 그렇지만 복잡한 명령은 필요없고 tar에 j 옵션을 붙여주면 간단히 bzip2 압축이 됩니다. (다만 오래된 버전의 tar 에서는 안될 수도 있습니다.)

우선 bzip2 로 압축하기 방법입니다.

다음과 같은 명령어를 사용하면, 현재 디렉토리의 모든 txt 확장자의 파일들이 test.tar.bz2 라는 하나의 파일로 묶어지고 또한 압축됩니다.
tar -cjvf test.tar.bz2 *.txt


파일을 풀거나 할 때는 j 옵션이 없어도 됩니다.

tar.bz2 파일 압축 풀기는 다음과 같이 합니다.
tar -xvf test.tar.bz2


tar.bz2의 압축 내용 보기는 다음과 같이 합니다.
tar -tvf test.tar.bz2



시스템에, bzip2bunzip2 라는 실행 파일 즉 명령어가 설치되어 있어야 합니다. 리눅스에는 기본적으로 있는데 오래된 유닉스 시스템에는 없을 수도 있습니다. (bzip2 는 서기2000년에 버전 1.0 이 발표되었습니다.) 만약 없다면 아래와 같은 식으로 에러가 납니다:
tar (child): bzip2: Cannot exec: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error exit delayed from previous errors


'Embedded Lab > linux, x86' 카테고리의 다른 글

[cp 명령어옵션]  (0) 2012.04.03
[cscope 설치 및 사용]  (0) 2012.04.03
[우분투 11.10 에서 nvidia 드라이버 설치하기]  (0) 2012.03.15
[VFS]  (0) 2012.02.15
[시너지]  (0) 2012.01.26
Posted by cyj4369
,

먼저 드라이버는 여기서 : NVidia driver 다운로드센터

Nvidia 드라이버가 NVidia-xx-xx.run 으로 되어있더군요.

그냥 터미널에서 실행하면, xwindows 를 종료하고 설치하라는군요.. -_-;



드라이버를 찾기쉬운곳에 복사해 놓는다 : 예) /home/자신의 아이디

먼저, root 계정 활성화 : 터미널에서 'sudo passwd root'  로 새로운 패스워드를 입력하고 활성화한다.


1. ctrl + alt + f1 으로 콘솔로 나간다.

2. root 계정으로 로그인한다.  

3. 'sudo service lightdm stop' 입력.

4. 'sudo ./home/자신의아이디/NVidia-xx-xx.run'  입력해서 설치 (아니면, 'cd /home/자신의 아이디' 로 이동후 'sudo ./NVidia~.run' 입력)

5.'sudo service lightdm start' 한후 ctrl + alt + f7 로 윈도우로 다시 들어가던지, 아니면  reboot 한다. (sudo reboot now)

'Embedded Lab > linux, x86' 카테고리의 다른 글

[cscope 설치 및 사용]  (0) 2012.04.03
[tar.bz2 압축풀기 명령어, tar.bz2 압축하기 방법; bzip2]  (0) 2012.04.01
[VFS]  (0) 2012.02.15
[시너지]  (0) 2012.01.26
[9장 커널 동기화 방법]  (0) 2012.01.26
Posted by cyj4369
,

[VFS]

Embedded Lab/linux, x86 2012. 2. 15. 17:57

□ 가상 파일 시스템(Virtual File System, VFS)

○ 파일시스템 인터페이스를 유저공간 프로그램에 제공하기 위해 구현된 커널의 서브 시스템

○ 모든 파일 시스템은 공존하는 것 뿐만 아니라 상호동작하기 위하여 VFS에 의존

○ 서로 다른 미디어의 서로 다른 파일 시스템에 대해 읽고 쓰는 작업을 하기 위해 표준 유닉스

시스템 콜을 사용할 수 있게 해줌

○ 실제 파일 시스템의 구현과 사용자 프로세스 사이에 존재하는 추상화 계층

○ 표준 유닉스 파일시스템과 관련한 모든 시스템 콜을 처리하는 kernel software layer

○ VFS 전체 구조

□ 공통 파일 시스템 인터페이스

○ open(), read(), write()와 같은 시스템 콜들이 파일시스템이나 물리적 매체와 무관하게 동작

할 수 있게 하는 역할

 

□ 파일 시스템 추상화 레이어

○ 모든 타입의 파일시스템에 대한 범용 인터페이스는 커널이 로우레벨 파일시스템, 인터페이스와

같은 추상 레이어에서 해당 인터페이스를 구현하기 때문에 사용 가능

⇒ 리눅스로 하여금 지원되는 속성이나 동작에 있어서 상이한 다른 파일시스템을 지원할 수 있게 해줌

○ 추상레이어는 모든 파일 시스템을 지원하는 기초 추상 인터페이스와 자료구조를 정의하는 방식으로 동작

○ 커널이 여러타입의 파일 시스템을 쉽고 명확하게 지원하도록 해줌

 

□ 유닉스 파일 시스템

○ 파일시스템 관련된 4가지 기본 추상화를 제공

◇ 파일

◇ 디렉토리 엔트리

◇ inode

◇ 마운트 포인트

○ 데이터를 특정한 구조를 표현하기 위한 계층적 저장소

◇ 파일, 디렉터리 관련 제어 정보를 포함

◇ 생성, 삭제및 마운팅이란 전형적인 동작을 제공

◇ UNIX에서 파일시스템은 네임스페이스란 알려진 전역 계층구조의 특정 마운트 포인트에 연결

⇒ 마운트된 모든 파일 시스템들은 단일 트리의 항목처럼 보임

파일이란 정렬된 바이트 스트링이라 할 수 있음

파일은 디렉터리 안에 존재

◇ 디렉터리는 서류철과 비슷한 것으로 일반적으로 서로 관련 있는 파일들을 포함

◇ 디렉터리는 서브디렉터리를 포함 가능

◇ 경로를 표현하기 위하여 디렉터리를 중첩사용 가능

◇ 경로의 각 요소를 디렉터리 엔트리라함

○ 파일자체와 파일에 대한 정보를 구분

◇ 이정보는 종종 파일 메타데이터라 불리며 inode라 불리는 분리된 자료구조에 저장

◇ superblock에 저장돼있는 파일시스템의 제어정보와 함께 포함

◇ superblock : 파일시스템 전체에 대한 정보를 담고 있는 자료구조

○ 위의 개념들을 디스크 레이아웃의 일부로 구현

○ 몇몇 비 유닉스 계열의 디스크 기반 파일시스템

◇ 디렉토리 트리에 각 파일이 있는 파일 할당 테이블(FAT) 를 사용

◇ 이 디렉토리들은 파일이 아닌데, 리눅스에서는 FAT-based filesystem 을 필요할 때 디렉토리와

유사한 파일을 즉석으로 구현

◇ 이러한 파일은 커널 메모리에 객체로만 존재

 

□ VFS 객체와 그 자료구조

○ 객체지향적, 일련의 자료구조들이 공통파일모델을 표현하기위해 존재

○ VFS의 네가지 주요 객체

◇ The superblock object

△ 마운트시킨 파일 시스템 정보를 저장

△ disk-based filesystem을 위해서 이 object는 보통 disk에 저장된 filesystem control block 에 해당

◇ The inode object

△ 특정 파일에 대한 일반 정보를 저장

△ disk-based filesystem을 위해서 이 object는 보통 disk에 저장된 filesystem control block에 해당

△ 각 object는 inode number를 가짐

◇ The file object

△ 열린 파일과 프로세스 사이의 상호 작용과 관련한 정보 저장

△ 이 정보는 단지 각 프로세스가 파일을 접근한 동안만 kernel memory에 존재

◇ The dentry object

△ 디렉토리 항목과 대응하는 파일간 연결에 대한 정보 저장

△ 각 디스크 기반 파일 시스템에서는 독특한 방법으로 정보를 디스크에 저장

○ VFS는 디렉토리도 일반파일로 취급한다.

○ 각 객체의 주요함수

◇ super_operation 객체 : read_inode(), sync_fs() 등

◇ inode_operation 객체 : create(), link() 등

◇ dentry_operation 객체 : d_compare(), d_delete() 등

◇ file 객체 : read(), write() 등

실제 C++처럼 명시적 오브젝트가 아니라 구조체를 객체처럼 사용하는 것이다.

 

■ 다른 VFS 객체

file_system_type : 각각의 등록된 파일시스템의 정보

vfsmount : 각각의 마운트 포인트의 정보

file_struct, fs struct, namespace : 프로세스당 3개씩 존재하는 구조체.

 

□ 수퍼블록 객체

<디스크 자료구조>

<superblock object>

○ 각 파일 시스템에 구현되어있음

○ 해당 파일 시스템을 표현하기 위한 다양한 정보들을 포함

(총 inode의 수, filesystem 크기, block 크기, 단편 크기, 그룹 당 inode 수, 마지막 mount한 시간, 마지막 쓰기 시간등 )

○ 디스크의 특별한 섹터에 저장되어 있는 수퍼블록이나 파일 시스템 제어블록에 해당

○ 디스크 기반이 아닌 파일시스템은 동작 중에 수퍼블록을 생성하고 이를 메모리에 저장

(sysfs등의 가상메모리 기반 파일시스템)

○ struct super_block으로 표현 <linux/fs.h>

struct super_block {

struct list_heads_list;//모든 수퍼블록의 목록

dev_ts_dev;//식별자

unsigned longs_blocksize;//bytes 단위의 블록 크기

unsigned longs_old_blocksize;//bytes 단위의 오래된 블록의 크기

unsigned chars_blocksize_bits;//bits 단위의 블록 크기

unsigned chars_dirt;//dirty 플래그

unsigned long longs_maxbytes;//파일 크기의 최대값

struct file_system_types_type;//파일시스템 타입

struct super_operations s_op;//수퍼블록 함수

struct dquot_operations*dq_op;//쿼터(quota) 함수

struct quotactl_ops*s_qcop;//쿼터 제어 함수

struct export_operations *s_export_op;//익스포트(export) 함수

unsigned longs_flags;//마운트 플래그

unsigned longs_magic;//파일시스템의매직넘버(magic number)

struct dentry*s_root;//디렉터리 마운트 포인트

struct rw_semaphores_umount;//언마운트 세마포어

struct semaphores_lock;//수퍼블록 세마포어

ints_count;//수퍼블록 참조 카운터

ints_syncing;//파일시스템 동기(syncing) 플래그

ints_need_sync_fs;//not-yet-synced 플래그

atomic_ts_active;//액티브 참조 카운터

void*s_security;//보안 모듈

struct list_heads_dirty;//dirty inode의 목록

struct list_heads_io;//writebacks의 목록

struct hlist_heads_anon;//익명 dentry

struct list_heads_files;//할당된 파일의 목록

struct block_device*s_bdev;//하당된 블록디바이스 드라이버

struct list_heads_instances;//이러한 형태의 파일시스템 목록

struct quota_infos_dquot;//quota 관련 옵션

chars_id[32];//텍스트 이름

void*s_fs_info;//파일시스템 특유의 정보

struct semaphores_vfs_rename_sem; //디렉터리 rename 세마포어

};

○ 수퍼블록 객체의 생성, 관리 및 파괴를 위한 코드 <fs/super.c>

○ alloc_super() 함수에 의해 생성/초기화

○ 파일 시스템이 마운트되면 파일시스템은 이 함수를 호출하여 디스크로부터 해당 수퍼블록을 읽어들여

스퍼불록 객체를 채움

 

■ 수퍼블록 연산

○ 수퍼블록 연산테이블(superblock operation table) s_op

○ struct super_operations으로 표현 <linux/fs.h>

struct super_oprations {

struct inode *(*alloc_inode) (struct super_block *sb);

void (*destroy_inode) (struct inode *);

void (*read_inode) (struct inode *);

void (*dirty_inode) (struct inode *);

void (*write_inode) (sruct inode *, int);

void (*put_inode) (struct inode *);

void (*drop_inode) (struct inode *);

void (*delete_inode) (struct inode *);

void (*put_super) (struct super_block *);

void (*write_super) (struct super_block *);

int (*sync_fs) (struct super_block *, int);

void (*write_super_lockfs) (struct super_block *)

void (*unlockfs) (struct super_block *);

int (*statfs) (struct super_block *, struct statfs *);

int (*remount_fs) (struct super_block *, int *, char *);

void (*clear_inode) (struct inode *);

void (*umount_begin) (struct super_block *);

int (*show_options) (struct seq_file *, struct vfsmount *);

};

○ 수퍼블록 객체에 대한 동작을 나타내는 포인터 파일시스템과 inode에 대한 로우레벨의 동작을 수행

○ 파일시스템이 수퍼블록에 대해 어떠한 동작을 수행해야 할 경우, 수퍼블록 객체에 대한 포인터 뒤에 필요한

함수를 명시하여 호출할 수 있음

ex) 파일시스템이 해당 수퍼블록을 쓰고(write)싶을 때,

sb->s_op->write_super(sb);

sb: 파일 시스템 수퍼블록에 대한 포인터

sb 포인터 뒤에 s_op를 명시하여 수퍼블록 연산테이블을 가리킬 수 있음

수퍼블록 연산(super_operations)

◇ struct inode *alloc_inode(struct super_block *sb)

- 주어진 수퍼블록에서 새로운 inode 객체를 생성하고 초기화

◇ void destroy_inode(struct inode *)

- 주어진 inode를 삭제

◇ void read_inode(struct inode *)

- inode->i_ino로 주어진 inode를 디스크로부터 읽어 inode 구조체의 나머지 부분을 채움

◇ void dirty_inode(struct inode *)

- inode가 수정된 경우 VFS로부터 호출

- ext3와 같은 저널링(journaling)파일 시스템은 기록을 갱신하기 위하여 이 함수를 사용

◇ void write_inode(sruct inode *, int)

- 주어진 inode를 디스크에 기록

- wait 파라미터는 이러한 동작이 동기적이어야 하는 지를 명시

◇ void put_inode(struct inode *)

- 주어진 inode를 해제

◇ void drop_inode(struct inode *)

- inode에 대한 최근 참조가 드롭(drop)된 경우 VFS로부터 호출

- 호출자는 반드시 inode_lock을 가지고 있어야 함

- 일반적인 유닉스 파일시스템은 VFS가 inode를 그냥 삭제하므로 이 함수를 정의하지 않음

◇ void delete_inode(struct inode *)

- 주어진 inode를 디스크로부터 삭제

◇ id put_super(struct super_block *)

- 언마운트 때 해당 수퍼블록을 해제하기 위하여 VFS로부터 호출

◇ void write_super(struct super_block *)

- 주어진 수퍼블록을 이용하여 디스크상의 수퍼블록을 갱신

- 메모리 상에서 수정된 수퍼블록과 데스크상의 수퍼블록을 동기화 하는데 사용

◇ int sync_fs(struct super_block *, int)

- 파일시스템 메타데이터를 디스크 상의 데이터와 동기화

- wait 파라미터는 해당 동작이 동기적이어야 하는지를 명시

◇ void write_super_lockfs(struct super_block *)

- 파일시스템에 대한 수정을 방지

- 디스크상의 수퍼블록을 명시된 수퍼블록으로 갱신

- 현재 LVM(Logical Volume Manager)에 의해 사용되고 있음

◇ void unlockfs(struct super_block *)

- write_super_lockfs()에 의한 변경이 있는 경우 파일 시스템의 락을 해제

◇ int statfs(struct super_block *, struct statfs *)

- 파일시스템에 대한 통계 정보를 얻기 위하여 VFS에 의해 호출

- 해당 파일시스템과 관련된 통계정보는 statfs에 존재

◇ int remount_fs(struct super_block *, int *, char *)

- 파일시스템이 새로운 마운트 옵션으로 다시 마운트 되는 경우 VFS로부터 호출

◇ void clear_inode(struct inode *)

- inode를 해제하고 이와 관련된 데이터를 포함하고 있는 페이지를 클리어하기 위하여 VFS로부터 호출

◇ void umount_begin(struct super_block *)

- 마운트 동작을 중단하기 위하여 VFS에 의해 호출

- NFS와 같은 네트워크 파일 시스템에서 사용

◇ int show_options(struct seq_file *, struct vfsmount *)

- 모든 함수는 VFS에 의해 프로세스 컨텍스트에서 호출, 모두 블록될 수 있음

- 파일시스템에 따라 일부의 수퍼블록 연산 구조체를 NULL로 설정할 수 있음

- 해당 포인터가 NULL인 경우, VFS는 동작에 따라 범용 함수를 호출하거나 아무런 일도 하지 않음

 

□ inode 객체

○ 파일이 생성될때 만들어진다.

○ 커널이 파일이나 디렉터리를 관리하기 위한 모든 필요한 정보를 표현.

○ 파일과 디렉토리 모두 한 개의 아이노드를 가진다.

○ 디스크에 정적으로 존재한다.

○ inode에는 디바이스파일이나 파이프와 같은 특별한 파일들이 포함된다.

○ 특수파일과 관련된 필드로는 i_pipe, i_devices, i_bdev, i_cdev 등이 있다.

○ 객체는 include/linux/fs.h 에 정의되어있다.

<inode 구조체>

 

<inode에서의 디렉터리 표현>

○ 구조체

struct inode {

struct hlist_nodei_hash;// 해시 목록

struct list_headi_list;// inode들에 대한 연결리스트

struct list_headi_dentry;// dentry들에 대한 연결리스트

unsigned longi_ino;// inode 번호

// 만약이것을 cid로 사용하면 32bit 제약이 따른다

 

atomic_ti_count// 참조카운터

umode_ti_mode;// 접근권한

unsigned inti_nlink;// 하드링크의 개수

uid_ti_uid;// 소유자의 사용자 id

gid_ti_gid;// 소유자의 그룹 id

kdev_ti_rdev;// 실제 디바이스 노드

// device special file인 경우, 실제 device장치 번호를 가지고 있음

 

loff_ti_size;// bytes 단위의 파일크기

struct timespeci_atime;// 최종 파일 접근시간

struct timespeci_mtime;// 최종 파일 수정시간

struct timespeci_ctime;// 최종 파일 변경시간

unsigned inti_blkbits;// bits 단위의 블록 크기

unsigned longi_blksize;// bytes 단위의 블록 크기

// file system independent부분에서 활용하는부분은 stat관련 뿐. <활용가능필드>

 

unsigned longi_version;// 버전번호

// directory cache에서 cache에 대한 버젼 비교 수행

 

unsigned longi_blocks;// block 단위의 파일크기

// independent부분에서 활용되는 부분은 stat관련 뿐이다. <활용 필드>

 

unsigned shorti_bytes;// 소비된 바이트

spinlock_ti_lock;// 스핀락

struct rw_semaphorei_alloc_sem;// si_sem으로 통제된 구역에서 이용

struct semaphorei_sem;// inode 세마포어

// write, writev,truncate inode create/delete 사이의 세마포어

 

struct inode_operations*i_op;// inode 연산테이블

// inode에 대한 operation function pointer

 

struct file_operations*i_fop;// 파일 연산

struct super_block*i_sb;// 연관 슈퍼블락

struct file_lock*i_flock;// 파일 lock 목록

struct address_space*i_mapping// 관련 맵핑

// VM상의 메모리 영역을 나타내는 vm_area_struct 객체의 포인터, 이를 이용하여 현재의

// inode에 걸린 VM상의 메모리 오브젝트를 접근할 수 있다. VM을 위한 필드이며, FS에서

// 는 큰 쓰임이 없다. FS에서는 mandatory lock이 Shared VM으로 걸린 inode인 경우 사

// 용하지 못하도록 하는 것이 고작이다.

 

struct address_spacei_data// 장치 주소 공간

struct dquot*i_dquot[MAXQUOTAS] // inode를 위한 디스크 quota

struct list_headi_devices;// 블록 장치 목록

struct pipe_inode_info*i_pipe;// 파이프 정보

struct block_device*i_bdev;// 블록 장치 드라이버

unsigned longi_dnotify_mask;// 디렉터리 notify 이벤트

struct dnotify_struct*i_dnotify;// 디렉터리 notify 용

unsigned longi_state;// 상태 플래그

unsigned longdirtied when;// 최초로 수정이 발생한 시각

unsigned inti_flags;// 파일시스템 플래그

unsigned chari_sock;// 소켓 여부

atomic_ti_writecount;// 쓰기 작업 카운터

void*i_securiry;// 보안모듈

__u32i_generation;// inode 버전번호

union {

void*generic_ip;// 파일 시스템 관련 정보

} u;

};

 

■ inode 연산

○ 슈퍼블록 연산과 더불어 VFS가 inode에 대해 호출할 수 있는 파일시스템 함수를 설명하는

inode_operations 역시 중요하다.

○ inode 연산의 호출법

◇ i->i_op->truncate(i)// 여기서 I 는 특정한 inode를 가리킨다.

<inode operations 호출과정>

inode 에 관련된 method

◇ 인덱스노드 연산 테이블

create(dir, dentry, mode)

- 새로운 disk inode 를 생성

lookup(dir, dentry)

- dentry object 에 포함된 filename 에 대응하는 inode 를 위해 directory 를 탐색

link(old_dentry, dir, new_dentry)

- dir 에서 old_dentry 에 의해 지정된 file 을 참조하는 새로운 hard link 를 생성

- 새 hard link 는 new_dentry 라는 이름을 사용

unlink(dir, dentry)

- dir 로부터 dentry object 에 의해 지정된 file 의 hard link 를 삭제

symlink(dir, dentry, symname)

- 같은 directory에 있는 dentry object를 가지는 symbolic link를 위한 새로운 inode를 생성

mkdir(dir, dentry, moder)

- 새로운 inode 를 생성

rmdir(dir, dentry)

- dentry object에 포함된 이름의 subdirectory를 삭제

mknod(dir, dentry, mode, rdev)

- 새로운 disk inode를 생성, mode와 rdev는 file type과 device의 major number지정

rename(old_dir, old_dentry, new_dir, new_dentry)

- 디렉토리 내부에서 디엔트리 객체가 나타내는 심볼릭 링크를 위해 새로운 아이노드 생성

readlink(dentry, buffer, buflen)

- buffer에 dentry에 명시된 symbolic link에 대응하는 file pathname을 복사

follow_link(inode, dir)

- inode object에 지정된 symbolic link를 변환

readpage(file, pg)

- 열린 파일에서 자료를 한 페이지 읽어온다.

writepage(file, pg)

- 자료 한 페이지를 열린 파일에 기록한다.

bmap(inode, block)

- inode와 관련된 파일의 file block number에 대응하는 logical block number를 return.

truncate(inode)

- inode에 있는 file의 크기를 수정

permission(inode, mask)

- file에 특정 access mode가 허용되는지 check.

smap(inode, sector)

- 디스크 섹터 번호 결정

updatepage(inode, pg, buf, offset, count, sync)

- 아이노드가 나타내는 파일 자료 한 페이지를 갱신

revalidate(dentry)

- dentry object에 의해 지정된 파일의 cache attribute를 갱신

 

□ 덴트리 객체

○ 디렉토리 entry를 메모리에서 읽으면, VFS에 의해서 dentry structure 의 dentry 객체가 바뀜

○ 프로세스가 찾은 경로의 각각의 component 로 구성된 dentry객체는 커널에 의해 생성

○ dentry객체는 inode와 유사한 구성요소와 연결

○ /tmp/test 경로명을 찾을 때

◇ 커널은 / 디렉토리를 위해 dentry 객체를 생성

◇ 두번째 / 디렉토리의 tmp entry 를 위한 dentry 객체를 생성

◇ 세번째 /tmp 디렉토리의 test entry 를 위한 dentry 객체를 생성

○ dentry 객체는 디스크에 대응하는 이미지가 없음

◇ dentry구조체에는 변경된 객체를 나타내는 field가 없음

◇ dentry객체는 dentry_cache라고 불리는 slab allocator cache에 저장

◇ dentry객체는 kmem_cache_alloc()와 kmem_cache_free()에 의해 생성과 소멸

○ 덴트리 객체는 strcut dentry로 표현, <linux/dcache.h>에 정의

struct dentry {

atomic_t d_count;

unsigned int d_flags;

struct inode * d_inode; /* Where the name belongs to - NULL

is negative */

struct dentry * d_parent; /* parent directory */

struct list_head d_vfsmnt;

struct lis t_head d_hash; /* lookup hash list */

struct list_head d_lru; /* d_count = 0 LRU list */

struct list_head d_child; /* child of parent list */

struct list_head d_subdirs; /* our children */

struct list_head d_alias; /* inode alias list */

struct qstr d_name;

unsigned long d_time; /* used by d_revalidate */

struct dentry_operations *d_op;

struct super_block * d_sb; /* The root of the dentry tree */

unsigned long d_reftime; /* last time referenced */

void * d_fsdata; /* fs-specific data */

unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */

};

Field

Description

atomic_d d_dount

Dentry Object의 사용계수

unsigned int d_flags

Dentry의 flag

struct inode *d_inode

파일 이름과 관련된 inode에 대한 포인터

struct dentry *d_parent

Parent directory에 대한 dentry 포인터

struct list_head d_vfsmnt

VFS mount에 대한 연결 구조체

struct list_head d_hash

Dentry를 찾기위한 hash 연결 구조체

struct list_head d_lru

사용되지 않은 Dentry에 대한 연결 구조체

struct list_head d_child

Parent directory에 포함된 dentry object들에 대한

연결 구조체

struct list_head d_subdirs

하위(sub) directory에 있는 dentry에 대한 연결 구조체

struct list_head d_alias

관련된 inode들에 대한 연결 구조체

struct qstr d_name

파일의 이름

unsigned long d_time

d_revalidate연산에 사용되는 필드로 어떤 시간이 흐른

뒤에 다시 유효한지를 검사하기 위한 것이다.

struct dentry_operations *d_op

Dentry에 대한 연산을 정의한 구조체에 대한 포인터

struct super_block *d_sb

Super block object에 대한 포인터

unsigned long d_reftime

Dentry가 제거된 시점을 가리키는 시간

void *d_fsdata

파일 시스템에 의존적인 데이터 구조를 가르키는 포인터:

이 구조체에 파일 시스템에 고유한 데이터 구조를저장해

둘 수 있다.

unsigned char d_iname[DNAME_INLINE_LEN]

짧은 파일 이름에 대해서 할당된 공간

○ Dentry는 프로세스가 파일시스템에 접근해서 구조적인 정보(directory)를 읽어와서 생성하는 데이터 구조체

○ 실행중인 프로세스는 번번한 접근이 일어나기에 이것을 캐쉬에 넣어서 보관하고 사용을 마쳤을

경우에는 사용이 끝났다는 것을 알리고 다시 재사용되기 위해서 연결리스트로 관리

 

■ 덴트리 상태

○ 덴트리구조체는 상태정보를 가지며,

아무런 유효한 정보를 가지고 있지 못할때는 FREE

◇ 커널에서는 사용하지 않지만 관련된 inode object에 대한 포인터를 가지고 있을경우에는 UNUSED

◇ 현재 커널에서 사용중이라면 Used

◇ dentry와 관련된 inode object를 없지만 나중에 다시 이것을 사용할 가능성이 있을경우를 대비 하여

캐쉬내에 보관할 경우에는 Negaticve

○ Unused

◇ d_count는 0이고, d_inode는 여전히 관련된 inode를 가리킴

◇ 유효한 정보를 유지하지만, 그 내용은 삭제될 수 있다.

○ Used

◇ dentry는 유효한 inode에 해당

◇ 객체에 대한 하나의 사용자가 존재 할수 있음

◇ d_count사용 카운터는 양수, d_inode필드는 연관된 아이노드 객체를 가리킴

◇ 유효한 정보를 유지하지만 이를 제거할 수 없다.

○ Negative

◇ 경로명이 정확하지 않거나 inode가 삭제됐기 때문에 유효한 inode와 연관돼 있지 않음

◇ dentry와 관련된 inode가 더 이상 존재하지 않고 d_inode는 null로 설정

◇ 향후의 참조가 빠르게 처리될수 있도록 dentry는 유지

◇ 같은 file pathname에 대해 탐색 연산을 빨리 해결하기 위해 여전히 dentry cache에 남아있음

○ Free

◇ 유효한 정보가 없고 VFS에 의해 사용되지 않음

◇ 해당 메모리 역역은 슬랩 할당자(slab allocator)에 의해 처리

 

■ 덴트리 캐쉬

○ 디스크에서 디렉토리 엔트리를 읽어서 이에 대응 하는 덴트리 객체를 생성하는 일은 비경제적

⇒ 사용을 끝낸 다음에도 다시 사용할 것 같은 덴트리 객체를 계속 메모리에 유지하는 것이 도움이 됨

⇒ dentry cache를 이용하여 덴트리 객체를 캐싱

⇒ 예를 들어, 사용자는 파일을 편집하고,파일을 컴파일한 다음 다시 편집하고, 인쇄하거나 복사하고,

복사한 파일을 편집한다. 이러한 경우 동일한 파일에 반복하여 접근하게된다.

○ 덴트리 캐쉬는 3부분으로 이루어져 있음

◇ 상태가 used인 덴트리들의 리스트

- inode객체의 필드 I_dentry를 통하여 inode들과 연결됨

- inode는 다수의 링크를 가질수 있으므로 다수의 덴트리 객체가 존재 할수 있으며 따라서 리스트가 사용

◇ 최근에 가장 적게 사용된 리스트

- 상태가 unused와 negative인 덴트리 객체에 대한 이중 연결 리스트

- 최산항목이 헤드(head)에 가깝도록 삽입시간을 기준으로 정렬

- 커널이 메모리를 회수하기 위해 항목을 제거하는 경우 리스트의 꼬리부터 제거

⇒ 리스트의 꼬리에 잇는 항목이 가장 오래 됐고 따라서 앞으로 사용할 확률이 가장 적음

◇ 해시테이블(hash table)과 해시함수

- 주어진 경로와 관련된 덴트리 객체를 빠르게 연관시키기 위한

- dentry_hashtable 배열을 사용하여 해시 테이브을 구현

- 각 항목은 해시 함수를 거치면 같은 해시 테이블 값으로 해시되는 디엔트리들의 리스트를 가리키는 포인터

- 배열의 크기는 시스템의 메모리 용량에 따라 다름

- 디엔트리 객체의 d_hash 필드는 해시 값이 같은 리스트의 인접 항목에 대한 포인터를 포함

- 해시 함수는 디렉토리와 파일명의 디엔트리객체 주소 두 개로부터 해서 값을 생성한다.

- 해시테이블 참조는 d_lookup()을 통하여 수행

⇒ 캐시를 검색하여 일치하는 덴트리 객채가

발견될 경우 해당항목을 반환

구걿지 않을 경우 NULL이 반환

○ dcache_lock 스핀 락은 멀티프로세서 시스템에서 디엔트리 캐시 자료 구조에 대한 동시

접근을 방지

 

■ 덴트리 연산

○ dentry_operations구조체는 VFS가 해당 파일시스템의 데릭터리에 대해 호출하는 함수가 명시

○ dentry_operations은 <linux/dcache.h>에 정의

◇ d_revalidate(dentry, flag)

- 파일 경로명을 변환하기 위해 디엔트리 객체를 사용하기 전에 디엔트리 객체가 아직도 유효한지 결정

- 기본 vfs 함수는 아무 일도 하지 않지만, 네트워크 파일시스템의 경우에 자신의 함수를 지정

○ d_hash(dentry, name)

- 해시 값을 생성

- 덴트리 해시 테이블에 사용되며, 파일시스템마다 독자적인 해시함수

- dentry 매개 변수는구성 요소를 포함하는 디렉토리

- name 매개 변수는 탐색하고자 하는 경로명 구성 요소, 해시 함수가 생성하는 값을포함하는 구조체

○ d_compare(dir, name1, name2)

- 두파일명을 비교

- name1은 반드시 dir이 가리키는 디렉토리에 속해야 함

- 기본 VFS함수는 단순 문자열 비교 함수

- 그러나 각 파일시스템은 자체의 방법으로 이 메소드를 구현할 수 있다.

예를 들어, MS-DOS는 대소문자를 구별하지 않는다.

○ d_delete(dentry)

- 덴트리 객체에 대한 마지막 참조가 삭제되면(d_count가 0이 될 때) 호출

- 기본 VFS 함수는 아무 일도 하지 않는다.

○ d_release(dentry)

- 덴트리 객체를 해제할 때(슬랩 할당자에 반납) 호출

- 기본 VFS 함수는 아무 일도 하지 않는다.

○ d_iput(dentry, ino)

- 덴트리 객체가 '네거티브'가 될 때 즉 아이노드를 잃을 때 호출

- 기본 VFS 함수는 아이노드 객체를 해제하기 위해 iput()을 호출

 

□ 파일 객체

○ 파일객체는 프로세스에 의해 열려있는 파일을 표현하기 위하여 사용된다.

○ 프로세스에서는 디렉토리를 수퍼블록이나 inode, 또는 덴트리가 아닌 파일로 취급한다. 그렇기

때문에 파일객체에 포함된 정보는 낯설지가 않다. 예를들어 연산의 read()나 write()와 같은 시스템

콜과 매우 유사한 명령어도 되어있다.

○ 또, 파일객체는 열려있는 파일에 대한 메모리 내부의 표현이다. 객체는 open()시스템 콜의 결과로

생성되며, close()시스템 콜로 삭제된다. 이와 같이 파일과 관련된 모든 호출들은 파일연산 테이블에

정의돼있는 실제 함수들이다. 여러 개의 프로세스가 동시에 하나의 파일을 열고 조작할 수 있으므로

동일파일에 대하여 여러 개의 파일 객체가 존재할 수 있다.

○ 파일 객체는 단지 열려진 파일에 대한 프로세스의 뷰를 표현해 줄 뿐이다.

○ 파일 객체는 struct file로 표현되며 <linux/fs.h>에 정의되어있다.

○ 구조체

struct file {

 

struct list_headf_list;/*파일 객체 리스트*/

struct dentry*f_dentry;/*연관돼있는 덴트리 객체*/

struct vfsmount*f_vfsmnt;/*연관돼있는 마운트된 파일시스템*/

struct file_operations*f_op;/*파일 연산 테이블*/

atomic_tf_count;/*파일 객체의 사용량 카운터*/

unsigned int f_flags;/*open시 명시되는 플래그*/

mode_tf_mode;/*파일 접근 모드*/

loff_tf_pos;/*파일 오프셋*/

struct fown_structf_owner;/*signal을 위한 사용자 데이터*/

unsigned int f_uid;/* 사용자의 UID*/

unsignen int f_gid;/*사용자의 GID*/

int f_error;/*에러코드*/

struct file_ra_statef_ra;/*read_ahead상태*/

unsigned longf_version;/*버전 번호*/

void *f_security; /*보안 모듈*/

void *private_data;/*tty 드라이버 훅(hook)*/

struct list_headf_ep_links;/* eventpoll 링크드리스트*/

spinlock_tf_ep_lock;/* eventpoll 락(lock)*/

struct address_space*f_mapping;/*페이지 캐시 매핑*/

};

○ 덴트리 객체와 유사하게 파일 객체는 디스크의 어떤 데이터에도 대응되지 않는다. 따라서 파일 객체에는

객체가 수정되어 디스크로 써져야 하는지를 나타내기 위한 어떠한 플래그도 존재하지 않는다. 파일 객체는

포인터 f_dentry를 이용하여 해당 객체와 연관된 덴트리 객체를 가리킨다. 이 덴트리 객체는 다시 해당 inode를

가리킨다.

 

■ 파일 연산

○ 파일 객체와 연관돼있는 연산들은 표준 유닉스 시스템콜의 기본을 구성하는 잘 알려진 시스템콜들이다.

○ 파일 객체 함수는 <linux/fs.h>에 정의된 file_operations 구조체에 명시돼있다.

○ 구조체

struct file_operations {

 

struct module *owner;

loff_t (*llseek) (struct file *, char *, loff_t, int);

ssize_t (*read) (struct file*, char *, size_t, loff_t *);

ssize_t (*aio_read) (struct kiocb *, char *, size_t, loff_t);

ssize_t (*write) (struct file *, const char *, size_t, loff_t *);

ssize_t (*aio_write) (struct kiocb *, const char *, size_t, loff_t);

int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, sturct poll_table_struct *);

int (*ioctl) ( struct inode *, struct file *, ubsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, struct dentry *, int);

int (*aio_fsync) (struct kiocb *, int);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*sendpage) (struct file *, loff_t *, size_t, read_actor_t, void *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned,

long, unsigned long, unsigned long);

int (*check_flags) (int flags);

int (*dir_nitify) (struct file *flip, unsigned long arg);

int (*flock) (struct file *flip, int cmd, struct file_lock *fl);

};

파일 시스템은 각각의 연산들에 대하여 고유의 함수를 구현하거나 가능한 경우 범용함수를 사용한다.

필요하지 않을때는 해당 함수를 NULL로 설정한다.

각각의 연산들을 보면

◇ loff_t llseek(struct file *file, loff_t offset, int origin)

- 파일 포인터를 주어진 오프셋으로 갱신하며 시스템콜 llseek()에 의해서 호출된다.

◇ ssize_t (*read) (struct file *file, char *buf, size_t count, loff_t *offset)

- 주어진 파일의 위치 offset에서 count만큼의 바이트를 buf로 읽어들인다. 이후 파일 포인터는 갱신된다.

이 함수는 시스템콜 read에 의해서 호출

◇ ssize_t (*aio_read) (struct kiocb *iocb, char *buf, size_t count, loff_t offset)

- iocb에 명시된 파일에서 count바이트를 buf로 읽어들이는 비동기 읽기동작은 시작한다.

이 함수는 시스템콜 aio_read()에 의해서 호출된다.

◇ ssize_t (*write) (struct file *file, const char *buf, size_t count, loff_t *offset)

- 해당 파일의 위치 offset에 buf의 내용을 count 바이트 만큼 기록한다. 이 함수는 write()함수에

의해서 호출된다.

◇ ssize_t (*aio_write) (struct kiocb *iocb, const char *buf, size_t, loff_t offset)

- buf의 내용에서 count 바이트만큼을 iocb가 나타내는 파일에 기록하는 비동기 쓰기 동작을 시작한다.

이 함수는 시스템콜 aio_write함수에 의해 호출된다.

◇ int (*readdir) (struct file *file, void *dirent, filldir_t filldir)

- 이 함수는 디렉토리 목록의 다음 디렉토리를 반환한다. 이 함수는 시스템콜 readdir()에 의해 호출된다

◇ unsigned int (*poll) (struct file *file, sturct poll_table_struct *poll_table)

- 해당 파일에 특정한 동작이 있을때까지 휴면하며 기다린다. 함수는 시스템콜 poll()에 의해 호출된다.

◇ int (*ioctl) ( struct inode *inode, struct file *file, ubsigned int cmd, unsigned long arg)

- 디바이스에 명령과 매개변수를 전송한다. 이 함수는 파일이 오픈 디바이스 노드일 때 사용되며,

시스테콜 ioct1()에 의해 호출된다.

◇ int (*mmap) (struct file *file, struct vm_area_struct *vma)

- 주어진 파일을 주어진 주소공간 메모리 맵핑하며 시스템콜 mmap()에 의해서 호출된다.

◇ int (*open) (struct inode *inode, struct file *file)

- 새로운 파일 객체를 생성하고 이를 해당 inode객체에 연결한다. 이 함수는 시스템콜 open()에 의해서 호출된다.

◇ int (*flush) (struct file *file)

- 오픈된 파일의 참조 카운터가 감소할 때마다 VFS에 의해서 호출되며 이 함수의 사용 목적은 파일

시스템마다 다르다.

◇ int (*release) (struct inode *inode, struct file *file)

- 파일에 대한 최종 참조가 파괴될 때 VFS에 의해서 호출된다. 예를 들어 파일 서술자를 공유하는

마지막 프로세스가 close()를 호출하거나 종료되는 경우를 들 수 있다. 이 함수의 사용목적은

파일 시스템마다 다르다.

◇ int (*fsync) (struct file *file, struct dentry *dentry, int datasync)

- 파일에 대한 모든 캐시된 데이터를 디스크에 쓰기 위해 사용되며 시스템콜 fsync()dp 의해서 호출된다.

◇ int (*aio_fsync) (struct kiocb *iocb, int datasync)

- iocb와 연관된 파일의 모든 캐시된 데이터를 디스크에 쓰기 위해 사용되며 시스템콜 aio_fsync()에

의해 호출된다.

◇ int (*fasync) (int, struct file *file, int on)

- 비동기 I/O의 시그널 통지를 활성화 시키거나 비활성화시킨다.

◇ int (*lock) (struct file *file, int, struct file_lock *lock)

- 주어진 파일의 파일 락을 조작한다.

◇ ssize_t (*readv) (struct file *file, const struct iovec *vector, unsigned long count, loff_t *offset)

- 주어진 파일에서 데이터를 읽고 그 결고를 vector로 표현되는 count개의 버퍼들에 저장하기 위해

시스템콜readv()로부터 호출된다. 이후 오프셋은 증가된다.

◇ ssize_t (*writev) (struct file *file, const struct iovec *vector, unsigned long count, loff_t *offset)

- vector로 표현되는 count개의 버퍼들에서 특정 파일로 쓰기 위해 시스템콜writev()로부터 호출된다.

이후 파일 오프셋은 증가된다.

◇ ssize_t (*sendpage) (struct file *file, loff_t *page, size_t size, loff_t *pos, int more)

- 하나의 파일에서 다른 파일로 데이터를 전송하기 위해 사용된다.

◇ unsigned long (*get_unmapped_area) (struct file *file, unsigned long addr, unsigned long len,

unsigned long offset, unsigned long flags)

- 주어진 파일과 매핑하기 위한 미사용 주소 영역을 획득하기 위해 사용된다.

◇ int (*check_flags) (int flags)

- 이 함수는 SETFL명령이 주어졌을때 fcnt1()시스템 콜에 전달될 플래그들의 유효성을 검사하기 위해

사용된다. 많은 VFS동작에 대해서는, 파일 시스템에 check_flags()를 구현할 필요가 없다. 현재로서

NFS에만 구현되어 있다. 이 함수는 해당 파일시스템이 범용 fcnt1()함수에서는 허영하지만 우효하지

않은 SETFL 플래그를 제한할 수 있게 해준다. NFS의 경우, O_APPEND와 O_DIRECT를 함께 쓰는

것은 허용되지 않는다.

◇ int (*flock) (struct file *flip, int cmd, struct file_lock *fl)

- 이 함수는 권고 잠금을 지원하기 위한 flock()시스템콜을 구현하기 위해 사용된다.

□ 파일 시스템과 관련된 자료구조

○ 기본적인 VFS 객체들과 함께, 파일시스템과 관련된 자료들을 다루기 위하여 다른 표준 자료

구조를 사용

○ 다수의 서로 다른 파일시스템을 지원하기 때문에 각 파일시스템의 기능이나 동작을 표현하기

위한 특별한 구조체가 필요

○ struct file_system_type: ext3나 XFS와 같은 파일시스템의 변종을 표현하기 위해 사용

struct file_system_type {

const char*name;//파일시스템의 이름

struct subsystemsubsys;//sysfs 서브시스템 객체

intfs_flags;//파일시스템 타입 플래그

 

//이 함수는 디스크로부터 수퍼블록을 읽어 들이는데 사용된다.

struct super_block *(*get_sb) (strut file_system_type *, int, char *, void *);

 

//이 함수는 수퍼블록에 대한 접근을 종료시키기 위하여 사용된다.

void(*kill_sb)(struct super_block *);

 

struct module *owner;//연관된 모듈 (하나라도 존재하는 경우)

struct file_system_type *next;//리스트의 다음 file_system_type 객체

struct list_head fs_supers;//수퍼블록 객체들의 리스트

};

 

◇ get_sb(): 디스크로부터 수퍼블록을 읽어 들이고 파일시스템이 로드뒜을 때 수퍼블록 객체를 생성하기

위해 사용

◇ 나머지 함수들은 파일 시스템의 속성들을 표현

◇ 파일시스템마다 오직 하나의 file_system_type이 존재

(시스템에 마운트된 파일시스템의 수에 상관없이)

○ 파일시스템의 마운트된 인스턴스를 표현하기 위해 사용

○ 파일 시스템의 특정한 인스턴스, 즉 마운트 포인트(mount point)를 나타내는데 사용

○ vfsmount <linux/mount.h>

struct vfsmount {

struct list_head mnt_hash;//해시 테이블 리스트

struct vfsmount*mnt_parent;//부모 파일시스템

struct dentry*mnt_mountpoint//해당 마운트 포인트의 dentry

struct dentry*mnt_root;//해당 파일시스템의 root의 dentry

struct super_block *mnt_sb;//해당 파일시스템의 수퍼블록

struct list_headmnt_mounts;//자식들(children)의 리스트

struct list_headmnt_child;//자식들의 리스트

atomic_tmnt_count;//사용량 카운터

intmnt_clags;//마운트 플래그들

char*mnt_devname;//디바이스 파일명

struct list_headmnt_list;//서술자들의 리스트

struct list_headmnt_fslink;//파일시스템별 만료 리스트

struct namespace *mnt_namespace//관련 네임스페이스

};

○ vfsmount의 연결 리스트들로 파일 시스템과 다른 모든 마운트 포인트들 간의 관계유지

○ 필드에 마운트에 관한 플래그를 저장

◇ 표준 마운트 플래그 목록

플래그

설명

MNT_NOSUID

해당 파일시스템의 바이너리에 대한 setuid와 setgd 플래그 금지

MNT_NODEV

해당 파일시스템의 디바이스 파일에 대한 접근 금지

MNT_NOEXEC

해당 파일시스템의 바이너리에 대한 실행 금지

- 관리자가 신뢰할 수 없는 이동식 디바이스에 가장 유용 <linux/mount.h>에 정의

 

□ 프로세스와 연관된 자료구조

○ VFS 계층과 시스템의 프로세스를 하나로 묶어줌

○ file_struct <linux/file.h>

◇ 이 테이블에 대한 주소는 프로세서 서술자의 files 항목에 저장

◇ 모든 프로세스마다 열린 파일과 파일 서술자 정보가 들어있음

struct files_struct {

atomic_tcount;//구조체의 사용량 카운트

spinlock_tfile_lock;//해당 구조체를 보호하는 락(lock)

intmax_fds;//파일 객체의 최대 개수

intmax_fdset;//파일 서술자의 최대 개수

struct file **fd;//모든 파일 객체의 배열

fd_set*close_on_exec; //exec() 시 close 하기 위한 파일 서술자

fd_set*open_fds;//오픈된 파일 서술자들에 대한 포인터

fd_setclose_on_exec_init;//exec() 시 close하기 위한 초기파일

fd_setopen_fds_init;//파일 서술자들의 초기 집합

struct file *fd_array[NR_OPEN_DEFAULT]; //파일 객체의 기본 배열

};

◇ fd 배열: 열린 파일 객체의 목록 (기본 fd_array 배열)

◇ NR_OPEN_DEFAULT = 32

◇ 32개 파일 객체에 대한 공간을 포함

◇ 프로세스가 32개 이상의 파일 객체를 여는 경우,

◇ 커널은 새로운 배열을 할당하고 해당 배열의 fd 포인터를 저장

(정적 배열을 사용하여 적당한 수의 파일 객체에 대한 접근은 빠르게 처리)

◇ 시스템관리자는 프리프로세서로 정의된 NR_OPEN_DEFAULT 값을 증가시킬 수 있음

○ fs_struct <linux/fs_struct.h>

◇ 프로세스와 관련된 파일시스템 정보를 포함

◇ 포인터가 프로세스 서술자의 fs 필드에 저장됨

struct fs_struct {

atomic_tcount;//구조체 사용량 카운트

rwlock_tlock;//구조체 보호 락(lock)

intumask;//디폴트 파일 퍼미션

struct dentry*root;//루트 디렉터리의 dentry

struct dentry*pwd;//현재 디렉터리의 dentry

struct dentry*altroot;//alternative 루트에 대한 dentry

struct vfsmount*rootmnt;//루트 디렉터리의 마운트 객체

struct vfsmount*pwdmnt;//현재 디렉터리의 마운트 객체

struct vfsmount *altrootmnt;//alternative 루트 디렉터리에 대한 마운트 객체

};

◇ 현재의 작업 디렉터리와 현 프로세스의 루트 디렉터리를 포함

○ namespace <linux/namespace.h>

◇ 프로세스 서술자의 namespace 필드에 의해 참조

◇ 리눅스 커널 2.4에서는 프로세스마다 네임스페이스가 추가되어서 각 프로세스가 시스템에 마운트된

파일시스템에 대한 유일한 뷰를 가질 수 있도록 해줌

(유일한 루트디렉터리 뿐만 아니라, 필요한 경우 유일한 전체적인 파일시스템의 계층제공)

struct manespace {

atomic_tcount;//구조체 사용량 카운트

struct vfsmount*root;//루트 디렉터리의 마운트 객체

struct list_headlist;//마운트 포인트들의 리스트

struct rw_semaphoresem;//네임스페이스를 보호하는 세마포어

};

- list: 네임스페이스를 구성하는 마운트된 파일시스템의 이중 연결리스트를 명시

○ 자료구조는 각 프로세스 서술자와 연관됨

○ 대부분의 프로세스는 그들의 프로세스 서술자가 유일한 files_struct과 fs_struct 구조체를 가리킴

○ 복제 플래그 CLONE_FILES 또는 CLONE_FS와 함께 생성된 프로세스들에 대해서는 공유

◇ 스레드: 일반적으로 CLONE_FILES과 CLONE_FS 플래그를 사용, 하나의 files_struct과 fs_struct을 공유

◇ 정규 프로세스: 각자 다른 파일시스템 정보와 열린 파일 테이블을 갖음

다중 프로세스 서술자는 동일한 files_struct 또는 fs_struct 구조체를 가리킬 수 있음

각 구조체의 count 멤버는 프로세스가 해당 구조체를 사용하는 동안에 구조체가 파괴되는 것을 막기 위한

참조 카운트를 제공

○ namespace 구조체는 정반대의 방식으로 동작

◇ 기본적으로 모든 프로세스들은 동일 네임스페이스를 공유

(동일한 마운트 테이블로부터 동일한 파일시스템 계층을 참조)

◇ clone()에서 CLONE_NEWNS 플래그가 명시된 경우에만 해당 프로세스는 유일한 namespace 구조체의

사본을 부여받음

◇ 대부분의 프로세스들은 이 플래그를 사용하지 않으므로, 부모의 네임스페이스를 상속

◇ 그러므로 많은 시스템들에선 단 하나의 네임스페이스가 존재

 

□ 리눅스의 파일시스템

○ 약 50개 이상의 파일 시스템을 공식 커널에서 지원

(네이티브(native) 파일시스템(ext2, ext3 등), 네트워크 파일시스템(NFS, Coda 등))

○ VFS 레이어

◇ 여러 종류의 파일시스템들을 각각의 구현, 표준 시스템콜을 이용한 인터페이스란 두 가지 관점에 대한

통일된 프레임워크를 제공함으로써 지원해줌

◇ 리눅스에서의 새로운 파일시스템 구현을 명확하게 해줌

◇ 새로 구현된 파일시스템이 유닉스의 표준 시스템콜을 통하여 자동적으로 상호작용할 수 있도록 해줌

 

Posted by cyj4369
,
컴퓨터를 여러대 사용하는 경우 아무래도 가장 불편한 점은 마우스와 키보드의 사용에 있을것이다.

키보드와 마우스에 손을 번갈아가며 왔다갔다하는 이런 귀찮음(?)정말 참을수가 없다.

더군다나 집에서 사용하는 컴퓨터 책상이 많이 좁고 불편해서(나의 컴퓨터생활에서 가장 오래된 놈이라고 할수있다.)

요런 상태에서 인터넷을 돌아다니다 발견한것이 synergy라는 키보드 마우스 공유 프로그램이다.

좀 더 찾아보니 Multiplicity 라는 것도 있지만 요놈은 상용에다 설정도 까다롭단다.

synergy의 장점으로는 오픈소스 프로젝트로 다양한 플랫폼을 지원하다.

따라서 본인이 쓰고 있는 환경에 가장 적합하다.


< 본인의 환경 >
윈도우즈 데스크탑
리눅스 노트북

그럼 본격적으로 시너지를 사용해 보자.


1. 호스트 설정

다음 사이트에서 시너지를 다운받을수 있다.

http://synergy2.sourceforge.net/index.html

각자의 환경에 맞는 파일을 받으면 되는것이다.

본인은 윈도우 환경에서 호스트를 구축할것이므로 윈도우용 바이너리를 받고 설치하였다.


사용자 삽입 이미지

server로 사용할 것이기 때문에 두번째 옵션인 Share this computer's keyboard and mouse (server)를 선택한다.

설정을 완료하게 되면 test 및 시작을 할수 있다.

사용자 삽입 이미지

사용할 스크린을 설정한다.

여기서 winxp는 호스트환경 supaflow-laptop( 본인의 노트북이름이다.)이다.

사용자 삽입 이미지

사용자 삽입 이미지

호스트에서 사용할 이름인 winxp를 입력한다.

포트는 기본값으로 사용하면 된다.


사용자 삽입 이미지

자동시작 메뉴로 왼쪽은 로그인했을때 오른쪽은 컴퓨터가 부팅을 하였을때와 같이 각각 설정할수 있다.

본인의 목적에 맞도록 설정하자.


2. 클라이언트(리눅스) 설정

호스트를 설정하였으니 리눅스에서 클라이언트를 설정해보자. 마찬가지로 위의 사이트에서 synergy받은 다음 설치하면 된다.

우분투같은 경우는 apt-get install synergy 로 설치할수 있다.

/etc/hosts 파일을 열어서 사용할 호스트와 클라이언트 주소, 호스트에서 설정한 이름을 적어주면 된다.

192.168.0.1 winxp

192.168.0.125 client

2.1 실행

실행할때는 터미널에서 다음 명령어를 실행하면 클라이언트가 실행된다.

synergyc winxp // 여기서 winxp는 호스트에서 설정한 스크린 이름


설정이 다되었으니 이제 사용해보자. 네트워크 연결을 확인한 후 마우스를 왼쪽 오른쪽으로 왔다갔다 하면 키보드와 마우스가 공유되는 희열(?)을 맛볼수 있다.


p.s : 리눅스에서 한글키가 안먹히니 패치를 구해서 사용하면된다.
http://magoja.com/tt/242

'Embedded Lab > linux, x86' 카테고리의 다른 글

[우분투 11.10 에서 nvidia 드라이버 설치하기]  (0) 2012.03.15
[VFS]  (0) 2012.02.15
[9장 커널 동기화 방법]  (0) 2012.01.26
[8장 커널 동기화 개요]  (0) 2012.01.26
[Top-Half와 Bottom-Half]  (0) 2012.01.26
Posted by cyj4369
,
  1. 원자적 동작(Atomic Operation)

    • 중단됨이 없이 한번에 실행되는 명령. 원자(Atom)가 더 이상 쪼갤 수 없는 원소를 의미하듯 원자적 동작은 나눠지지 않는 명령등을 의미한다.
  2. 원자적 정수연산, 원자적 비트연산

    • 원자적 정수연산은 특별한 자료구조인 atomic_t를 사용한다.

      1. 원자적인 함수들이 다른 자료형에 잘못 사용되는 것을 방지.
      2. 원자적동작이 엘리어스(alias)가 아닌 실제 메모리 주소를 사용하게 한다.
      3. 컴파일러가 이 자료형으로의 접근을 잘못 최적화하지 않도록 방지.
      4. 아키텍처에 따라 다른 구현을 감춰주는 역할.
    • 커널코드 작성시 모든 아키텍처에서 사용가능한 함수를 확인해 보아야 한다.
  3. 스핀락(Spin Lock)

    • 최대한 하나의 스레드에 의해 잠길 수 있는 락을 말한다.

      1. 만약 어떤 스레드가 이미 잠겨진 스핀락을 다시 잠그려 시도한다면 그 스레드는 루프(Busy Loop)를 돌면서(Spin)락을 잠글 수 있을때 까지 기다린다.
      2. 이러한 스피닝(Spinning)은 동시에 하나 이상의 스레드가 같은 위험구역에 진입하는 것을 방지해 준다.
    • 다른 스레드들을 스핀하도록(프로세스 시간을 소모함) 하므로 두번의 컨텍스트 스위칭 시간보다 짥게 잡고 있도록 하는 것(휴면방식을 사용하는것) 이 중요하다.
    • 프로세서가 하나인 시스템 에서도 인터럽트 핸들러가 공유 데이터를 접근하는 것을 방지하기 위해 인터럽트를 비활성화해야 한다.
    • 실제로 보호가 필요한 것은 코드 자체가 아닌 위험구역 안에 있는 데이터이므로 락을 특정한 데이터와 관련시키는 것이 좋다. "struct foo 는 foo_lock 으로 잠김"
  4. 스핀락과 보톰하프(Bottom Half)

    • 보톰하프는 프로세스 컨텍스트 코드를 선점할 수 있으므로, 만약 어떤 데이터가 보톰하프와 프로세스 컨텍스트간에 공유된다면 락을 사용함과 함께 보톰하프를 비활성화시켜 데이터를 보호해야 한다.

      • 프로세스 컨텍스트란 ? 프로세서 고유 컨텍스트(Processor Specific Context) 프로세스는 시스템의 현재 상태의 총합 으로 생각할 수 있다. 프로세스는 실행될 때마다, 프로세서의 레지스터와 스택 등을 사용 한다. 이것이 프로세스 컨텍스트이며, 프로세스가 중단될 때 CPU 고유의 컨텍스트들은 모두 그 프로세스의 task_struct에 저장되어야 한다. 스케쥴러가 이 프로세스를 다시 시작할 때, 이 컨텍스트는 이 정보로부터 복구된다.
  5. Reader 와 Writer 스핀락

    • 리스트가 갱신(쓰기)되는 경우에는 다른 코드가 동시에 리스트에 쓰거나 읽지 않도록 해야 한다. 상호배제(Mutual Exclusion)가 필요하다.
    • 리스트를 탐색(읽기)하는 경우에는 다른 코드가 동시에 리스트에 쓰는 것만을 방지하면 된다. 쓰기작업이 없다면 동시에 여러 곳에 탐색이 가능하다.
    • 자료구조의 사용이 Reader/Writer로 확연히 구분되는 경우(생산자/소비자)에는 락킹 메커니즘은 Reader/Wrtier 스핀락을 제공한다.
    1. rwlock_t mr_rwlock = RW_LOCK_UNLOCKED;
    2. read_lock(&mr_rwlock);
    3. /* 위험 구역(읽기 전용) */
    4. read_unlock(&mr_rwlock);
    5. write_lock(&mr_rwlock);

    6. /* 위험 구역(읽기, 쓰기 가능) */
    7. write_unlock(&mr_rwlock);
    • 대기중인 writer는 모든 Reader가 락을 해제할 때까지 락을 잠그지 못하게 되므로 리더가 상당 수 있는 경우 기아상태가 발생하게 될 수 있다.
  6. 세마포어

    • 락을 잡고 있는 시간이 길어지거나 락을 잡은 채 휴면해야 하는 경우에 사용한다.
    • 동작시나리오

      1. 어떤 태스크가 이미 잠겨진 세마포어를 잠그려 하는 경우, 세마포어는 그 태스크를 대기큐로 삽입하고 해당 태스크를 휴면 상태로 만든다.
      2. 프로세서는 다른 코드를 실행할 수 있게 된다.
      3. 세마포어를 잡고 있는 프로세스가 락을 해제하면, 대기큐에 있는 하나의 태스크가 깨어나서 세마포어를 잡게 된다.
    • 바쁜 루프로 시간을 소모하지 않기 때문에 스핀락보다 더 나은 활용도를 보여주지만 스핀락에 비해 세마포어는 훨씬 많은 부가작업이 필요하다.( 인생은 Trade-Off -_ㅡ;; )
    • 세마포어의 휴면 동작이 주는 의미

      1. 락을 기다리는 태스크들이 휴면하게 되므로 세마포어는 락을 오랫동안 잡게 되는 상황에 적합하다.
      2. 락을 잡고 있는 시간이 짧을 경우 휴면하고 대기큐를 관리하고 다시 깨워주는 추가작업들이 락을 잡고 있는 시간보다 길어질 가능성이 있다.(비효율적.)
      3. 스레드의 실행은 락 경쟁에서 휴면 상태가 될 수 있고 인터럽트 컨텍스트는 스케줄이 불가능하기 때문에 세마포어는 오직 프로세스 컨텍스트에서만 얻을 수 있다.
      4. 세마포어를 잡은채로 휴면할 경우 다른 프로세스가 같은 세마포어를 잡으려 하더라도 데드락에 빠지지 않는다.( 두번째 스레드 역시 휴면되므로 결국 첫번째 스레드가 실행)
      5. 세마포어를 사용할 경우 스핀락을 잡고 있어서는 안되는데, 왜냐하면 세마포어를 잡으려면 휴면해야 하는데 스핀락을 잡고 있는 상태에서는 휴면해서는 안된다.
    • 세마포어 VS 스핀락

      1. 락을 잡고 있는 시간을 근거로 결정을 내려야 한다.
      2. 세마포어는 커널 선점을 비활성화하지 않으므로 세마포어를 잡고 있는 코드는 선점될 수 있다.( 스케줄링에 불리하게 작용하지 않는다. )


        요구사항 추천
        락 부담이 적어야 하는 경우 스핀락
        락 기간이 짧은 경우 스핀락
        락 기간이 긴 경우 세마포어
        인터럽트 핸들러에서 락을 해야 하는 경우 반드시 스핀락(스케줄링)
        락을 소유한 채 휴면해야 하는 경우 반드시 세마포어
    • 세마포어는 동시에 여러 스레드가 같은 락을 잠글 수 있다. 선언시 그 숫자를 지정할 수 있다.

      1. 락의 갯수를 1로 지정하여 하나의 스레드만이 이 락을 잡을 수 있도록 한것을 바이너리 세마포어, 상호배제 관점에서의 뮤텍스라고 부른다.
      2. 카운팅 세마포어는 다수의 스레드가 동시에 같은 위험구역에 진입할 수 있도록 하므로 상호배제를 보장하지 않는다.
    • Reader-Writer 세마포어

      1. 모든 Reader-Writer 세마포어는 뮤텍스이다.
      2. Wirter 가 없는 한 다수의 Reader가 락을 소유할 수 있고 Reader 가 없는 상황에서 오직 하나의 Writer 만 락을 소유할 수 있다.
  7. 완료 변수( Completion Variable )

    • 커널의 두 태스크를 동기화시키기 위해 사용, 한 태스크가 이 변수를 통해 다른 태스크로 이벤트를 알려주는 식으로 동작한다.
    • 세마포어를 대신하여 간단히 사용하기 위해 제공되는 방법이다.
    • complete() 을 호출하여 완료변수에 시그널 되기를 기다리는 대기중인 태스크들을 깨어나도록 하는 방법.
  8. 큰 커널 락(BKL, Big Kernel Lock)

    • 특성

      1. 소유한 상태에서 휴면할 수 있다.
      2. BKL이 잠겨있는 경우 커널 선점은 비활성화 된다.
      3. 재귀적인 락이다.
      4. 오직 프로세스 컨텍스트에서만 사용가능하다.
      5. 이것은 필요악이다. 악마의 현신!!
    1. lock_kernel();
    2. /*
    3. 다른 모든 BKL 사용자들과 동기화되는 위험 지역..
    4. 여기서는 안전하게 휴면할 수 있으며, 휴면할 경우 락이 자동적으로 해제된다. 또 휴면했다가 다시 스케줄링되면 자동적으로 락을 얻는다.
    5. 즉, 어느 경우이든 데드락은 피할 수 있지만, 정말로 여기서 데이터를 보호할 생각이라면 휴면하지 말아야 한다.!!!!
    6. */
    7. unlock_kernel();
  9. seq 락

    • 공유 데이터를 읽거나 쓰기 위한 아주 간단한 메커니즘을 제공.
    • 많은 Reader와 적은 Writer가 있는 경우를 위한 매우 가볍고 확장성이 좋은 락이다.( Writer를 선호, 다른 Writer가 없을경우 항상 락을 잠글 수 있다. )
  10. 선점의 비활성화

    • 커널이 선점형이므로 새로 실행되는 태스크가 선점된 태스크와 동일한 위험구역에 진입할 수 있다 .그러므로 커널 코드는 스핀락을 사용하여 선점 불가능한 코드 구역을 표시해야 한다.
    • 하나의 변수가 프로세서에 고유한 변수라면 락이 필요치 않게 되지만 프로세서간 선점으로 인해 유사 동시적으로 접근될 수 있다.

      1. preempt_disable();
      2. /* 선점이 비활성화됨 ..*/
      3. preempt_enable();
      • 커널 선점을 비활성하여 한다.
  11. 오더링과 배리어

    • 프로그램 코드에 정의된 순서대로 행해야 하는 경우가 있다.

      • 하드웨어를 가지고 작업할 때는 어떤 읽기 작업이 다른 읽기나 쓰기 작업 이전에 행해져야 한다는 식의 요구조건.
    • 컴파일러와 프로세서 모두가 최적화를 위해 읽기/쓰기(인텔x86예외) 명령의 순서를 마음대로 뒤바꿀수 있기 때문에 별도의 방법이 존재한다.

      • 읽기와 쓰기 명령의 순서를 바꾸는 프로세서들은 명령의 순서 요구조건을 만족시킬 수 있는 기계어 명령을 제공한다.
      • 컴파일러에게 특정 지점에서는 명령어의 순서를 바꾸지 말도록 지시할 수도 있다. 배리어(Barrier) !!
    1. a = 1;
    2. b = 2;

      // 프로세서는 a보다 먼저 b에 새로운 값을 저장할 수도 있다. 컴파일러와 프로세서는 a와 b 사이에 어떠한 관련성이 있음을 알 수 없다.

    3. // a와 b사이에 명백한 연관이 존재하지 않기 때문에 재배열이 발생한다.
    4. a = 1;
    5. b = a;
    6. // a와 b사이에 명확한 데이터 의존성이 존재하기 때문에 재배열하지 않는다. a와 b사이에 명확한 데이터 의존성이 존재.
    • barrier() 함수를 통해 컴파일러가 로드와 스토어 명령을 최적화하는 것을 방지 할 수도 있다.
  12. 요약

    1. 원자적 연산 : 동기화를 담보하는 가장 단순한 방법.
    2. 스핀락 : 해당 락에 오직 하나의 보유자만이 존재할 수 있는 가벼운 락으로 경쟁이 발생하면 바쁜 대기를 수행한다.
    3. 휴면락 : 세마포어와 완료변수, seq락
    4. 경쟁상태를 유발하지 않으면서 훌륭한 동기화를 보장할 수 있는 커널코드를 작성하자!! 심플하게~!

      출처 : http://dblab.co.kr/category/Study/System?page=1 

'Embedded Lab > linux, x86' 카테고리의 다른 글

[VFS]  (0) 2012.02.15
[시너지]  (0) 2012.01.26
[8장 커널 동기화 개요]  (0) 2012.01.26
[Top-Half와 Bottom-Half]  (0) 2012.01.26
[인라인(inline) 함수]  (0) 2012.01.11
Posted by cyj4369
,
  1. 커널 동기화 ?

    • 메모리를 공유하는 응용 프로그램은 항상 동시적인 접근으로부터 공유된 자원을 보호해야 한다.
    • 다수의 실행중인 스레드가 동시에 데이터를 변경할 경우 다른 스레드가 변경한 부분을 또 다른 스레드가 잘못 덮어써버릴 가능성이 있다.
    • 멀티프로세싱, 선점형 스케줄링 환경에서는 다양한 동시성 문제가 발생할 수 있다.


  2. Critical Region(위험구역) and Race Condition (경쟁 상태)

    • 위험구역이란 공유된 데이터를 접근하여 조작하는 코드 부분을 가리킨다. 원자적 실행을 보장해야 한다.
    • 경쟁상태란 스레드들이 위험구역에 들어가기 위해 경쟁하는 상태.


  3. 락킹(Locking)

    • 한번에 오직 하나의 스레드만이 공유하는 자료구조를 조작할 수 있다는 것을 확실할 방법.
    • 특별한 구역에서 다른 스레드가 동작하고 있을 때 해당 자료구조를 접근하지 못하게 하는 방법.
    • 락은 동시성을 방지하여 경쟁상태로부터 큐를 보호할 수 있다.

      1. 스레드는 방에 들어서면서 방문을 잠근다. 공유 데이터의 사용이 끝나면 스레드는 자물쇠를 열고 방을 떠난다.
      2. 만약 다른 스레드가 방문에 도착했을 때 문이 잠겨져 있다면, 안에 있는 스레드가 문을 열고 나올 때까지 기다린 후에 방에 들어가게 된다.
      3. 스레드는 락을 잠그고, 락은 데이터를 보호한다.


  4. 동시성

    • 프로그램들이 선점되면서 스케줄링 된다는 사실에서 비롯된다. 어떤 프로세스가 위험지역에 있을 때 비자발적으로 선점돼 버리는 일이 발생할 수 있다.

      • 유사동시성(peudo-concurrency) - 실제로는 동시에 발생하지 않지만 서로 엇갈려서 실행돼 마치 동시에 실행되는 것과 같은 효과가 발생.
      • 진정한동시성(true-concurrency) - 대칭형 멀티프로세싱 시스템에서 두 프로세스는 정확히 가튼 시간에 하나의 위험지역에 진입 할 수 있다.
    • 커널에서의 동시성의 원인

      1. 인터럽트 - 비동기적으로 어느때나 발생하여 현재 실행중인 코드를 중단시킨다.
      2. softirq와 태스크릿 - 커널은 현재 동작중인 코드를 중단하기 위해 거의 언제라도 softirq나 태스크릿을 레이즈하거나 스케줄할 수 있다.
      3. 커널 선점 - 커널도 선점형이므로, 커널에 있는 한 태스크가 다른 태스크를 선점할 수 있다.
      4. 유저공간에서의 휴면과 동기화 - 커널의 태스크는 휴면할 수 있으며 따라서 스케줄러가 새로운 프로세스를 실행하게 된다.
      5. 대칭형 멀티프로세싱 - 2개 이상의 프로세서가 코드를 동시에 실행할 수 있다.
    • 동시성으로 인해 문제가 발생되는 경우 (락이 없다면..) : 경쟁상태가 발생

      1. 커널에서 어떤 자원을 조작하고 있는 도중에 인터럽트가 발생하여 같은 자원에 접근하는 경우.
      2. 커널이 공유 자원을 사용하고 있는 동안 커널 코드가 선점되는 경우.
      3. 위험지역을 실행하는 도중 커널 코드가 휴면하는 경우.
      4. 두 프로세서가 동시에 공유데이터에 접근하는 경우.


  5. 보호영역의 결정

    • 동시에 접근될 수 있는 모든 코드는 보호가 필요로 하므로, 사실 보호가 필요없는 코드를 먼저 찾아내는 법도 있다.

      1. 특정스레드에 로컬한 데이터가 있을 경우.( 로컬자동변수, 스택에서만 존재하는 자료구조들.. )
      2. 특정 태스크에 의해서만 사용되는 데이터.
    • 보호영역의 선정 ( 커널의 거의 모든 전역 데이터와 공유 데이터는 어떠한 형식으로든 동기화. )

      1. 데이터가 전역적인가 ? 다른 스레드가 이 데이터에 접근 가능한가?
      2. 데이터가 프로세스 컨텍스트와 인터럽트 컨텍스트간에 공유되는가?
      3. 데이터를 사용하는 도중 프로세스가 선점되는 경우, 새로 스케줄된 프로세스가 같은 데이터에 접근하는가?
      4. 현재 프로세스가 휴면(or 블록)하는 경우가 있는가? 만약 그렇다면 휴면(블록)시 공유 데이터가 어떤 상태에 놓이게 되는가?
      5. 다른 곳에서 이 데이터가 해제되지 않으려면 어떻게 해야 하는가?
      6. 이 함수가 다른 프로세서에서 호출되면 어떤 일이 벌어지는가?


  6. 데드락(Deadlock)

    • 어떠한 스레드도 더 이상 진행할 수 없는 상태

      • 각 스레드가 어떤 자원을 얻으려 대기하지만 이미 모든 자원의 락이 잡혀있을 때 발생한다. 모든 스레드가 서로에 대해 대기하므로 이미 잡고 있는 자원을 해제할 수도 없다.
    • 셀프 데드락(self-deadlock) 이란 어떤 스레드가 자기 자신이 이미 잡고 있는 락을 다시 얻으려 시도할려고 할때 발생.
    • 죽음의 포옹(deadly embrace) or ABBA 데드락 이란 2개의 스레와 2개의 락이 있는 경우 발생.

      • 스레드1이 락 A를 얻음 / 스레드2가 락 B를 얻음
      • 스레드1이 락 B를 얻으려 함 / 스레드2가 락 A를 얻으려 함
      • 스레드1이 락 B에 대해 대기 / 스레드2가 락 A에 대해 대기
    • 데드락 방지

      1. 중첩된 락은 반드시 같은 순서로 잠겨져야 한다. 죽음의 포옹을 방지.

        • 서로 다른 구조체를 보호하는 cat, dog, fox 라는 3개의 락이 있을 때 어떤 함수가 cat->dog->fox 순서로 접근을 하였다.
        • 다른 모든 함수 역시 같은 순서로 락들을 잠가야 한다.
      2. 기아현상(starvation)을 방지해야 한다. 코드가 종료하는가를 되짚어 봐야 한다. 어떠한 코드도 무한대기가 일어나서는 안된다.!

        • 락이 풀리기를 대기중인 스레드들은 적당한!! 시간에 락을 얻을 수 있어야만 한다.
      3. 같은 락을 두 번 잠그지 않아야 한다.( 리눅스에서는 재귀적 락을 제공하지 하는다. )
      4. 락은 최대한 단순하게 설계해야 한다.


  7. 경쟁과 확장성

    • 현재 사용되고 있는 어떤 락을 다른 스레드가 얻으려 하는 상황을 락경쟁(Lock Contention) 혹은 경쟁이라고 한다.

      • 경쟁이 심한 락일수록 시스템 성능 저하에 더 큰 영향을 미친다.
    • 확장성이란 어떤 시스템이 얼마나 잘 확장될 수 있는가를 가리킨다.
    • 락킹의 세세함(granularity) 이란 그 락이 보호하는 데이터의 크기를 고려하는 것을 의미한다.
    • 정확하지않은(coarse)락은 많은 양의 데이터, 전체 서브시스템의 자료구조를 보호하는 경우이다.

      • 락경쟁이 심할 경우 확장성의 저하로 이어진다.
    • 매우 촘촘한(fine grained)락은 매우 적은 양의 데이터, 큰 구조체에 속한 하나의 항목만을 보호한다.

      • 충분한 락 경쟁이 없는 경우 소모적인 부하로서 작용한다. 


        출처 : http://dblab.co.kr/category/Study/System?page=1
          

'Embedded Lab > linux, x86' 카테고리의 다른 글

[시너지]  (0) 2012.01.26
[9장 커널 동기화 방법]  (0) 2012.01.26
[Top-Half와 Bottom-Half]  (0) 2012.01.26
[인라인(inline) 함수]  (0) 2012.01.11
[커널의 종류]  (0) 2012.01.11
Posted by cyj4369
,