vi에서 설정이 부족해서(?) backspace 키나 화살표키 등이 안먹힌다.


그래서 ~/.exrc 파일에 몇가지 설정을 해주면 잘 동작한다.

(걍 vim 설치해서 쓰면되긴 한다.)



그렇다면 저 파일은 뭘까해서 찾아봤당.


vi 사용시, user의 home 디렉터리에서 이 파일을 찾아서 설정을 반영한다.

indentation, tab space 설정 같은것 들이다.

대게 이런 설정은 파일이 열려있을 때 까지만 유효하기 때문에 .exrc라는 파일에 설정을 Group화 할 수 있다.



vim 설정파일인 .vimrc 같은 것 이다.




암튼 저 상황에 대한 해결책은


$ cd ~

$ vi .exrc


set autoindent

set number

set bs=2

set nocp



$ source .exrc         // 적용



'System > Linux OS ' 카테고리의 다른 글

리눅스 환경변수와 UID, PID, 시스템자원의 제한  (0) 2015.12.24
lsof  (0) 2015.12.24
strace, ltrace  (0) 2015.12.24
gcc / gdb  (0) 2015.12.24
man  (0) 2015.12.24



이전

__turn_mmu_on, __turn_mmu_on_end영역만 page table에 매핑









__create_page_table  계속...


커널 코드 시작부터 .bss 섹션의 끝까지 메모리를 매핑한다.

page table의 시작위치는 0x4000 (r4)




* x >> (20-2)

 

1) 

x >> 18

x / 2^18

x / (2^20 x 2^2)


2)

x >> 20       ; x/1M

x << 2         ; x*4



그 후, BSS의 마지막 부분을 매핑한다.

_end(.bss section의 끝) 0x8092378C -1 상수값을 r6에 넣는다.

(참고. https://github.com/iamroot12D/linux/issues/31)


cf)

arm명령어 체계에서는 상수를 맨 오른쪽 비트 1개만 사용하여 표현하기때문에

0~256까지만 표현할 수 있기때문에, 상수자체가 256이 넘는 것은 계산할 수가 없다.

즉, mov r0, 270 은 안된다.


하지만, 

mov r0, 0xlf00000000

=> 상수 0xlf에, shift bit에 24가 들어가서 계산이 된다.


즉, 소스에서 상수를 로드할 때, mov가 아닌 ldr을 사용하는 이유이다.



여기까지 우리가 하는 일을 잠시 다시 생각해보자.








메모리 매핑 현재 과정




다시 소스 !!


r6 : .bss의 끝(.end) 0x8092378b

r7 : mmu_flags 값

r8 : 물리메모리의 시작, 0x0 (커널의 베이스 어드레스)


(cf. bss영역이 9.X M정도여서, 약 10M 라고 가정하에... 그림그림.)

=> 찾아보니, 정확히 한 9.2M정도 이넹~~ 





=> 즉, 

r6 = 0x809   => 0x2024

즉, 0x6000~0x6024까지 커널의 .bss섹션까지의 영역과 매핑한다.


....









.bss위에 DTB가 있었는데, 이것도 옮긴다.

r3 = 6028    ; DTB의 영역을 매핑할 주소를 구함

가상메모리에서 6028와 매핑되어있는 물리주소 


2번 수행해서 2M영역을 페이지 테이블에 맵핑하는 이유는, 

DTB의 최대 사이즈가 1M 일 수 있다. 우리의 페이지 엔트리는 1M 단위로 하는데, .bss까지의 .end가 9.2정도 이므로, 10M 영역을 넘을 수 있기때문에,

영역이 겹쳐버릴 수 있다. 그래서 2번 수행해서 총 2MB을 추가로 확보하는 것이다.


* addruart  :  UART 장치의 선두 주소를 얻어옴. 레지스터에 시리얼 포트 주소 값을 설정)

kconfig에서 값을 읽어와서 레지스터들을 로드

addruart r7, r3, r0      ; r7 물리주소 0x20201000, r3 가상주소 0xf0201000
r7 = io의 mmuflag값, 0xc02

0xc02 => c는 앞에비트니까 생각하지말고, 02는 0010으로

c                    b

 ...

 ...

 0

 0

 1

 0



r3 = 0xf0201000를 20번 right 시프트하면 5개자리가 떨어져나가서 0xf02(물리주소)이고, 

r3 = 이걸 두번 다시 left shift하면 0xf02 * 4

r3 = (0xf02 * 4) + 0x4000 = 0x202

r7 = 0x202 = 0xc02

r0 = 0x7c08

r3 = 0x2020000+0x00000c02

r3 = 20200c02

여기에 xn비트 적용하면, r3 = 0x20200c12  


* xn비트(execute never)가 서있으면, https://en.wikipedia.org/wiki/NX_bit


이걸 페이지 테이블에 매핑하면 가상주소의 인덱스가 7c08인 값이 20200c12



참고.

http://kth3321.blogspot.kr/2013/04/arm-cortex-1.html




디버깅에 필요한 잡업들을 위해 매핑까지 수정하면,



ret lr 

(리턴  lr!! creatfe page끝 )





<결론>


* 커널이 사용할 2GB : PAGE_OFFSET 값으로


* 가상주소를 물리주소로 변환하는 방법

은 right shift 20, left shift 2 



이전시간에 turn_mmu_on 을 가상 메모리 공간에 0M부분에도 매핑하는데, 오늘은 커널부분을 커널영역부분에 맵핑했다

결국 turn_mmu_on이 매핑되는 부분은 2군데가 되는데

이는 turn_mmu_on을 하기 전까지 우리는 커널영역의 주소공간을 사용한다.  

즉, 가상메모리 주소공간이 0x80000000 공간을 사용하지 않고 0x0~~ 주소체계를 사용하고 있기때문에 바로 주소가 변환하는 매커니즘을 거치면 오류가 발생하기 때문에, 

이를 방지하기 위해 따로 매핑을 해놓고 사용한다.









* head.S에서 지금까지한게 SMP랑 __create_page_table









[initfn 매크로 부분]


r10 :  proc.info 구조체,  (라즈베리파이의 CPU의 proc.info)


