rm -r 폴더이름 : 해당폴더와 그 밑에 하위 폴더 모두 삭제

Posted by cyj4369
,

0. 서문

현재 Linux가 돌고 있는 시스템 중의 대부분은 Intel IA32 CPU이다. Linux는 Intel 80386부터 시작하여 80486, Pentium 계열의 CPU에서 실행이 된다. 운영체제의 기능을 구현하려면 CPU의 지원을 필요로 하는데, Linux는 80386부터 등장한 32-bit 보호모드(protected mode)의 지원을 이용하여, 메모리 관리, 프로세스 관리 등을 하고 있다. 여기서는 Linux를 구현하기 위해 필요한 보호모드의 기능들을 간단히 살펴보도록 한다. 
  

1. 실제모드 

실제모드는 x86 계열로 처음 등장한 Intel 8086 CPU와 같은 동작 모드를 말하는 것으로, 16 bit CPU인 8086, 80286에서뿐만 아니라, 80386 이후의 모든 32-bit 프로세서에서도 이를 지원한다. x86 계열의 모든 CPU는 처음 시작할 때는 실제모드로 동작한다. 

실제모드에서는 20 bit address bus를 사용하여 총 1MB의 메모리를 사용할 수 있으며, 16 bit register를 사용한다. 레지스터의 크기가 16 bit이기 때문에, 20 bit 주소를 나타내기 위해 segment register라는 것을 도입하였다. 이는 16 bit segment register와 16 bit offset을 중첩시켜서 20 bit의 주소를 만들어내는 것이다. 모든 메모리 접근에는 segment와 offset이 같이 필요하며, 하나의 segment를 사용하면 64K(0x10000)만큼의 메모리를 사용할 수 있다. 실제모드에서는 가상 메모리라는 개념이 존재하지 않으며, segment와 offset으로 만들어지는 주소는 바로 물리적인 메모리 주소이다. 또한 이 모드에서 동작하는 모든 프로그램은 메모리의 어떤 영역이든지 맘대로 접근할 수 있으며, cli (clear interrupt), sti (set interrupt)같은 명령어를 포함하여 실제모드에서 사용할 수 있는 모든 명령어들을 모두 사용할 수 있다. 

IBM-PC는 8086 CPU를 이용하여 만들어졌는데, 이것이 처음 등장하던 당시엔 1MB의 메모리는 상당히 큰 것이었다. 그래서 IBM은 앞의 640KB(0 - 0x9ffff)만을 프로그램이 사용할 수 있게 하고, 나머지 384KB(0xa0000 - 0xfffff)는 BIOS와 ISA 장치용으로 사용하게 하였는데, 이는 실제모드로 동작하는 각종 프로그램들이 640KB의 메모리만을 사용할 수 밖에 없는 제약을 만들었다. 일반적으로 리눅스 부팅을 할 때 사용되는 LILO도 실제모드로 시작하여 리눅스 커널을 로드하기 때문에, 마찬가지로 640KB의 제약을 받게 된다. 

그림 1-1. 실제모드에서의 메모리 변환과정 
 
그림 1-2. 실제모드에서의 메모리 계산방법



2. 보호모드(Protected Mode) 

Intel 80286부터 처음 도입된 보호모드는 80386에 이르러서 완성된 모습을 보여 지금에 이르게 된다. 80286의 보호모드를 간단히 살펴보면, 우선 24 bit address bus를 사용하여 총 16MB(0x1000000)의 메모리를 사용할 수 있게 하였다. 

segment register는 selector라는 명칭으로 바뀌었고, descriptor table이라는 것을 통하여 16 bit segment를 24 bit base address로 바꾸게 하였다. 이 base address에 offset을 더함으로써 모두 16MB의 메모리를 사용할 수 있었지만, 80286 역시 16 bit CPU였기 때문에 각 segment의 크기는 여전히 64KB의 제한을 가지게 되었다. 

