강좌 & 팁
안녕하세요 판다 이우영입니다.
복습!!
기억나시나요? 블록 디바이스 드라이버를 등록하고 제거하는 방법
int register_blkdev( unsigned int major, const char *name );
int unregister_blkdev( unsigned int major, const char *name );
위 두함수를 통해 했었죠?
2개의 인자는 이 디바이스의 주번호와 디바이스 이름이이라고 했습니다.
위보다 더중요한게 있었습니다. 바로 block_device_operations 입니다.
간단하게 표로 설명을 드리겠습니다.
구조체의 소유자를 나타내기 위해 사용 | |
int (*open) (struct block_device *, fmode_t); | 해당 블록 디바이스가 마운트 되는 경우에 호출 |
해당 블록 디바이스가 언마운트 되는 경우에 호출 | |
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); | 실제 동작을 정의하여 사용 |
int (*media_changed) (struct gendisk *); | 주기적으로 호출되어 장치가 연결 되었는지 확인 |
int (*revalidate_disk) (struct gendisk *); | 디바이스의 등록 상태를 정검을 위해 호출 |
그럼 간단하게 넘어가고 오늘 배울 내용을 알아 보겠습니다.
블록 디바이스 등록! (2)
block_device_operations 을 만들어 주었다면 드록을 해주는것이 당연하겠죠?
그럼 이 구조체는 어떻게 등록을 할까요? 오늘은 여기서 부터 시작을 해보겠습니다.
커널 2.4버전에서는 register_blkdev를 해줄때 같이 입력을 했었습니다.
하지만 2.6버전에서는 gendisk 구조체를 등록하면서 block_device_operations를 입력해 줍니다.
실질적으로 gendisk 구조체를 이용해 블록 디바이스를 등록하고 제거합니다.
(register_blkdev는 별로 의미가 없습니다.)
그럼 이 gendisk 구조체를 어떤 식으로 등록하는지 간단하게 알아보겠습니다.
typedef struct { void *priv; struct request_queue *queue; spinlock_t lock; struct gendisk *gd; } XXX_device; static XXX_device device[MAX_DEVICE]; ----------------------------------------------------------------------------------------------------------- int XXX_init( void) { .... for(lp = 0; lp < MAX_DEVICE; lp++) { device[lp].data = vdisk[lp]; device[lp].gd = alloc_disk(1); .... device[lp].gd->major = XXX; device[lp].gd->first_minor = lp; device[lp].gd->fops = &fops; ....
add_disk(device[lp].gd); } .... } |
위에서 XXX_device 와 같이 디바이스 드라이버 안에서 관리할 구조체들을 모아서 관리를 합니다.
드라이버가 등록되어 init이 호출될때 위와같이 gendisk를 등록해 줍니다.
(gendisk에는 아주 많은 필드들이 있지만 간단하게 3개만 적어 보았습니다.)
이때 fops를 등록하죠! 여기에 등록하는것이 바로 block_device_operations 입니다.
그리고 마지막에 add_disk 함수를 통해 gendisk를 등록해 줍니다.
하지만 for문을 통해 여러번 등록하고 있습니다.
블럭디바이스는 문자 디바이스와 다르게 파티션이 나누어져 있을 수 있습니다.
그렇기 때문에 여러 파티션을 부번호를 부여하여 등록하게 됩니다.
그럼 gendisk 구조체를 알아볼까요?
include/linux/genhd.h 파일에 정의 되어 있습니다.
133struct gendisk { 134 /* major, first_minor and minors are input parameters only, 135 * don't use directly. Use disk_devt() and disk_max_parts(). 136 */ 137 int major; /* major number of driver */ 138 int first_minor; 139 int minors; /* maximum number of minors, =1 for 140 * disks that can't be partitioned. */ 141 142 char disk_name[DISK_NAME_LEN]; /* name of major driver */ 143 144 /* Array of pointers to partitions indexed by partno. 145 * Protected with matching bdev lock but stat and other 146 * non-critical accesses use RCU. Always access through 147 * helpers. 148 */ 149 struct disk_part_tbl *part_tbl; 150 struct hd_struct part0; 151 152 struct block_device_operations *fops; 153 struct request_queue *queue; 154 void *private_data; 155 156 int flags; 157 struct device *driverfs_dev; // FIXME: remove 158 struct kobject *slave_dir; 159 160 struct timer_rand_state *random; 161 162 atomic_t sync_io; /* RAID */ 163 struct work_struct async_notify; 164#ifdef CONFIG_BLK_DEV_INTEGRITY 165 struct blk_integrity *integrity; 166#endif 167 int node_id; 168};
아주 많죠? 하지만 걱정하지 않으셔도 됩니다.
gendisk는 alloc_disk() 함수를 통해 할당을 받아옵니다.
이때 기본적인 내용들이 초기화 되서 오기 때문에 우리가 설정해주어야 하는 필드들은 몇개 없습니다.
int major;
주번호 입니다. 같은 드라이버라면 모두 같은 값이 들어갑니다.
int first_minor;
부번호 입니다. 같은 장치가 하나만 있는것이 아니기 때문에 부번호로 장치를 구별하게 됩니다.
struct block_device_operations *fops;블록 오퍼레이션 구조체를 등록합니다. 커널2.4와 다른점이라고 했습니다.struct request_queue *queue;블록 디바이스마다 관리하는 요구 큐를 등록 해주어야 합니다.char disk_name[DISK_NAME_LEN];블록 디바이스의 기본이름을 입력합니다. 파티션이 나누어 질수록 뒤에 숫자를 붙여 나타냅니다.int flags;블록 디바이스의 특성을 입력합니다.제거가 가능하거나 CD-ROM같은 경우 입력합니다.void *private_data;request() 함수에서 디바이스 정보를 얻기위해 사용한다.그럼 오늘은 여기까지!!그럼 다음시간에 만나겠습니다.