(실행주소가 0x8000베이스로 실행되는 중)


--v7proc_info는 파일에 들어있는 값이고 0x80008200이런식으로 되어있었다. (지난번에 했음)

v7proc_info 들어가면, __lookup_processor_type레이블이 있고, __lookup_processor_type_data를 타고 0x8080이 들어갔다고 치고,  (실행주소가  0x8000베이스로 돌아가는 중, 런타임시)

__lookup_processor_type돌아와서 adr명령어를 통해서 r3 = 0x8080 로 들어간다. 

*adr명령어는 PC의 기준으로 현재 실행되고있는 메모리의 값을 읽어오는 거라서(런타임시), 0x8000베이스고,


(덤프확인 주소들이 https://github.com/iamroot12D/linux/issues/31)

 r3

 r4 

 r5 

 r6 

 0x8080

 "."은 현재 데이터이므로

0x80008080

(컴파일시점에서 소스에 박힘)

 

 

 (** arm 명령어체계)


2: .long 0x80008080 => r4

    .long 0x80000000 




initfn    --v7-setup, --v7proc_info    => 이렇게 빼주는게, 컴파일시에 읽어온 0x80000000 베이스의 주소를

 현재 런타임에 사용되는 베이스인 0x8000 형식으로 만들어주기위해 빼주는 과정임.

(initfn가 --v7-setup - --v7proc_info 하는 매크로임)



예를들어

initfn 80008300, 80008400




=> r12에는 -100이 들어감

r10 = 8400

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

이 둘을 add해서 r12에 저장, 즉 8400 + (-100) = 8300

결국 --v7_setup(80008300)인 컴파일시점의 주소를 현재 실행중인 베이스인 0x8000으로 바꾸는 역할을 하는 코드임.


결국 0x8000000을 0x8000베이스로 바꿔주기위해서 앞부분을 빼주는 역할을 함

=> 결국 이것도, 메모리에 있는 --v7_setup매크로를 호출하기 위해서 이렇게 한거.


자, 이제 v7_setup로 쩜프~!!







(아 왜 이렇게 짠거야...?에 대한 토론)


CISC와 다르게 RISC에서는 무조건 4바이트 명령어로 16비트 명령어 체계에서 2byte만 상수를 표현할수 있다. (0-256) 

그래서 한줄로 표현할수있는 것도, 복잡하게 여러줄로 나눠서 표현해야하기도 함.

위에서 봤던, ldr, mov명령이라던가...


CISC도 파이프라이닝을 위해서 SISC 처럼 명령어를 마이크로 명령어라는 단위로 잘라서 사용하기도 한다. 

이런 테크닉들이 많이 있다.



CISC는 명령어 길이가 가변적이므로, 설계도 복잡해지고 CPU성능에도 많은 가변적인 문제가 있다.








head.S




; 컴파일 시점에 이루어짐


r3 : r2번 레이블의 물리주소 0x8080

r4 : 2번 레이블의 가상주소 0x80008080

r8 : 커널 메모리 시작 offset. 0x80000000

r8 = r8 + r4 (0x8000000 + 0x8000000 = 0x0  -> overflow 발생으로 더한값이 0이 된다.)


#ifndef CONFIG_XIP_KERNEL

adr    r3, 2f

ldmia r3, {r4, r8}

sub    r4, r3, r4         ; r3-r4 = 0x8080 - 0x80008080   이상태에서 -플래그가 세워지지는 않음(명령어에s가 없으니까ㅋㅋ) 값은 0x8000000

add    r8, r8, r4        ; r8을 delta값처럼 쓴다? create page table에서 사용.... (r8의 용도는 무엇인가?!)

#else

ldr    r8, =PLAT_PHYS_OFFSET    ;  PLAT_PHYS_OFFSET이 0으로 설정되어있음

#endif



=> MMU를 설정하기 전이라 가상주소를 사용할 수 없기때문에, 이런 식으로 주소설정을 해준다. (이를 위해 base address를 0으로..?)

https://github.com/raspberrypi/linux/commit/72a20e22f49e2dad3180c23980a9df1c63faab0a




* __create_page_tables: 페이지테이블을 0으로 초기화하는 과정


- page_table 인자(rd, phys)로 넘어온걸 계산하는 매크로 

- r4에 페이지테이블의 시작 주소를 구한 후 루틴에 들어간다. 

(어떤 루틴인가~~~시작!)





* ###_LPAE ? 우리는 세팅이 안되어있어서 스킵됨


* 인라인 어셈블러

1. https://wiki.kldp.org/KoreanDoc/html/EmbeddedKernel-KLDP/app3.basic.html 

* clobber가 메모리 배리어

* =r 포인터

* I는 integer라서 커널소스에서보면, i가 쓰임


2. http://codecat.tistory.com/entry/%EC%9D%B8%EB%9D%BC%EC%9D%B8-%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC

3. http://stackoverflow.com/questions/25294649/how-does-this-inline-assembly-define-a-variable


스크립트로 C언어의 DEFINE을 쫙구현할때 main()에서 인라인어셈블러문법을 사용해서 쫘르륵 설정한다. 

     참고로 '->' 는 인라인어셈블러문법에서 자동으로 값들을 설정하기위해서 스크립트를 이용하는데 이때 상수로 쓰기위한 문법?

'sed script' => stream editor의 약자로 \n를 사용해서 시작한다.


ex. 리눅스 명령어로 사용할 때, aa.c라는 파일을 cc.c로 바꿀 때,

$ ls | sed 's/aa/cc/g'

     

참고2.  #define문법(C에서는 컴파일타임)을 안쓰고 offsetof을 사용해서 구현한 이유는 뭘까?

define할 값들을 스크립트를 통해서 동적으로 적용하기 위해서는 런타임 시점에 값을 변경해야 할 필요가 있음. 


참고1, 참고2 => 일종의 코드생성을 위해 sed를 일반적으로 사용하는데 이를 이용해서 헤더파일을 만들어서 define들을 만들어서 사용할라고~~




* ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] 0 mm_mmuflags