이런 제한을 없애고 완전한 32 bit address space와 32 bit register set을 제공하는 80386이 등장하였고, 이후에 운영체제들은 80386에서 제공하는 보호모드의 기능을 활용하게 되었다. 보호모드에서는 CPU가 제공하는 모든 기능과 명령어들을 활용할 수 있다. 보호모드에서는 privilege level이라는 것이 등장하는데, 이는 프로세서를 활용할 수 있는 권한정도를 말한다. 이는 0부터 3까지 있는데, 0이 모든 일을 할 수 있는 모드로 일반적인 운영체제 구현에서 커널모드가 이 상태이며, 3은 응용프로그램처럼 사용자 권한의 실행상태를 나타낸다. 이 privilege level을 통하여 커널모드/사용자모드를 구현하게 된다. 보호모드에서는 32 bit address bus를 통하여 4GB의 메모리를 사용할 수 있으며, 메모리 보호기능과, 페이징(paging) 메커니즘 등을 통해 가상 메모리를 효율적으로 구현할 수 있다. 인터럽트나 예외처리, task switching 등도 모두 보호모드에서 지원하는 기능을 활용한다. 

그림 2-1. 보호모드 레지스터와 자료구조


3. Intel 80386 Registers
 

  • 일반 목적의 data register (32 bits) :
    • EAX, EBX, ECX, EDX
    • ESI (source pointer), EDI (destination pointer)
    • ESP (stack pointer), EBP (base pointer)
  • Segment Register / Selector (16 bits) :
    • CS (code segment), DS (data segment), SS (stack segment)
    • ES, FS, GS
  • Flag Register (32 bits) : EFLAGS
  • Instruction pointer : EIP
  • Control Register : 시스템의 동작들을 제어하기 위한 레지스터
    • CR0 : 프로세서의 상태와 동작모드를 제어하는 여러가지 제어 flag를 가지고 있다. 대표적인 flag로는 PG (Paging. paging 사용여부 설정), PE (Protection Enable, 보호모드를 사용하는지 여부)가 있다.
    • CR1 : reserved
    • CR2 : page fault가 발생하였을 때 이것이 발생한 linear address를 가지고 있다.
    • CR3 : page directory가 시작하는 physical address를 가지고 있다.
    • CR4 : 아키텍쳐별로 확장한 여러가지 flag들을 가지고 있다.
  • Descriptor Table Register :
    • GDTR (Global Descriptor Table Register) : GDT의 위치를 가리키는 register. LGDT (Load GDT), SGDT (Store GDT) 명령으로 참조하고 설정한다.
    • LDTR (Local Descriptor Table Register) : GDT안에 LDT descriptor를 가리키는 selector. LLDT (Load LDT), SLDT (Store LDT) 명령으로 참조하고 설정한다. 내부적으로 segment의 base address와 segment limit도 가지고 있지만 외부에서는 selector 값만을 참조할 수 있다.
    • IDTR (Interrupt Descriptor Table Register) : IDT의 위치를 가리키는 register LIDT (Load IDT), SIDT (Store IDT) 명령으로 참조하고 설정한다.
  • Task Register : TR
      TSS (Task State Segment)가 있는 위치를 가리키는 GDT 내의 TSS descriptor를 가리키는 selector. 내부적으로 segment의 base address와 segment limit도 가지고 있지만 외부에서는 selector 값만을 참조할 수 있다. LTR (Load TR), STR (Save TR) 명령으로 참조하고 설정한다.
  • Test Register :
    • TR1 : test parity check
    • TR2, TR3, TR4, TR5 : cache test register
    • TR6, TR7 : TLB (Translation Look-aside Buffers) test registers
    • TR9, TR10, TR11 : BTB (Branch Target Buffers) test registers
    • TR12 : new feature control
  • Debug Register :
    • DR0, DR1, DR2, DR3 : debug address register. breakpoint의 linear address를 가지고 있다.
    • DR4, DR5 : reserved
    • DR6 : debug status register
    • DR7 : debug control register
  • Model Specific Registers (MSRs) : 

     

4. 보호모드에서의 메모리 관리 

보호모드에서는 segmentation과 paging 메커니즘을 이용하여 메모리를 관리한다. segmentation은 4GB의 메모리를 segment라는 단위로 쪼개는 것을 말한다. 여기서는 16 bit의 selector와 32 bit의 offset을 이용하여 4GB 범위안에 있는 32 bit의 선형주소(linear address)를 만드는 일을 한다. 이렇게 만들어진 선형주소가 물리주소(physical address)가 되는 것이 아니라 메모리를 4KB 단위로 쪼개서 관리하는 paging mechanism을 거쳐서 물리주소로 변환된다. (control register CR0의 PG flag를 수정함으로써 paging 메커니즘을 사용하지 않을 수도 있다) 

그림 4-1. 보호모드에서의 메모리 변환과정 
 
segment는 segment descriptor라는 것으로 정의가 된다. 

