Step by Step 커널 프로그래밍 강좌⑥
작성자 정보
- 웹관리자 작성
- 작성일
컨텐츠 정보
- 15,437 조회
- 0 추천
- 목록
본문
Step by Step 커널 프로그래밍 강좌⑥
파일시스템마운트
이번 강좌는 지난 강좌에 이어 파일시스템 마운트 과정의 나머지 부분에 대해 알아보기로 한다. 내용 중 일부는 지난 호와
관련되기 때문에 지난 호와 같이 봐야 이해가 될 것이라 생각한다.
글 _ 김민찬 KLDP 멤버, 전문 프로그래머
연재 순서
① 커널 프로그래밍 환경 구축하기와 특징
② 모듈 구현하기
③ 리눅스 커널의 메모리 관리
④ 커널의 동기화에 관하여
⑤ 파일 시스템 마운트 1
⑥ 파일 시스템 마운트 2
alloc_inode 함수로 inode 할당
alloc_inode 함수를 좀 더 자세히 살펴보자. 이 함수는 리눅스커널에서 아주 중요한 역할을 하는 구조체 중 하나인 inode를 할당하는 함수이다. inode는 많은 필드를 가지고 있으며 초기화 과정 또한 그리 만만치 않다. inode의 중요한 몇몇 필드(address_space,backing_dev_info, host, i_mapping)만을 alloc_inode 함수를 살펴보며 같이 보기로 하자.
struct inode {
struct hlist_node i_hash;
struct list_head i_list;
struct list_head i_sb_list;
struct list_head i_dentry;
unsigned long i_ino;
...
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev;
...
unsigned int i_blkbits;
unsigned long i_blksize;
unsigned long i_version;
unsigned long i_blocks;
unsigned short i_bytes;
unsigned char i_sock;
...
struct inode_operations *i_op;
struct file_operations *i_fop; /* former → i_op→ default_file_ops */
struct super_block *i_sb;
struct file_lock *i_flock;
struct address_space *i_mapping;
struct address_space i_data;
...
/* These three should probably be a union */
struct list_head i_devices;
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
...
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying*/
unsigned int i_flags;
atomic_t i_writecount;
void *i_security;
union {
void *generic_ip;
} u;
};
코드 1. inode 구조체
alloc_inode 함수는, 인수로 받은 superblock의 alloc_inode함수 포인터가 정의돼 있다면 정의된 함수를 호출해 inode를 할당받는다(이것 또한 hook이다. 커널의 VFS 구조는 파일시
스템의 많은 유연성을 제공하기 위해 많은 hook을 제공한다).
하지만 rkfs에는 해당함수가 정의돼 있지 않기 때문에 커널은 inode_cachep을 통해 inode를 할당받는다.
static struct inode *alloc_inode(struct super_block *sb)
{
static struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static struct file_operations empty_fops;
struct inode *inode;
if (sb → s_op → alloc_inode)
inode = sb → s_op → alloc_inode(sb);
else
inode = (struct inode *)kmem_cache_alloc(inode_cachep, SLAB_KERNEL);
if (inode) {
struct address_space * const mapping =
&inode → i_data;
inode → i_sb = sb;
inode → i_blkbits = sb->s_blocksize_bits;
inode → i_flags = 0;
atomic_set(&inode → i_count, 1);
inode → i_sock = 0;
inode → i_op = &empty_iops;
inode → i_fop = &empty_fops;
inode → i_nlink = 1;
atomic_set(&inode→ i_writecount, 0);
inode → i_size = 0;
inode → i_blocks = 0;
inode → i_bytes = 0;
inode → i_generation = 0;
#ifdef CONFIG_QUOTA
memset(&inode→ i_dquot, 0, sizeof(inode→ i_dquot));
#endif
inode → i_pipe = NULL;
inode → i_bdev = NULL;
inode → i_cdev = NULL;
inode → i_rdev = 0;
inode → i_security = NULL;
inode → dirtied_when = 0;
if (security_inode_alloc(inode)) {
if (inode → i_sb → s_op → destroy_inode)
inode → i_sb →s_op → destroy_inode(inode);
else
kmem_cache_free(inode_cachep, (inode));
return NULL;
}
mapping → a_ops = &empty_aops;
mapping → a_ops = &empty_aops;
mapping → host = inode;
mapping → flags = 0;
mapping_set_gfp_mask(mapping,GFP_HIGHUSER);
mapping → assoc_mapping = NULL;
mapping → backing_dev_info = &default_backing_dev_info;
/*
* If the block_device provides a backing_dev_info for client
* inodes then use that. Otherwise the inode share the bdev's * backing_dev_info.
*/
if (sb → s_bdev) {
struct backing_dev_info *bdi;
bdi = sb → s_bdev→ bd_inode_backing_dev_info;
if (!bdi)
bdi = sb → s_bdev → bd_inod → i_mapping → backing_dev_info;
mapping → backing_dev_info = bdi;
}
memset(&inode → u, 0, sizeof(inode → u));
inode → i_mapping = mapping;
}
return inode;
}
코드 2. alloc_inde 함수
위의 함수에서 address_space는 page cache를 구현하는, 아주 중요한 역할을 하는 구조체이다.
또한 실제로 파일시스템에서 블록을 읽기 위한 기능을 구현하 는 address_space_operations 구조체를 포함하기도 한다. 파일을 읽기 위한 함수 테이블 필드로는 inode_operations 구조
체인 i_op와 file_operations 구조체인 i_fop가 있다. inode_operations 구조체는 파일과 관련된 inode를 만들고 삭제하는 등의 역할을 하는 inode 관련 함수들의 모음이다.
반면, file_operations 구조체는 응용 프로그래머들이 일반적으로 사용하는 open, read, write, close, ioctl, mmap 등과 연관되는 함수이다.
일반적으로 file_operations의 함수들은 최종적으로 address_space_operations의 함수들을 호출해 실제적으로 block device에서 페이지들을 읽게 된다.
read-ahead 메커니즘 활용
다음으로 backing_dev_info 필드는 read-ahead와 관련된 정보를 저장하는 필드이다.
리눅스는 disk-based filesystem을 일반적으로 사용해왔다. 지금처럼 nand memory를 저장 장치로 사용하는 임베디드 리눅스가 보편화되기 전까지만 해도 그랬다. 따라서 리눅스 커널은 read-ahead 메커니즘을 사용해 파일시스템 성능을 개선했다.
disk-based 저장 장치는 특정 섹터를 찾기 위해 헤더와 실린 더를 이동해야하고, 따라서 그 속도가 섹터를 읽거나 쓰는 것에 비해 현저히 느리다. 때문에 한 섹터를 읽을 때 인접한 섹터들을 미리 읽어둬 페이지 캐시에 저장해 놓는 기술 즉, readahead 기술을 활용했다.
이는 특정 프로그램이 특정 섹터를 필요로 한다면 조만간 인접한 다른 섹터도 필요로 할 확률이 높을 것이라는 낙관론적 방법에서 시작됐다. 하지만 nand와 같이 seek time이 거의
들지 않는 저장장치를 사용할 때 read-ahead가 주는 장점은 많이 줄어들며 심지어는 부팅타임에 시간을 잡아먹는 요소로 작용하기도 한다.
read-ahead와 관련된 작업은 inode를 처음 할당할 때 시작 된다. 먼저 default_backing_dev_info의
관련자료
-
이전
-
다음