r7 = __lookup_process_type으로 proc_info를 가지고와서 r10에 저장해 놨었음


__lookup_process_type를 쭉 따라들어가면 PROC_INFO라고 define시키는 구문이 있음 (vmlinux의 섹션헤더 부분에 데이터들이 박히게된다.)

-> __lookup_process_type_data   -> ###_begin, ###_end 중간에 포인터들로 박힌다. 

=> /linux/arch/arm/mm/proc-v7.S를 확인해보면 소스 확인 가능함 ('proc.info.init'으로 검색)

=> .macro __v7_proc

=> ALT_SMP  => PMD_TYPE_SECT, ... 비트셋팅!

이렇게하면 비트들이 설정이 된다.

여기까지하면 플래그 값이  0x11c0e


...... 여기서 유니프로세스랑 멀티프로세스일때 어케 되는지(ALT_UP)

- 유니 : 0xc02

- 멀티 : 0xc0e




* adr r0, __turn_mmu_on_loc     ; mmu turn on 주소를 r0에 넣기



r0 = __turn_mmu_on_loc의 물리주소 0x8134

r3 = __turn_mmu_on_loc의 가상주소 0x80009134

r5 = __turn_mmu_on의 주소 0x80008240

r6 = __turn_mmu_on_end의 주소 0x80008260


... github  소스랑 주석 참고 





r4에 페이지테이블의 시작 주소를 구했었음. 



00100000

(base가 됨)






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


* XIP : 커널을 압축하지 않고 ROM FLASH에서 바로 실행되는 커널

* ARMSim - The ARM Simulator Dept. of computer science

* arm-bcm###

arm-linux-gnueabihf-objdump (cross compile)