여기에는 segment의 base address와 segment limit (크기), 그리고 DPL (descriptor privilege level)을 비롯한 정보가 들어간다. (여기에 보면 segment type이라는 것이 있다. 이것은 이 descriptor가 나타내는 것이 무엇인지를 말하는 것이다. segment는 일반적인 code/data/stack일 수도 있고, 뒤에 나오는 TSS 일수도, gate일 수도 있는데 이 type을 가지고 무엇을 가리키는 descriptor인지 구별할 수 있으며, 이에 따라 descriptor의 구조가 달라진다.) 

segment descriptor table은 이들 segment descriptor를 모아두고 있는 것을 말하는데, 여기에는 시스템 전체에 대한 descriptor table인 GDT (global descriptor table)과 프로세스마다 개별적으로 정의하고 있는 LDT (local descriptor table)이 있다. 이들은 모두 메모리 상에 존재하게 되는데, GDT의 위치는 GDTR (GDT register)가 가리키고 있다. LDT의 위치는 LDTR (LDT register)가 가리키고 있는데 이 LDTR은 원래 GDT에 있는 한 segment descriptor를 가리키고 있는 selector이다. 즉 GDT에는 시스템에 있는 모든 LDT에 대한 segment descriptor가 들어있으며, LDTR은 이 중 현재 프로세스의 LDT에 대한 segment descriptor를 가리키는 selector이다. 

16 bit인 selector에는 이 descriptor table에서의 index 값과, 이것이 이것이 GDT에서의 index인지, LDT에서의 index인지를 나타내는 TI (table indicator), 권한을 나타내는 RPL (requestor privilege level)이 들어있다. 이 TI와 index를 가지고 해당하는 table에서 segment descriptor를 찾아서 base address를 구하게 된다. 이 값에 offset을 합하면 지정한 selector와 offset에 해당하는 linear address가 만들어지게 된다. 


그림 4-2. Selector

그림 4-3. Segment Descriptor

그림 4-4. descriptor table 참조

그림 4-5. 가상주소에서 선형주소로의 변환

이렇게 segmentation을 거쳐 나온 linear address는 paging 메커니즘을 통하여 physical address로 변환이 된다. 

paging은 요즘의 운영체제에서 모두 구현하고 있는 paging을 지원하기 위한 것이다. 이를 이용하여 실제로 하드웨어적으로 있는 메모리보다 많은 메모리를 사용할 수 있으며, 메모리를 효율적으로 사용할 수 있으며, swapping을 쉽게 구현할 수 있게 된다. 메모리는 4KB 단위의 page로 쪼개지며, 이들은 page directory와 page table로 체계화된다. 

각 page들의 시작 위치는 page table entry에 기록이 되며, 각 page table들의 위치는 page directory entry에 들어있다. page directory의 시작 위치는 control register중의 하나인 CR3이 가지고 있다. 앞에서 넘어온 linear address는 page directory에서의 index, page table에서의 index, offset 세가지로 쪼개지며, 이들 table들을 차례로 따라가서 실제 page의 주소를 얻고, 여기에 offset을 더함으로써 실제 physical address가 나오게 된다. 

그림 4-6. 선형주소에서 물리주소로의 변환 
 

이렇게 가상주소가 물리주소로 변환되는 과정을 정리하면 다음과 같다. 


그림 4-7. 가상주소에서 물리주소로의 변환


5. 보호모드에서의 태스크 관리
 

보호모드에서는 각 task별로 TSS (Task State Segment)라는 것을 관리한다. 여기에는 하나의 task의 상태 - 일반 register, segment register, flag, EIP, stack segment selector, stack pointer, LDT selector, page directory base address 등 - 가 저장이 되며, task switching이 일어날 때 이전의 상태를 자동으로 여기에 저장을 하며, 새로운 TSS에 있는 상태가 현재 상태로 복구가 된다. task register인 TR은 현재 task의 TSS를 가리키는 selector이다. 각 TSS는 GDT에서 TSS descriptor로 기술되며, TR은 이 중에서 현재 TSS의 descriptor를 가리키는 것이다. 


그림 5-1. Task Register


task switching을 하는 방법으로 우선 call이나 jmp 명령에 전환할 TSS selector를 지정하는 것이 있다. 이렇게 하면 프로세서는 현재 상태를 현재 TSS에 저장을 하고, 새로운 task의 TSS로 상태를 모두 바꾼후, 새로운 task를 실행한다. 

