블록 디바이스 드라이버.pptx
08. 블록 디바이스 드라이버
0. 개요
•블록
디바이스 드라이버
•리눅스에서
지원하는 세가지 핵심 디바이스 드라이버 중 하나
•문자
•블록
•네트워크
•하드디스크와
같은 저장 디바이스
•블록단위의
입출력
•일반적으로
마운트를 통해 파일시스템을 구현하는 시스템으로
사용 됨
•요구
큐라는 request_queue_t
구조체를
이용해 입출력
•2.4일때와 2.6일때가
전혀 다름 =>
스케줄러의
향상
1. 리눅스와
블록 디바이스
•하드디스크와
디바이스 파일
•윈도우의
표현 방법
•하나의
파티션으로 구성됨
•C:,
D:
•물리적인
하드디스크를 표현하는 방법이 없음.
•리눅스의
표현 방법
•하나의
디바이스 파일로 취급
•/dev/hda
•/dev/hdb
•디바이스
파일에 파티션을 분할
•/dev/hda1
•/dev/hda2
•문자
드라이버와 차이점
2. 하드
디스크의 특성
•하드디스크의
데이터 처리 방식
•하드디스크에
데이터를 읽거나 쓰기 위해 드라이버에 명령을 줌
•인터럽트로
요구된 처리가 끝났는지 확인함
•쓰기
•디스크
드라이버가 하드디스크에 쓰기 명령과 함께 쓸 데이터를 전달
•커널에서
논리적인 섹터 번호와 써 넣을 데이터의 주소가 전달 됨
•디바이스
드라이버가 실제 섹터 위치를 지정하는 값으로 변환
•쓰기 완료
후 드라이버의 인터럽트 서비스 함수가 커널에 인터럽트를 발생 시킴
•읽기
•드라이버가
디스크에게 데이터 읽기 명령 전달
•섹터
번호와 읽어올 데이터를 넣을 주소.
•완료 후
인터럽트 발생
•하드디스크의
데이터 처리 방식
•특정
섹터를 읽은 다음 다른 섹터를 읽으려면 최소한 한바퀴의 디스크 회전이 필요.
•미리 읽기
•읽을
섹터를 기준으로 여분의 섹터를 더 읽음
•장점
•이전에
읽은 데이터를 응용에서 요구하면 디스크 접근 없이 버퍼에서 바로 읽을 수 있음.
•클러스터
•섹터가
아닌 클러스터 단위로 처리하는 것
•e.g. 두개의
섹터를 하나의 클러스터 단위로 처리.
•장점
•미리
읽기시 여분의 읽기 섹터 수를 고정 처리하는 효과가 있음
3. 블록
디바이스 드라이버와 요구 큐
•호출
주체에 따른 입출력 처리
•호출
주체에 따라 두 가지로 나뉨
1.외부에서
블록 디바이스에 직접 접근
•응용에서
디스크의 내용을 읽거나 쓸 수 있도록 드라이버에게 직접 요구
-> 드라이버에서
수행
•e.g.
•fdisk
같은
파티션 분할 프로그램
•
2.블록
디바이스를 응용 프로그램에서 간접 접근
1.응용이
파일에 읽기/쓰기를
수행
2.파일
시스템에서 드라이버에 읽기/쓰기 요청
3.디바이스가
읽기/쓰기 수행
•문자
드라이버와의 처리 방식의 차이점
•문자
드라이버
•응용에서
읽기를 시도하면 file_operation의 read()
함수에서
처리
•블록
드라이버
•응용
프로그램의 읽기에 대응하는 read()함수가
없음.
•읽기 루틴
구현의 필요성
•문자 드라이버와의
처리
방식의 차이점
•블록
드라이버에서 읽기 요청
1.응용
프로그램에서 read 요청
2.커널이
커널 내부 버퍼에 있는지 살펴봄
3.있으면
전달, 없으면
드라이버에 요청
•커널은
디바이스를 효율적으로 처리하기 위해 처리할 내용을 큐에 모아 둠
•일정
시간이 지나거나 임계치에 도달하면 드라이버에 처리를 요구
•커널과
드라이버간의 전달은 요구 큐와 처리 함수를 이용
•문자
드라이버와 다른 점임.
4. 버전별
블록 디바이스 처리
•새로운
입출력 스케줄러
•읽기
요청을 우선 처리
•엔터프라이즈
환경에서 읽기가 더 발생
•요구 큐의
데드라인 도입
•bio 구조체
•진행중인
블록 IO 연산에
대한 직관적인 인터페이스 제공
•블록 IO 요청이 bio 구조체로
표시됨
•요청에
있는 각 블록이 bio_vec
이라는
구조체에 저장되어 배열로 관리됨
•bio_vec은 <page, offset, len>과 같은
형식으로 표현 되면서 하나의 IO
연산을
나타냄.
5. 커널
2.6의 블록
디바이스 드라이버
•블록
디바이스 드라이버 작성 요구 사항
•블록
디바이스의 특성 정의
•주 번호와
부 번호 및 디바이스명에 대한 정의
•블록
디바이스 드라이버 등록
•블록
디바이스 드라이버를 위한 구조체 선언
•파일
오퍼레이션 구조체 처리
•요구 큐에
관련된 처리 및 함수 선언
•블록
디바이스 추가를 위한 gendisk
구조체
생성 및 등록
•블록
디바이스의 크기 설정 및 기타 속성 처리
•블록
디바이스 드라이버 등록과 해제
•등록
•모듈
초기화 함수
•커널에
의해 호출 되는 초기화 함수
•register_blkdev()
•인자 : 주번호, 디바이스
이름
•해제
•unregister_blkdev()
•인자 : 주 번호, 디바이스
이름
•블록
디바이스 드라이버를 위한 구조체 선언
•블록
디바이스 드라이버를 다루기 위한 내용
1.request_queue
구조체
변수
•요구
처리를 위해
2.gendisk
구조체
변수
•블록
디바이스 처리를 위해
3.spinlock_t
구조체
변수
•request
방식을
사용할 경우 필요함
4.블록
디바이스를 위한 자체적인 정보 변수
typedef struct
{
void *priv; //자체 정보 관리 주소변수
struct request_queue *queue;
spinlock_t lock;
struct gendisk *gd;
} xxx_device;
•struct
block_device_operations
•블록
디바이스가 동작하기 위해서는 위 구조체를 이용해 동작을 위한 기본
함수들을
등록 해야함.
•이
구조체는 gendisk
구조체의 fops 필드에
등록 됨.
•add_disk()
함수에
의해 gendisk
구조체가
등록될 때 함께 등록 됨.
•struct
block_device_operations
•메소드
•struct
module *owner;
•파일
오퍼레이션의 소유자
•open/release
•디바이스가
마운트/언마운트
될 때 호출 됨.
•모듈
카운트 관리
•ioctl
(struct
inode *inode, struct file *filp, unsigned int cmd, unsigned log arg)
•인자 cmd에 들어갈
항목
•BLKGETSIZE
– 섹터 수로
표현되는 블록 디바이스의 크기를 응용에 전달.
•BLKFLSBUF
– 드라이버
내부에 저장된 버퍼의 내용을 실제 디바이스에 모두 써 넣기
•BLKRAGET
– 응용에서
디바이스에 설정되어 있는 읽기 값을 미리 얻어 옴
•BLKRASET
– 디바이스의
미리 읽기 값을 설정
•BLKRRPART
– 디바이스의
파티션 테이블을 다시 읽기 요청함
•HDIO_GETGEO
– 디바이스의
특성을 디스크 구조에 대한 형태로 응용에 제공
•struct
block_device_operations
•메소드
•media_changed
•CD-ROM
처럼 제거
할 수 있는 디바이스를 다루기 위해 주기적으로 호출해 검사 함.
•이를 구현
하면 revalidate_disk를 필수로
선언해야 함
•revalidate_disk
•디바이스가
교체 되었다면 디바이스 상태를 재설정 해야함.
•요구 큐
관련 처리 및 함수
•make_request
방식
•램디스크,
loop
•request
방식
•대부분의
경우
•make_requst
방식
•bio
구조체를
사용 하여 처리
•블록 디바이스에
읽기나 쓰기 발생
->커널이 make_request()
함수를
호출해 처리를 요구함
static int
xxx_make_request(request_queue_t *q, struct bio *bio)
{
struct bio_vec *bvec;
: //요구
유효성 검사 처리
bio_for_each_segment(bvec, bio, i)
{
: // 요구 처리
}
bio_endio(bio, bio->bi_size, 0); // 처리 종료
return 0;
fail :bio_IO_error(bio, bio->bi_size);
return 0;
}
•bio
구조체
중요 변수
•bio->bi_bdev->bd_disk->private_data
: 디바이스 관리를 위한 구조체 메모리 주소
•bio_data_dir(bio):
처리 방향
•bio->bi_sector
: 논리적
시작 섹터 번호
•bio->bi_size
: 처리해야
할 총 데이터
크기
•request
방식
•대부분의
블록 디바이스의 경우 request를
이용하여 처리
•elv_next_request()
함수로
처리할 구조체가 없을때 종료
static void
xxx_request(request_queue_t *q)
{
struct request
*xxx_req
while(1)
{
xxx_req
= elv_next_request(q);
if(!xxx_req)
return ;
:
//처리 루틴
if(처리 성공)
end_request(xxx_req, 1);
else
end_request(xxx_req, 0);
}
}
•request
구조체
중요 변수
•xxx_req
-> rq_disk->private_data
: 디바이스를
관리하기 위해 할당한 메모리 주소
•xxx_req
-> sector : 처리해야
할 논리적 섹터번호
•xxx_req
->current_nr_sectors : 처리해야
하는 섹터 수
•xxx_req
-> buffer : 읽기나
쓰기를 처리할 데이터의 선두 주소
•기타
처리 함수
•get_capacity(xxx_req
-> rq_disk)
•처리중인
디바이스의 용량을 구함.
•request
함수
초기에 처리 요구 범위가 디바이스의 크기를
넘는지
검사
•rq_data_dir(xxx_req)
•처리
방향을 얻음
•gendisk
구조체
생성 및 등록
•커널 2.6에서
드라이버가 디바이스를 처리하기 위해서는 add_disk()
함수를
이용해 등록
•for(lp
=0; lp<XXX_MAX_DEVICE; lp++)
{
device[lp].gd = alloc_disk(XXX_MAX_PARTITIONS);
device[lp].gd->major
= XXX_DEV_MAJOR;
device[lp].gd->fops = &vhdd_fops;
device[lp].gd->queue = device[lp].queue;
device[lp].gd->private_data =
&device[lp];
sprintf(device[lp].gd->disk_name, “vhdd%c”. ‘a’+lp);
set_capacity(device[lp].gd, XXX_SECTOR_TOTAL);
add_disk(device[lp].gd);
}
•add_disk는 gendisk
구조체
변수를 이용해 디바이스의 정보를 커널에 등록
•gendisk
구조체
생성 및 등록
•gendisk
구조체를
이용해 디바이스를 등록하고 제거할 때 사용하는 함수
•alloc_disk
: gendisk 구조체
생성
•add_disk
: 등록
•del_gendisk
: 제거
•put_disk
: 구조체
소멸
•gendisk
구조체
생성 및 등록
•add_disk
이용전 gendisk
구조체
할당과 필드 설정 필요
•#include
<linux/genhd.h>에
선언 되어 있음
•alloc_disk()
함수에서
초기화 됨
•드라이버가
추가적으로 초기화 해야 하는 필드
•int
major ::주번호
•int
first_minor ::부
번호
•struct
block_device_operations *fops:: 블록 파일 오퍼레이션 구조체 변수 주소
•struct
request_queue *queue :: 디바이스
요구 처리를 위한 변수 주소
•char
disk_name[16] :: 디바이스
이름
•/proc/partition에 나타남
•int
flags :: 디바이스의
특성
•cd-rom
이나 removable
디바이스
•void
*private_data :: 정보
처리를 위한 변수 주소 (request()
함수에서
구할 수 있음)
•gendisk
구조체
할당과 해제
•gendisk
구조체
변수를 할당하고 해제
•할당
•struct
gendisk *alloc_disk(int minors)
•해제
•void
put_disk(struct gendisk *disk)
•gendisk
구조체
등록과 해제
•gendisk
구조체를
커널에 등록하고 해제
•등록
•void
add_disk(struct gendisk *disk)
•해제
•void
del_gendisk(struct gendisk *gp)
•블록
디바이스의 크기 설정
•커널에
등록하기 전 디바이스의 크기를 설정 해야 함
•void
set_capacity(struct gendisk *disk, sector_t size)
•블록
디바이스 제거
•대부분의
블록 디바이스 드라이버는 커널이 부팅 될 때 등록 됨
•만약
드라이버 모듈을 제거할 때는 다음 순서를 거쳐야 함
1.del_gendisk()를
이용하여 제거
2.alloc_disk()로 할당한
메모리를 put_disk()로 해제
3.등록된
요구 큐를 blk_cleanup_queue()로
커널에서 제거
4.등록된
디바이스를 unregister_blkdev()를 이용해
제거
참고 : [IT EXPERT 리눅스 디바이스 드라이버]