(http://hybridego.net/entry/armlinuxgnueabihfgcc-%EC%99%80-armlinuxgnueabigcc-%EC%B0%A8%EC%9D%B4)





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

page offset

physical offset

text offset

Entry point address

Coherent, coherence 캐시 일관성 관련




리눅스는 유닉스 계열의 시스템이다. 유닉스 계열의 시스템에서는 모든 것을 파일로 취급한다고 한다. 
(1%는 아닌 것도 있다고 한 것 같기도.. 이는 추후에 찾아보기로 한다.)

아무튼 리눅스에서는 일반파일(regular file) 뿐 아니라, 모니터나 키보드, 마우스, 네트워크 인터페이스 카드(NIC) 같은 디바이스 들도 몽땅 파일(device file)로 취급하여 관리한다. 따라서 파일의 관리는 굉장히 중요한 부분을 차지한다.


OS에서 파일을 관리하는 관리하기 위해 File System이라는 개념을 사용하는데 Disk를 예로 들자면 대충 이런식이다.




파일 시스템은 보조기억장치를 관리하는 소프트웨어이다. 

궁극적인 목표는 자원을 최대한 아껴서 사용하는 것이며 이를위해 정책(policies)을 가지고 내/외부 단편화를 관리한다.


이는 MMU나 MPU와 같은 메모리 관리 시스템(Memory Management System)도 마찬가지인데 둘 다 기억장치를 관리하는 소프트웨어이지만 File System은 사용자가 관리를 수월하게 하기위해 "Naming"이라는 특성을 사용하여 파일단위로 추상화하여 관리하도록 한다는게 다른점이라고 한다.



또한 파일시스템을 아래 그림처럼 같이 하나의 tree 구조로 관리한다. 


# Linux Directory Hierarchy

출처. Learning The Linux File System (http://freedompenguin.com/articles/how-to/learning-the-linux-file-system/)



유닉스 계열에서는 mount namespace(#1')이 있어서 filesystem mount points의 sets별로 isolation할 수 있다. 다른 마운트 네임스페이스에 있는 프로세스들은 다른 파일시스템 계층 뷰를 가질 수 있다. 하드링크(hard link)/소프트링크(soft link) 기능이 가능한 이유인듯 하다. 리눅스에서는 프로세스별로 개별적인 마운트 뷰(view)를 가진다.



실제로 프로세스 관련 폴더인 /proc폴더에서 해당 프로세스의 폴더로 들어가면 namespaces들에 대한 내용을 비롯한 프로세스 별로 관리되는 정보들이 존재함을 확인할 수 있다. 상세한 내용은 man pages를 참고하자.



유닉스 파일시스템에서 모든 파일은 tree구조에 있기때문에 파일이나 폴더들은 각각 고유의 경로(path)로 표현된다. 예를들어 사용자 라이브러리 파일들은 /usr/lib/[파일]이라는 경로로 표현할 수 있다. 이때 경로를 이루는 항목인 "usr", "lib", "[파일명]" 각각을 디렉터리 엔트리(Directory Entry)라고하며 이를 묶어서 덴트리(Dentry)라고 한다.




이런식의 관리를 제공해주는 파일시스템의 디스크 할당 방식을 크게 두가지 방법으로 구분한다.

 연속 할당방식

 (sequential allocation)

 - 인접한 연속의 공간에 할당

 - 불연속 할당방식보다 속도가 빠름

 - 요구되는 크기 만큼의 인접한 공간이 존재해야함

 - 파일의 크기가 변하면 문제가 됨

 불연속 할당방식

 (non-sequectial allocation)

 - 인접하지 않은 공간에 할당

 - 해당 정보가 어디에 흩어져 있는지 위치정보를 따로 저장/관리해야함

 - 파일의 크기가 커져도 할당이 비교적 쉬움



일반적으로 불연속 할당방식이 사용되며 이는 다시 몇가지 방법이 존재한다.

 블록 체인 기법

 - 같은 파일의 정보를 저장하는 블록을 체인(포인터)로 연결

 - 원하는 정보를 검색할 때(lseek()) 처음부터 읽어가야 함

 인덱스 블록 기법 

 - 위치정보를 인덱스 블록이라는 별도의 공간에 블록을 두고 관리

 - 파일마다 인덱스 블록을 가짐

 - 검색 속도가 비교적 빠름(lseek())

 - 인덱스 블록을 유실하면 해당 파일을 사용할 수 없음

 - 파일의 수가 늘어날수록 인덱스 블록을 저장하는 공간이 많이 소비되게 됨

 FAT(File Allocation Table) 기법

 - 파일시스탬마다 위치정보를 FAT라는 자료구조에 저장

 - FAT 정보를 유실하면 손해가 크기때문에 중복으로 관리

 - 인덱스 블록 기법과 마찬가지로 처음부터 데이터를 읽어갈 필요가 없어 속도가 비교적 빠름(lseek())



하드디스크의 물리적 모습은 다음 그림과 같으며 이전에 정리한 것을 참고하도록 한다.

* Linux File System : http://jiming.tistory.com/120

(함께 참고. VFS : http://jiming.tistory.com/127)




(여기서 우선 알고넘어가야 할 점은 sector이다. sector는 데이터를 디스크에서 Read/Write하는 기본단위로 일반적으로는 512byte이다.)




리눅스의 디폴트 파일시스템인 ext4 등 ext계열의 파일시스템에서는 인덱스 블록 기법과 유사한 Inode라는 구조를 채택해서 사용하고 있다.


Inode 개념은 두가지가 있다.

하나는 디스크에 기록되어 저장된 파일들 각각이 가지고있는, 파일 자신에 대한 정보를 담는 구조체이다. 또 하나는 시스템에서 사용을 위해 "open()" 시스템콜(system call)로 오픈한 파일에 대한 정보를 나타내는 객체로 메모리에서 관리되어지는 개념이다.


여기에서 말하려는 Inode구조는 첫번째 개념으로 파일이나 디렉터리에 대한 모든 정보를 가지는 자료구조로 해당 파일시스템에서 사용하는 Inode구조체이다. 즉 파일시스템과 관련되어 있으므로 /fs 디렉터리 내에 해당 파일시스템에 관련하여 정의되어 있다. 다음은 github에서 클로닝한 현재기준 커널버전 4.5 프로젝트의 ext4 파일시스템에 정의된 inode 구조체이다.

(* 참고로 메모리에서 관리하는 inode객체는 /include/linux/fs.h에 정의되어 있으며 추후에 정리한다.)



예. /fs/ext4/ext4.h



 638     /*

 639     * Structure of an  on the disk

 640     */

 641     struct ext4_inode {

 642           __le16  i_mode;   /* File mode */

 643           __le16  i_uid;    /* Low 16 bits of Owner Uid */

 644           __le32  i_size_lo;  /* Size in bytes */

 645           __le32  i_atime;  /* Access time */

 646           __le32  i_ctime;  /* Inode Change time */

 647           __le32  i_mtime;  /* Modification time */

 648           __le32  i_dtime;  /* Deletion Time */

 649           __le16  i_gid;    /* Low 16 bits of Group Id */

 650           __le16  i_links_count;  /* Links count */

 651           __le32  i_blocks_lo;  /* Blocks count */

 652           __le32  i_flags;  /* File flags */

...

 664           __le32  i_block[EXT4_N_BLOCKS];/* Pointers to blocks */

 665           __le32  i_generation; /* File version (for NFS) */

 666           __le32  i_file_acl_lo;  /* File ACL */

 667           __le32  i_size_high;

 668           __le32  i_obso_faddr; /* Obsoleted fragment address */

...

 691           __le16  i_extra_isize;

 692           __le16  i_checksum_hi;  /* crc32c(uuid+inum+) BE */

 693           __le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */

 694           __le32  i_mtime_extra;  /* extra Modification time(nsec << 2 | epoch) */

 695           __le32  i_atime_extra;  /* extra Access time      (nsec << 2 | epoch) */

 696           __le32  i_crtime;       /* File Creation time */

...

 700     };


주석만 봐도 이 구조체에 저장되는 내용이 어떤것인지 알 수 있다. 


- i_blocks : 해당 파일이 가지고있는 데이터 블록의 갯수 저장

- i_mode : 해당 inode가 관리하는 파일의 속성 및 접근제어 정보 저장

- i_links_count : 해당 inode가 가리키는 파일 수(또는 링크 수)

- i_uid, i_gid : 파일을생성한 소유자의 id 및 group id

- i_atike, i_ctime, i_mtime : 파일의 접근시간, 생성시간, 수정시간

....



본디 파일시스템이 저장하는 정보에는 두가지가 있는데 바로 meta data와 user data이다.


- meta data : 파일의 속성정보, 데이터블록의 인덱스 정보 등

- user data : 사용자가 실제 저장하려는 데이터


위와같은 구조를 같는 Inode를 통해서 파일에 대한 모든 정보를 관리할 수 있다.



리눅스 기반의 운영체제에서 "$ ls -l"명령어를 실행하면 파일이나 디렉터리에 대해 다음과 같은 정보가 출력된다.


이러한 정보들이 해당 구조체의 정보를 사용해 출력되는 것이다. 

umask, chmod, chown, lsattr, chattr 등의 명령어를 통해 파일/디렉터리에 관련한 정보를 설정 및 수정 할 수 있다.



Inode구조체에는 File mode를 의미하는 i_mode라는 16bit짜리 변수가 있다.

이 변수는 해당 inode가 관리하는 파일의 속성 및 접근제어 정보를 저장하는 변수로 다음과 같은 구조를 가진다.




상위 4bit은 파일의 유형(type)을 나타내며 ls -l의 결과인 아래그림에서 빨간색으로 표시한 부분이다.


유닉스 기반의 파일시스템에서는 모든 디바이스를 파일로 다룬다고 하였다. 이 4bit의 타입 필드가 해당 파일이 어떤 유형의 파일인지 나타내는 역할을 한다. 종류는 다음과 같다.

 -

 정규파일

 S_IFREG

 d

 디렉터리

 S_IFDIR

 c

 문자장치

 S_IFCHR
 b

 블록장치

 S_IFBLK 

 l

 링크파일  S_IFLNK 

 p

 파이프파일

 S_IFFIFO 

 s

 소켓파일

 S_IFSOCK


그리고 하위 9개의 비트는 파일의 접근제어(사용자 / 그룹 / 기타사용자)를 의미하며 아래의 빨간부분을 의미한다.


그 다음 가운데 3비트는 각각 다음과 같으며, chmod 명령을 통해서 설정할 수 있다.

 u bit

 SETUID 

 4000

 Set User ID, 파일이 수행될 때 수행시킨 태스크의 사용자의 권한이 아닌, 파일의 소유자 권한으로 동작할 수 있게 함

 g bit

 SETGID

 2000

 Set Group ID, 파일이 수행될 때 수행시킨 태스크의 소유그룹 권한이 아닌, 파일의 소유그룹 권한으로 동작할 수 있게 함

 s bit

 Sticky Bit

 1000

 파일 및 디렉터리에 설정되는 접근권한을 나타내는 플래그 비트

 태스크가 메모리에서 쫒겨날 때(swap-out), swap 공간에 유지되도록 할 때(swap-in), 디렉터리에 대한 접근 제어에 사용


chmod 4755 파일명[or 디렉터리명]

chmod 2755 파일명[or 디렉터리명]

chmod 1755 파일명[or 디렉터리명]

(*755는 일반 퍼미션)


* 파일의 속성에 대한 부분은 이곳을 참고. http://devanix.tistory.com/233



그 다음 "Pointers to blocks"라는 주석이 달린 i_block이라는 변수를 살펴보자. 

위에서 살펴본 i_mode 변수나 그 이외의 변수들이 파일의 메타데이터(meta data)를 저장한다면, i_block이 가리키는 블락의 주소라는 것은 바로 실제 저장된 데이터(user data)의 위치를 의미한다.


여기에서 보면 한 엔트리당 크기가 32비트인(포인터라서), 길이가 EXT4_N_BLOCKS인 배열변수이다. inode구조에서 i_block은 파일에 속한 디스크블록들의 주소를 저장하는 포인터배열이며 길이는 통상 15이다.


현재 최신버전의 리눅스커널 소스의 EXT4_N_BLOCKS은 다음과 같이 정의되어있다. 


즉 여기에서도 총 15개의 엔트리로 구성되는데 12개는 direct block이라고 하며, 나머지 3개는 indirect block이다.

추측이지만, 소스 상에서 이를 적절히 활용해 쓰기위해 define을 저렇게 나누었을 것이다. 

(normal direct block, indirect block, double indirect block, triple indirect block ?? ㅎㅎㅎㅎ)



아래는 inode 구조체에서 i_block 부분을 나타낸 모습이다.





우선 i_block 배열은 12개의 direct block과 3개의 indirect block으로 구성되어 총 15개의 엔트리를 갖는다고 하였다.

direct block은 실제 파일의 내용을 담고있는 디스크의 데이터 블록을 가리키는 포인터변수이다. 그림에서 처럼 해당 파일에 속하는 디스크에 위치한 실제 데이터 저장장소를 가리킨다. 그런데 direct block은 12개 밖에 없다. 일반적으로 하나의 데이터 블록의 크기는 페이지프레임(page frame)의 크기와 동일하므로  일반적으로 4KB가 되는데 이대로라면 4KB짜리 데이터 블록 12개만큼을 한 파일에서 저장공간으로 사용할 수 있다는 이야기가 된다. 

계산해보자. 4KB * 12개 = 48, 즉 0-11까지의 총 12개의 직접블록으로 가질 수 있는 한 파일의 최대 크기는 고작 48KB이다. 


이는 절대 말이되지 않는다. 이와 상반되게 이번엔 indirect block이 어떻게 동작하는지 알아보자.


우선 간접블럭이라는 말은 디스크 상에 해당되는 위치를 직접적으로 가리키는 것이아니라 인덱스를 저장하는 인덱스블록을 중간에 더 두고 이를 거쳐서 가리키게 하는 것이다.

indirect block은 그림에서 처럼 3개로 나뉜다. (그림에서는 single indirect block만 디스크에 표시)


single indirect block :  단일 간접블록, 하나의 인덱스 블록을 가짐

double indirect block : 이중 간접블록, 2단계의 인덱스 블록을 가짐

triple indirect block :  삼중 간접블록, 3단계의 인덱스 블록을 가짐



single indirect block은 하나의 인덱스 블록을 갖는다고 하였다. 하나의 데이터 블록을 인덱스 블록으로 만든 후 더 많은 데이터 블록을 가리키게 하는 것이다. 이렇게 하면 고작 하나의 데이터 블록의 크기로 훨씬 더 많은 데이터 블록을 가리킬 수 있게 된다.


이제 single indirect block으로 지원가능한 파일의 크기를 알아보자. 

한개의 블록이 4KB, 즉 4096byte이고 주소를 가리키는 포인터는 4byte이다. 계산을 해보면 한 블록으로 (4096byte / 4byte = ) 1024개의 포인터를 표현할 수 있고 이 말은 즉, 한 블록이 가리킬 수 있는 주소공간은 1024개가 되는 것을 의미한다. 위에서 한 블록의 최소 크기가 4KB이라고 가정하였으니 single indirect block으로 표현할 수 있는 한 파일의 최대 크기는 4KB(데이터블록 한개) * 1024개(의 포인터) = 4096KB, 즉 4MB가 된다.


(* 여담으로 데이터블록의 최소 단위가 4KB라고 가정하였으므로, 최소 파일의 크기도 4KB가 된다. 파일에 아무것도 기록하지 않아도 생성자체만으로도 4KB를 확보하게 된다는 말이다.)



그렇다면 double indirect block은 어떨까? 말그대로 인덱스 블록을 이중으로 가진다.



계산해보자. 1024개 * 1024개 * 4KB(한 블록의 크기) = 4GB, 즉 double indirect block로 가질 수 있는 최대 파일의 크기는 4GB이다.



triple indirect block도 마찬가지이다. 인덱스 블록을 3중으로 가지므로 계산은 1024 * 1024 * 1024 * 4KB = 4TB 이렇게 된다.

즉, 여기서의 최대 파일의 크기는 4TB이다.



최종적으로 inode구조에서 지원할 수 있는 최대 파일의 사이즈는 direct block + single indirect block + double indirect block + triple indirect block이므로 48KB + 4MB + 4GB + 4TB가 된다.



하지만 32bit환경에서 리눅스가 지원하는 실제 파일 사이즈는 4GB(or 2GB)인데, 이는 node가 4TB정도의 파일을 지원할 수 있다고 하더라도, 리눅스 커널 내부의 파일 관련 함수들이 사용하는 변수나 인자들이 32bit로 구현되어 있기때문이다. (예. f_pos, 2^32 = 4GB...... 가 표현할 수 있는 최대 file offset이 됨)



전체적인 그림은 이걸 참고하자. (출처. http://uw714doc.sco.com/en/FS_admin/graphics/s5chain.gif)





이렇게 파일마다 가지고 있는 inode구조체를 파일시스템에서는 어떻게 각각의 파일과 연결하여 저장하는 것일까?
이를 위해서 "디렉터리 엔트리(Directory Entry)"라고하는 테이블을 사용하며 구조는 아래와 같다.



디렉터리 엔트리는 간단한 정보만 유지하고 실제 데이터 블록 인덱스 등의 세부적인 정보는 inode 번호를 통해 inode 자료구조에 접근해 사용한다.




디바이스 파일들은 /dev 디렉터리 밑에 위치하게 된다. /dev는 각종 디바이스의 디바이스 드라이버들이 저장되어 있는 디렉터리이다.

 IDE 디스크

 /dev/hd

 SCSI 디스크

 /dev/sd

 CDROM 

 /dev/scd

 COM 시리얼 포트

 /dev/ttyS

 콘솔장치

 /dev/console

 ...

 /dev/...



같은 종류의 디바이스에 대하여 디바이스 드리이버는 하나이므로(추후 장치관리자의 매이저/마이너 넘버 관련) 같은 종류의 장치에 대해서는 a, b, ... 식으로 네이밍이 된다.

또 디스크 내의 파티션은 1, 2, ... 처럼 숫자로 네이밍이 된다. 아래의 그림을 참고하자.




디스크마다 최대 64개의 파티션을 만들 수 있으며 파일시스템은 각 파티션마다 설치된다. $ mkfs 명령어를 사용하여 파티션에 파일시스템을 생성할 수 있다.


EXT 계열의 파일시스템은 크게 부트스트랩 코드가 들어있는 Boot Block과 N개의 Block Group이라는 것으로 구성되어있다. 


Boot Block에는 파티션의 부트섹터를 위해서 예약되어져 있으며 LILO나 GRUB이 들어간다. 

Block Group이라는 것은 파일을 저장할 때 인접한 부분에 데이터를 기록함으로써 Disk의 효율(seektime)을 높이고자 하는 개념이다. 


Block Group은 다음과 같이 구성되어있다.


# Block Group 

 Super Block

 - 파일 시스템의 전체적인 정보를 저장

 - 파일 시스템의 크기, 마운트 정보, 데이터 블럭의 갯수, inode 갯수, 블럭그룹 번호, 블록의 크기(1KB, 2KB, 4KB), 그룹당 블룩 수, 슈퍼블록의 수정여부 정보

 Group Descriptor

 (GDT)

 - 해당 파일 시스템 내의 모든 Block Group들의 정보를 저장

 - Block Bitmap의 블럭 번호, Inode Bitmap의 블럭 번호, 첫번째 Inode Table Block의 블럭 번호, 그룹안에 있는 빈 블럭 갯수, 그룹안에 있는 inode의 갯수, 그룹안에있는 빈 디렉토리 갯수 

* 크기가 일정하지 않아 Block Bitmap의 위치에 영향을 미치므로 그 위치 정보도 저장

 Block Bitmap

 - 데이터 블록 내에서 빈 공간을 관리하기 위해 사용

 - Block 사용 현황을 bit로 표현 (Super Block, Group Descriptor Block, inode table block 등등)

 - 한 블럭그룹 내의 각각의 블럭은 Block Bitmap의 각각의 bit에 대응

 - 사용중인 block은 Block Bitmap의 해당되는 인덱스의 값이 1로, 사용중이지 않으면 0으로 표현

 Inode Bitmap

 - inode table 내에서 빈 공간을 관리하기 위해 사용

 - Inode 사용 현황을 bit로 표현

 - 한 블럭그룹내의 각각의 inode는 Inode Bitmap의 각각의 bit에 대응

 - 사용중인 inode는 Inode Bitmap의 해당되는 인덱스의 값이 1로, 사용중이지 않으면 0으로 표현

 Inode Table

 - inode들을 관리

 - 파일 시스템 구축 시 계산된 값으로 결정. 고정된 값을 가짐

 - 인접하는 연속된 블럭으로 구성. 각 블럭은 미리 정의된 inode 갯수를 포함

 - Inode Table의 첫 번째 블럭번호를 Group Descriptor에 저장

 - 모든 inode 구조체의 크기는 128byte

 - 한 블록 내에 X개의 inode가 존재. 

        ex. default는 4096byte. 

              1024byte의 inode table block은 8개의 inode를 가질 수 있음

              2048byte의 inode table block은 16개의 inode를 가질 수 있음

              4096byte의 inode table block은 32개의 inode를 가질 수 있음 

         디스크가 남더라도 최대 숫자가 넘으면 파일 생성을 할 수 없음.

 Data Blocks

 - 파일에서 데이터를 저장하는 블럭

 - inode에 포함되어 있으며, inode는 몇개의 데이터 블럭을 포함


 참고. 리눅스에서는 sector가 512byte이다. 한번에 데이터를 read/write하는 블록 단위가 4KB(4096byte)일때, 

     8개의 sector가 대응되어야 한다.


슈퍼블록과 GDT는 사실상 파티션에 한 곳에만 저장되어도 되는 내용이지만, 데이터가 손상된다면 문제가 심각하게 될 수 있기 때문에 Block Group 마다 정보를 복사해 가지고 있는다. 


그리고 각 Block Group 내에서는 어디까지 데이터가 기록되어 있는지에 대한 정보를 가지고 있어야 하는데 이를 이를 위해 Block bitmap과 Inode bitmap이 사용된다.

Block bitmap와 Inode bitmap는 각각 자신의 영역의 비트로 해당 Block Group내에서의 블록 및 inode의 사용상태를 나타낸다. 




(이 예제는 "리눅스커널 내부구조"라는 책에 나와있다.)



root 밑에 File1.c와 mydir을 생성했다고 하므로써 disk block이 24까지 사용되었고, inode는 12까지 사용되었다. 

(참고로 root의 inode number는 2로 이미 설정되어 있으며, 일반 파일 생성 시 처음 할당되는 inode번호가 11이 되는 것도 이미 설정되어 있다.)


이를 bitmap으로 표현하면 아래 그림과 같다. 




사실 Block bitmap과 Inode bitmap은 한 블록내에 존재하며, 만약 블록이 4096byte * 8개의 블록을 표시해야 한다.




사실 EXT4는 기존의 EXT 시스템과는 달리 48bit의 주소체계를 사용하며 Extent 및 다른 변경된 기능을 사용한다.

이 부분에 대해서는 나중에 추후에 정리하도록 하겠다. (참고. http://behonestar.tistory.com/9)



* EXT2, 3, 4의 간단한 내용은 이전 포스팅에 있다. 

Linux File System: http://jiming.tistory.com/120








https://github.com/torvalds/linux/





#1. 리눅스 namespaces


1. IPC

2. Network

3. Mount

리눅스에서는 프로세스마다 파일시스템의 mount tree view를 가짐. 이로써 심볼릭/하드 링크 기능이 가능함

4. PID

PID Namespace를 사용하면 같은 isolation구역의 프로세스들은 같은 PID로 관리되어지며 suspending/resuming 등의 기능을 사용할 수 있다.

프로세스가 생성될 때 부모프로세스가 복사되어 생성되면서 가지게되는 부모프로세스의 PID가 새로 할당받는 자신의 PID와는 별도로,

해당 isolation 영역에서의 PID로 사용되는 듯

5. User

6. UTS


http://man7.org/linux/man-pages/man7/namespaces.7.html

https://lwn.net/Articles/531114/








'System > Linux Kernel' 카테고리의 다른 글

kernel/head.S (2)  (0) 2016.03.05
kernel/head.S  (0) 2016.02.27
[Linux Kernel Concept] GNU / Linux Kernel / Linux Distros  (0) 2016.02.13
gzip  (0) 2016.01.23
barrier와 volatile  (0) 2016.01.23


작년에 처음으로 리눅스를 접하게 되면서 왜 특히 이 분야에 철학이 중요하다고 하는지 많이 느끼게 되었다.

그런의미로 리눅스 커널의 개념에 대해서 조금씩 정리를 해볼까 한다.



10개월 동안 GNU, 커널, 배포판 이라는 단어를 특히 많이 접하게 되었는데 이를 그 첫번째 주제로 해볼까 한다.

[출처-위키]








GNU는 GNU is Not UNIX의 약자로 GNU는 GNU Project를 통해 개발된 UNIX기반의 Software OS이다. 당시 가장 핫하고 안정적이었던 운영체제인 UNIX가 자유 소프트웨어가 아니라는 점에 화(?)가난 리처드 스톨먼이 GNU 개발을 처음 시작하였고 '그누'라고 부르자고 제안하였다.


GNU Project는 1983년 9월 GNU선언문을 비롯한 여러 문서와 함께 발표되었으며 사실 리처드 스톨먼이 설립자이다. 

리처드 스톨먼은 자유 소프트웨어의 라이센스를 정의하였고 이것이 GNU GPL(General Public License)#1'에 있다.


또 리처드 스톨먼은 GNU Project를 철학적, 법률적, 자금적으로 조달하기 위해 1985년 자유소프트웨어재단(FSF, Free Software Foundation) 또한 설립한다.


GNU Project는 GNU를 완성하기 위해 노력하여 1990년까지 Emacs, gcc, 라이브러리, 유틸리티 등 커널을 제외한 다른 시스템들의 개발을 완료하였다. 하지만 호환에 있어서 커널 개발에는 어려움을 겪고 있었다. 


Trix(MIT), HURD(BSD), Mach



마침 1991년 9월, 10월 두차례에 걸쳐 리누스 토르발즈(이하 리누스 토발즈)가 GPL 기반으로 인터넷을 통해 리눅스 커널을 공개한다.

(Linux는 본디 커널을 의미한다.) 


리누스가 리눅스를 만들게 된 계기는 자신의 교수인 앤드류 스튜어트 타넨바움이 자신이 개발한 Minix라는 교육용 UNIX를 수업에 활용했지만, 학생들이 개조하지 못하게 제한을 두는것에 대해 불만을 가졌기 때문이라고 한다.

그래서 Linux는 Linu's miniX(리누스의 miniX)를 뜻한다고 하는데 이는 리누스 토발즈가 ftp로 Linux Kernel을 개발하도록 도와준 아리람케라는 사람이 지어주었다고 하며 참고로 처음에 리누스는 리눅스를 freax라고 명명하고 싶었다고 한다.


처음에 Linux는 단순한 에뮬레이터에서 시작으로 파일을 제어하는 기능이 완성되고 POSIX 호환을 목표로 하여 개발이 되었으며, lilo라는 부트로더까지 개발되었다. 

(처음에는 부팅을 위해 Minix같은 다른 프로그램의 도움이 필요했었다.)



1992년 GNU Project와 리누스 토발즈의 Kernel이 결합하여 GNU Project의 유틸리티를 사용하게 됨으로써 GNU/Linux라는 OS가 완전공개 되었다.

이것이 우리가 말하는 리눅스 OS이다.


참고로 Linux 마스코트인 저 펭귄의 이름은 Tux(턱스)라고 한다.



이후로 각종 배포판(Linux Distributions)들이 탄생하게 된다.

Linux Distros는 Linux Kernel, GNU Software 및 각종 free software로 만들어진 UNIX 계열의 OS라고 정의하며 회사차원, 커뮤니티차원 등 다방면으로 만들어지고 사용되어지고 있다. 대표적인 계열로는 Debian, SUSE, Ubuntu, RedHat, Arch, Fedora, SlackWare, Gentoo가 있다.



2016년 가장 핫한 배포판은 다음의 주소로 들어가면 확인할 수 있다.

https://www.linux.com/news/software/applications/878620-the-best-linux-distros-of-2016







리처드 스톨먼이나 리누스 토발즈나 정말 대단한것 같다. 리누스 토발즈는 리눅스 커널관리를 위해 Git까지 개발했고, 소스관리 뿐 아니라 커뮤니티 차원에서도 지금 엄청나게 사용되어지고 있으니..

둘다 구글에 찾아보면 대단한 어록들이 존재하고 또 재미있게 찾아 읽었다. 


그 중 기억에 남는 몇가지는, 

리누스 토발즈가 데이터구조에 대한 중요성을 굉장히 강조한다는 점과 리처드 스톨먼이 아직 싱글같다는 점이다(ㅋㅋㅋㅋ).

(http://yisangwook.tumblr.com/post/82653891224/linus-torvalds-good-programmer)






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

#1. GNU GPL


GNU GPL(General Public License)는 FSF에서 만든 자유소프트웨어 라이센스이며, 가장 큰 카피레프트이다.

이 라이센스로 되어있는 프로그램을 계승하면 그 역시 GNU GPL을 따라야 한다.


GNU LGPL이라는 것도 있는데 L은 약소되었다는 의미의 Lesser이며 라이브러리 단위를 겨냥한다.

또한 문서 형태에는 GNU FDL(Free Document License)를 사용한다.


'System > Linux Kernel' 카테고리의 다른 글

kernel/head.S  (0) 2016.02.27
[Linux Kernel Concept, File System] (1) 유닉스 파일시스템과 Inode구조체  (2) 2016.02.14
gzip  (0) 2016.01.23
barrier와 volatile  (0) 2016.01.23
PLT GOT  (0) 2016.01.23

g - unzip


마지막 4bit가 압축풀릴 사이즈를 나타내므로

커널에서 실제로 압축을 해제할 때 사이즈에서 -4를 수행함



inline함수를 사용하는 이유는, 매크로의 단점을 해결하기 위해서인뎅...


http://www.crashcourse.ca/wiki/index.php/Memory_barriers

여기서 보면



우리가 

DMB

DSB

ISB

때 했던 배리어가 있는 것도 확인 할 수 있다.

'System > Linux Kernel' 카테고리의 다른 글

[Linux Kernel Concept] GNU / Linux Kernel / Linux Distros  (0) 2016.02.13
gzip  (0) 2016.01.23
PLT GOT  (0) 2016.01.23
wont_overwrite  (0) 2016.01.23
CFS 구현의 핵심  (0) 2016.01.22

http://lapislazull.tistory.com/54

'System > Linux Kernel' 카테고리의 다른 글

gzip  (0) 2016.01.23
barrier와 volatile  (0) 2016.01.23
wont_overwrite  (0) 2016.01.23
CFS 구현의 핵심  (0) 2016.01.22
커널에서 원하는 값을 가지고오기 위해 clz를 하는 이유  (0) 2016.01.16
/* IAMROOT-12D (2016-01-23):
 * --------------------------
 * 여기서 부터
 */

wont_overwrite:
/*
 * If delta is zero, we are running at the address we were linked at.
 *   r0  = delta
 *   r2  = BSS start
 *   r3  = BSS end
 *   r4  = kernel execution address (possibly with LSB set)
 *   r5  = appended dtb size (0 if not present)
 *   r7  = architecture ID
 *   r8  = atags pointer
 *   r11 = GOT start
 *   r12 = GOT end
 *   sp  = stack pointer
 */

		/* IAMROOT-12D (2015-12-19):
		 * -------------------------
		 * r1 = r0 | r5 (delta | 덧붙여진 dtb의 크기)
		 * if(r1 == 0)
		 *	goto not_relocated;
		 */
		orrs	r1, r0, r5
		beq	not_relocated

		add	r11, r11, r0
		add	r12, r12, r0

#ifndef CONFIG_ZBOOT_ROM
		/*
		 * If we're running fully PIC === CONFIG_ZBOOT_ROM = n,
		 * we need to fix up pointers into the BSS region.
		 * Note that the stack pointer has already been fixed up.
		 */
		add	r2, r2, r0
		add	r3, r3, r0

		/*
		 * Relocate all entries in the GOT table.
		 * Bump bss entries to _edata + dtb size
		 */
1:		ldr	r1, [r11, #0]		@ relocate entries in the GOT
		add	r1, r1, r0		@ This fixes up C references
		cmp	r1, r2			@ if entry >= bss_start &&
		cmphs	r3, r1			@       bss_end > entry
		addhi	r1, r1, r5		@    entry += dtb size
		str	r1, [r11], #4		@ next entry
		cmp	r11, r12
		blo	1b





'System > Linux Kernel' 카테고리의 다른 글

barrier와 volatile  (0) 2016.01.23
PLT GOT  (0) 2016.01.23
CFS 구현의 핵심  (0) 2016.01.22
커널에서 원하는 값을 가지고오기 위해 clz를 하는 이유  (0) 2016.01.16
system(), fork()  (0) 2015.12.24

CFS 스케쥴링 알고리즘은 다음 실행할 프로세스를 실행할 때 vruntime 이 가장 작은 프로세스룰 선택한다.
단순히 타임슬라이스 개념만 생각하지 않고, 저 값의 비율을 생각함


이는 CFS에서 가장 중요한 특징이며,
이를 효율적으로 처리하기위해 red-black algorithm을 사용




스케줄링에 red-black algorithm이 사용되는 이유가 이거군..



'System > Linux Kernel' 카테고리의 다른 글

PLT GOT  (0) 2016.01.23
wont_overwrite  (0) 2016.01.23
커널에서 원하는 값을 가지고오기 위해 clz를 하는 이유  (0) 2016.01.16
system(), fork()  (0) 2015.12.24
[Unix V6] 시스템 부팅  (0) 2015.12.19

+ Recent posts