task gate는 task switching을 일으키는 특별한 descriptor로서 LDT나 IDT (Interrupt Descriptor Table)에 들어가는 descriptor이다. 여기에는 TSS descriptor에 대한 selector가 들어있다. task gate는 interrupt나 trap이 발생하였을 때 task switching이 일어나게 하는 것처럼 간접적으로 task switching이 일어나게 하거나, 특정한 task들이 TSS에 접근할 수 있도록 하기 위해서 사용한다. 앞의 경우에서처럼 call이나 jmp에 task gate의 selector를 지정을 해주면 여기서 가리키는 TSS로 task switching이 일어난다. 또한 task gate가 IDT에 있을 때 해당하는 interrupt가 발생하면 이 task gate를 통하여 해당하는 TSS로 task switching이 일어나게 된다. 


그림 5-1. Task Gate


6. 보호모드에서의 인터럽트/예외 처리
 

인터럽트나 예외가 발생하면 어떤 것이 발생하였는지 식별하는 번호가 나오게 된다. (인터럽트는 CPU의 인터럽트 핀에 의하여 발생하며, 예외는 프로세서가 작업을 하는 중에 잘못된 일을 발견하거나 - 여기에는 fault, trap, abort가 있다 -, 프로그래밍으로 들어가 있는 코드에 의하여 - INT 3, INT n, BOUND 등 - 에 의하여 발생하는 것이다) 이 번호를 vector라고 부르는데 이를 index 삼아, IDT (interrupt descriptor table)에 있는 descriptor를 찾아서 이를 처리하게 된다. IDT는 interrupt 처리에 관련된 descriptor들을 모아둔 것으로 이의 위치는 IDTR (IDT register)가 가지고 있다. 

여기에 들어가는 descriptor로는 task gate, interrupt gate, trap gate가 있다. task gate는 앞에서 이야기한 것처럼 TSS selector를 가지고 있는 gate로서, 해당하는 TSS로 task switching이 일어나게 된다. interrupt gate와 trap gate는 GDT/LDT에 있는 segment descriptor에 대한 selector와 offset을 가지고 있는 gate로서, selector를 가지고 segment의 시작주소를 얻고, 여기에 offset을 더하여 이를 처리할 handler의 주소를 얻게 된다. 그리고 이 handler의 위치로 건너가서 해당하는 처리를 한 후 이를 마친후에 복귀하게 된다. 이 과정에서 privilege level이 바뀔 수도 있으며, 처리 중에는 현재 TSS에 있는 stack을 그대로 사용한다. interrupt gate와 trap gate를 통하는 경우의 차이는, interrupt gate를 통하는 경우 처리하는 동안 interrupt를 금지하고 (flag의 IF를 clear) 처리를 마치고 복귀할 때 다시 이를 복구하지만, trap gate를 통하는 경우에는 interrupt를 금지하지 않는다. 


그림 6-1. Interrupt Gate와 Trap Gate

그림 6-2. Interrupt 발생시 Task Gate


7. 보호모드에서의 시스템콜
 

시스템콜은 사용자모드에서 커널모드에서 실행되는 코드를 부르기 위한 방법이다. 이를 위해서 위해서 보호모드에서는 call gate라는 것을 제공한다. call gate는 다른 privilege level 사이로 제어권을 넘기는 방법으로, segment selector와 offset을 가지고 있다. 이 selector는 GDT/LDT에 있는 segment descriptor를 가리키고, 여기서 얻어지는 base address에 offset을 더하여 실행할 함수의 위치를 얻게 된다. call이나 jmp 명령에 목적하는 call gate의 segment selector를 지정함으로써 call gate를 통하여 커널모드로 진입하게 된다. 

이 때 CPL (current privilege level)과 call gate descriptor의 DPL, call gate selector의 RPL을 비교하여 권한을 검사한다. 시스템콜에 진입하였을 때에는 현재 task의 TSS에 있는 stack을 사용하며, ret 명령을 통해서 결과값과 함께 시스템콜을 부르기 이전의 상태로 되돌아가게 된다.

그림 7-1. Call Gate



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

[vi편집기 자동정렬]  (0) 2012.05.01
[rm, rndir 옵션]  (0) 2012.04.11
[TSS(Task State Segment)]  (0) 2012.04.04
[extern 키워드]  (0) 2012.04.04
[cp 명령어옵션]  (0) 2012.04.03
Posted by cyj4369
,

우선 예전 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
,