Linuxのノード(iノード)、デバイス(CDEV)、ドライブ(OPS)にリンクする方法
CDEVのinode構造(チップセレクト)との比較
struct inode {
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev; // 设备文件的设备号
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
const struct inode_operations *i_op;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
};
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
可能性のあるリンク
- iノード構造体CDEVの*のi_cdev、装置(CDEV)ポインタを保持することができます
- iノードの\ CDEVは、ファイル・オペレーターあります:struct file_operations * FOPSを。
- iノードの\ CDEVは、デバイス番号の情報があります。dev_tのDEVを、
キャラクタ・デバイス・ドライバ・コード(チップセレクト)
#define VSER_MAJOR 256
#define VSER_MINOR 0
#define VSER_DEV_CNT 2
#define VSER_DEV_NAME "vser"
static DEFINE_KFIFO(vsfifo0, char, 32);
static DEFINE_KFIFO(vsfifo1, char, 32);
struct vser_dev {
struct kfifo *fifo;
struct cdev cdev;
};
static struct vser_dev vsdev[2];
static int vser_open(struct inode *inode, struct file *filp)
{
filp->private_data = container_of(inode->i_cdev, struct vser_dev, cdev);
return 0;
}
static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
ssize_t Ret = 0;
unsigned int copied = 0;
struct vser_dev *dev = filp->private_data;
Ret = kfifo_to_user(dev->fifo, buf, count, &copied);
if (Ret)
return Ret;
return copied;
}
static struct file_operations vser_ops = {
.owner = THIS_MODULE,
.open = vser_open,
.release = vser_release,
.read = vser_read,
.write = vser_write,
};
static int __init vser_init(void)
{
int i;
int ret;
dev_t dev;
dev = MKDEV(VSER_MAJOR, VSER_MINOR);
ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
if (ret)
goto reg_err;
for (i = 0; i < VSER_DEV_CNT; i++) {
cdev_init(&vsdev[i].cdev, &vser_ops);
vsdev[i].cdev.owner = THIS_MODULE;
vsdev[i].fifo = i == 0 ? (struct kfifo *) &vsfifo0 : (struct kfifo*)&vsfifo1;
ret = cdev_add(&vsdev[i].cdev, dev + i, 1);
if (ret)
goto add_err;
}
return 0;
add_err:
for (--i; i > 0; --i)
cdev_del(&vsdev[i].cdev);
unregister_chrdev_region(dev, VSER_DEV_CNT);
reg_err:
return ret;
}
module_init(vser_init);
module_exit(vser_exit);
間で
// 第一个字符设备的设备号dev
dev = MKDEV(VSER_MAJOR, VSER_MINOR);
// 连续申请VSER_DEV_CNT个字符设备的设备号,设备名为VSER_DEV_NAME
ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
...
// 增加VSER_DEV_CNT个字符设备到系统中
for (i = 0; i < VSER_DEV_CNT; i++) {
// 初始化cdev->ops
cdev_init(&vsdev[i].cdev, &vser_ops);
...
vsdev[i].fifo = i == 0 ? (struct kfifo *) &vsfifo0 : (struct kfifo*)&vsfifo1;
...
// 初始化cdev->dev, 值为:dev + i, 仅申请一个内存地址存放该cdev
ret = cdev_add(&vsdev[i].cdev, dev + i, 1);
}
register_chrdev_region
int register_chrdev_region(dev_t from, unsigned count, const char *name)
-> __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);
#define CHRDEV_MAJOR_HASH_SIZE 255
// 用于保存设备号信息
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct file_operations *fops;
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
// 申请新的内存空间,存放新设备号信息
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock);
// 对于传入主设备号major=0的,在chrdevs数组中查找未使用的元素,记录索引i,作为动态申请得到的主设备号。
/* temporary */
if (major == 0) {
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
if (i == 0) {
ret = -EBUSY;
goto out;
}
major = i;
ret = major;
}
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strncpy(cd->name,name, 64);
// 将主设备号major转化为数组索引i, i=major % CHRDEV_MAJOR_HASH_SIZE; 宏 CHRDEV_MAJOR_HASH_SIZE 的值为255;可以相像,cahrdevs是一个保存了255条链表,每条链表都是major转化后hash值相同的设备的设备号信息的集合。因此, major=1, major=256,的两个设备号存放在同一条链表上。
i = major_to_index(major);
// 先比较major
// 同一条链表,major从小往大排,所以在链表中查找合适的位置,主要关注major
// 1、当前节点的major大于新节点
// 2、当前节点的major值与新节点相同并且当前minor也大于新节点
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
// 再比较minor
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor;
int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor + minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
// chardevs[major%255]这条链表中插入新节点
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
// 全局变量
static struct kobj_map *cdev_map;
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
unsigned index = MAJOR(dev);
unsigned i;
struct probe *p;
if (n > 255)
n = 255;
// 申请内存,保存设备探针
p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
}
mutex_lock(domain->lock);
// domain = cdev_map, cdev_map 保存了255条探针链表,索引值与major对应。
// p -= n
for (i = 0, p -= n; i < n; i++, p++, index++) {
struct probe **s = &domain->probes[index % 255];
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s;
*s = p;
}
mutex_unlock(domain->lock);
return 0;
}
mknod
指定されたデバイスの数は、また、コマンドmknodeを使用して、明確な生成ノードを容易にするため:
mknod /dev/vser c 256 0
デバイス番号をアプリケーションが動的である場合、PROC後にデバイスをロードすることによって得ることができます。
cat /proc/devices
トラッキングのmknod
sys_mknod(/fs/namei.c)
sys_mknodat(AT_FDCWD, filename, mode, dev);
vfs_mknod(nd.dentry->d_inode, dentry,mode, new_decode_dev(dev));
dir->i_op->mknod(dir, dentry, mode, dev);
// 根据文件系统决定
jffs2_mknod
static struct inode_operations yaffs_dir_inode_operations = {
.create = yaffs_create,
.lookup = yaffs_lookup,
.link = yaffs_link,
.unlink = yaffs_unlink,
.symlink = yaffs_symlink,
.mkdir = yaffs_mkdir,
.rmdir = yaffs_unlink,
.mknod = yaffs_mknod,
.rename = yaffs_rename,
.setattr = yaffs_setattr,
};
/*
* File creation. Allocate an inode, and we're done..
*/
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) // 内核版本区分, 2.5以后的版本
static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
dev_t rdev)
#else // 2.5及更旧的版本
static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode,
int rdev)
#endif
{
struct inode *inode;
yaffs_Object *obj = NULL;
yaffs_Device *dev;
// 文件夹的Kobj对象
yaffs_Object *parent = yaffs_InodeToObject(dir);
int error = -ENOSPC;
uid_t uid = current->fsuid;
gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
if((dir->i_mode & S_ISGID) && S_ISDIR(mode))
mode |= S_ISGID;
if (parent) {
T(YAFFS_TRACE_OS,
(KERN_DEBUG "yaffs_mknod: parent object %d type %d\n",
parent->objectId, parent->variantType));
} else {
T(YAFFS_TRACE_OS,
(KERN_DEBUG "yaffs_mknod: could not get parent object\n"));
return -EPERM;
}
T(YAFFS_TRACE_OS, ("yaffs_mknod: making oject for %s, "
"mode %x dev %x\n",
dentry->d_name.name, mode, rdev));
dev = parent->myDev;
yaffs_GrossLock(dev);
switch (mode & S_IFMT) {
default:
// 字符类型
/* Special (socket, fifo, device...) */
T(YAFFS_TRACE_OS, (KERN_DEBUG
"yaffs_mknod: making special\n"));
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
obj =
yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid,
gid, old_encode_dev(rdev));
#else
obj =
yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid,
gid, rdev);
#endif
break;
case S_IFREG: /* file */
T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n"));
obj =
yaffs_MknodFile(parent, dentry->d_name.name, mode, uid,
gid);
break;
case S_IFDIR: /* directory */
T(YAFFS_TRACE_OS,
(KERN_DEBUG "yaffs_mknod: making directory\n"));
obj =
yaffs_MknodDirectory(parent, dentry->d_name.name, mode,
uid, gid);
break;
case S_IFLNK: /* symlink */
T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n"));
obj = NULL; /* Do we ever get here? */
break;
}
/* Can not call yaffs_get_inode() with gross lock held */
yaffs_GrossUnlock(dev);
if (obj) {
// 将inode与obj、dentry关联
inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
d_instantiate(dentry, inode);
T(YAFFS_TRACE_OS,
(KERN_DEBUG "yaffs_mknod created object %d count = %d\n",
obj->objectId, atomic_read(&inode->i_count)));
error = 0;
} else {
T(YAFFS_TRACE_OS,
(KERN_DEBUG "yaffs_mknod failed making object\n"));
error = -ENOMEM;
}
return error;
}
/*
* Mknod (create) a new object.
* equivalentObject only has meaning for a hard link;
* aliasString only has meaning for a sumlink.
* rdev only has meaning for devices (a subset of special objects)
*/
static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type,
yaffs_Object * parent,
const YCHAR * name,
__u32 mode,
__u32 uid,
__u32 gid,
yaffs_Object * equivalentObject,
const YCHAR * aliasString, __u32 rdev)
{
yaffs_Object *in;
YCHAR *str;
yaffs_Device *dev = parent->myDev;
/* Check if the entry exists. If it does then fail the call since we don't want a dup.*/
if (yaffs_FindObjectByName(parent, name)) {
return NULL;
}
in = yaffs_CreateNewObject(dev, -1, type);
if(type == YAFFS_OBJECT_TYPE_SYMLINK){
str = yaffs_CloneString(aliasString);
if(!str){
yaffs_FreeObject(in);
return NULL;
}
}
if (in) {
in->chunkId = -1;
in->valid = 1;
in->variantType = type;
in->yst_mode = mode;
#ifdef CONFIG_YAFFS_WINCE
yfsd_WinFileTimeNow(in->win_atime);
in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
#else
in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
in->yst_rdev = rdev;
in->yst_uid = uid;
in->yst_gid = gid;
#endif
in->nDataChunks = 0;
yaffs_SetObjectName(in, name);
in->dirty = 1;
yaffs_AddObjectToDirectory(parent, in);
in->myDev = parent->myDev;
switch (type) {
case YAFFS_OBJECT_TYPE_SYMLINK:
in->variant.symLinkVariant.alias = str;
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
in->variant.hardLinkVariant.equivalentObject =
equivalentObject;
in->variant.hardLinkVariant.equivalentObjectId =
equivalentObject->objectId;
list_add(&in->hardLinks, &equivalentObject->hardLinks);
break;
case YAFFS_OBJECT_TYPE_FILE:
case YAFFS_OBJECT_TYPE_DIRECTORY:
case YAFFS_OBJECT_TYPE_SPECIAL:
case YAFFS_OBJECT_TYPE_UNKNOWN:
/* do nothing */
break;
}
if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) {
/* Could not create the object header, fail the creation */
yaffs_DestroyObject(in);
in = NULL;
}
}
return in;
}
Linuxカーネルのキャラクタデバイスはデバイスのクラスから抽象化され、Linuxカーネル用のデバイスドライバは、フレーム内で実施されなければならないキャラクタデバイスドライバを書くときにLinuxプログラマーを駆動し、ドライバの製造のためのフレームワークを提供します。
- カーネルの抽象文字デバイスの
データ構造から抽象化のすべての文字の中核機器、彼らはキャラクタデバイスのコアを表すエンティティのデータ構造:
----------------- -------------------------------------------------- -------------------------
構造体CDEV {
たkobject KOBJ ;. //埋め込みカーネルオブジェクト構造体
構造体モジュールの所有者; //文字デバイスロケーションカーネルモジュールオブジェクトポインタ
file_operations構造体CONST OPS; //構造説明した装置は、文字の方法を達成することができるである
//重要な構造。
LIST_HEAD一覧ストラクト; //既にコアに登録すべての文字デバイスリストを形成する。
たdev_t devの; //文字デバイスのデバイス番号、メジャーとマイナー番号で構成される。
unsigned int型のCOUNT; //は、マスタデバイス番号番号の同じサブ番号に属しています。
};
構造体CDEVインタフェースは、操作コア構造は、以下の主要な分析があります
ボイドcdev_init(構造体CDEV 、file_operations構造体CONSTを);
この関数は、構造体の構造体CDEVの初期化を行うことが主であり、主な仕事は、(1)全体の構造がクリアされます;(2)自体を指すように初期化メンバリスト;(3)メンバーKOBJを初期化するステップと、(4)OPSメンバーを初期化します。
構造体CDEVの*のcdev_alloc(ボイド);
この関数は、主に構造を割り当てる構造体CDEVで、呼の初期化cdev_alloc後に必要(第四工程からなる初期化動作cdev_initの前にステップ3を実行し、すなわち、明示的に初期化を行う:. OPS = xxx_ops)。
上記の2つの初期化機能では、我々は所有者のメンバー、DEVのメンバーが表示されていない、初期のメンバーは頼りに、実際には、所有者のメンバーは、ドライバとカーネルモジュールとの間に密接な関係を反映しているがあり、構造体のモジュールはのための中核であります場合抽象化モジュール、メンバーは、明示的にTHIS_MODULEを= .ownerユーザ初期化によって、一般的な書き込みドライバに、デバイスを防止部材は、デバイスを使用している、デバイスに属する文字装置モジュールで具現化され得ますモジュールがアンロードされます。カウントのdevのメンバーとメンバーがcdev_add実効値に割り当てられます。
cdev_add int型(構造体CDEV ; p型、のdev_t devの、符号なしのCOUNT)
カーネル構造体CDEVによって正式な告知構造体CDEV構造登録するカーネル関数のp-代表キャラクタデバイスを使用する準備ができているし。(1)第一のデバイス番号DEV、及び装置(2)の数に関連付けられているデバイスの数:当然のことながら、2つのパラメータを提供することが必要。構造体CDEVのDEVのメンバーとカウントメンバーに直接割り当てられたこれら2つのパラメータ。
cdev_delボイド(構造体CDEVのP);
構造体CDEV構造をログオフするためにカーネル関数のカーネル構造体CDEVのことで、正式な通告その pが表す文字デバイスが使用されていません。
(2)devデバイス番号;インタフェースの上記の議論から、我々は、初期化と登録プロセス構造体CDEVのために、我々はいくつかのこと(1)構造体file_operations構造のポインタを提供する必要があることを発見した数回の(3)数デバイスの数。しかし、我々はまだ終わりでこれらの値を理解していない私たちは、これらの値を構築するために行くべきか、そしてどのように表しています!
デバイス番号
メジャーおよびマイナー番号組成のデバイス数のLinuxシステムは、対応するデバイスドライバと二次デバイス番号を検索する主要な数のLinuxカーネルドライバによって使用され、それが識別するために管理しますいくつかの同様の装置。したがって、デバイス番号(特にメジャー番号)は資源システムで、ユーザが必要とするとき、カーネルは、アプリケーションに登録する必要があり、カーネル、デバイス番号に償却されるべきではもはや使用されません。
:デバイス番号は、データ・タイプは、図の以下の内部実装で表すことができるのdev_tカーネル、示している
のdev_t(+ N-M)ビット:
メジャー(nビット)
マイナー(mビット)
単位のユーザーの行動を、我々は実際にジャンプを行ってはなりません以来、推測どのように多くのビットビットメジャーとマイナーの半分とのdev_tの実現を推測するべきではありません。カーネルはまた、マクロいくつかの便利な操作を提供してくれます:
MAJOR(DEV)/ MINOR(DEV):メジャーデバイス番号から抽出し、マイナー
のmkdev(メジャー、マイナー):メジャーとマイナーデバイス番号を通じて構築し
、我々は上記のマクロでデバイス番号を構築し、相関値を取得する必要があります。
上記の議論では、デバイスドライバを検索するために使用される主要な数は、マイナーデバイス番号が管理プログラムを駆動する同様のデバイスの数を識別するために述べました。しかし、これまでのところ、我々は唯一のあなたは、構造体を登録する際にCDEV構造は、パラメータを入力する必要があることを知っている:devのとメジャー番号はデバイスドライバを検索する方法です、我々はまだ理解していないカウント、マイナーデバイス番号は、管理対象のドライバを特定する方法でありますいくつかの類似のデバイス?私たちは!あなたが正しいデバイス番号を構築することができない、この真理を理解していない
、我々はカーネル空間のドライバでは、次の質問に答えなければならない、この質問に答えるために、ユーザ空間内のユーザがそれによって使用される方法ですか?答えのために、発表された続編に耳を傾けます!構造体file_operations -ハブドライバの
答えは文書化することです!Linuxで文字やブロックデバイスがファイルを抽象化され、デバイスの動作のためのユーザ空間は、ファイル操作のためであり、操作インタフェースは、ファイルI / Oインターフェースであり、我々は、これらのI / Oインタフェースのためにそれを推測することができドライバのカーネル空間に対応するコールは最終的に行くために呼び出されます。最終的にはつまり、これらのユーザ空間カーネル空間の誰フロントI / Oインタフェースのですか?
その答えは、体内の構造体structのfile_operationsのCDEVです。この構造は、所有者の部材以外の部材(意味や構造体CDEVで使用される所有者のメンバーと一致している)において定義され、残りのメンバ関数ポインタです。これは、構造体file_operationsの構造体のメンバは、Linuxアプリケーション・プログラミングの友人に精通しているであろう、なぜ既視感を持っている、もちろん、いくつかの名前が同じように見えるが、その引数が異なっています。すべての抽象化機能の構造は、キャラクタデバイスを持つことができ、制御および操作するためのデバイスを実現するために、これらの関数ポインタを達成するために、プログラマによって駆動されます。私たちは詳細な説明を行うことをするための第3、第6及びLDD他の章である構造を構築する方法についてのメンバーは、ブックを参照してください。
4。デバイスファイルの作成
先に述べたように、Linuxオペレーティングシステム上で動作し、文字やブロックデバイスは、実際にはファイルシステム上のファイルであり、そのようなファイルがデバイスファイルと呼ばれます。しかし、デバイス・ファイルは、「あなたは参照するか、それはそこにあります見ていない」ではありません!それは、それを作成するために私たちを必要とし、我々は十分な情報を提供しなければなりません。ここではLinux上のキャラクタデバイスのコマンドラインを作成することです。
mknod / DEV / demodev C 2 0
C意味はmknodは、デバイスファイル(または被呼デバイスノード)を作成することで上記のコマンドライン、コマンド「/ devの/ demodev」デバイスファイル名、作成は、それぞれの文字(B、ブロックデバイスである)、2と0でありますこれは、メジャーとマイナー番号を表します。コマンドラインパラメータから、私たちが漠然とデバイス番号はドライバに対応するユーザ空間とカーネルスペースのファイル操作のみ、あるいは線であってもよい張られます見ることができるようです。この質問を解決するために、のは、使用するカーネルのコマンド反応の原因を最終的にはカーネル空間でのライン、そしてどのようにメジャーとマイナーデバイス番号を見てみましょう。
既存の文書のいずれかのために、コアはデータ構造の種々の構造部材であるこれに対応する構造体のinode(/リナックス/ fs.hを含むように定義される)と呼ばれる構造を有しているが、文字デバイス用言葉は、本当に唯一の3メンバーを気にする必要があります。
dev_tのi_rdev; //デバイス番号デバイスファイル
構造体CDEVのi_cdev、キャラクタデバイス用//抽象カーネル、それがこのような構造であるデバイスのユーザ空間//ファイルが表示されます文字デバイスのカーネル空間でそれの妥当性を確認してください。
file_operationsのconstの構造体 i_fopを、デバイスファイルの//すべての機能を持つことができます。
ユーザーがのmknodコマンドスペースを呼び出すと、カーネル空間には、二つのことを行います:
(1)のinode構造エンティティを生成する;
(2)のinode構造の上記メンバーの初期作業を行います。
上記のメンバーの初期化については、我々は憶測を助けることができなかった、その初期化プロセスは、このようになります。構成のmknodコマンドで渡さi_rdevデバイス番号値、キーの残りの部分は、割り当てをi_cdevする方法ですi_cdevのメンバーであるためそこのstruct file_operations *メンバーでもあり、そしてこのメンバーは、私たちの建物のドライバの実装であるので、それはi_fopのiノードに直接割り当てる必要があります。そして限り、値に与えi_cdevを与えるとして、それは言うまでもないi_fop。それはそれから来i_cdev値ですか?他の利用可能な情報のデバイス番号に加えて、運転者のために、mknodを介してダウン通過した後。この機能は、友人をcdev_addどのように覚えて、すぐにカーネルはまたi_cdev機能を見つけるために、デバイス番号によって提供されなければならない、推測しなければなりません。この初期化処理が正常に完了されます!あなたはこれら上記、おめでとうを考える場合、あなたはこの記事を見ているプロセスは、彼の思考に参加した説明。
しかし、それはそんなに早くああ、すべての後に自己満足することはできません、何もなく、「人民元牙ビュー」のみ、ディ大人はまだ話していません!ハハ......
私たちはどのように問題の真実を見ている取ります!
デバイスドライバinit_special_inodeと密接な関係にMKNODカーネル実行過程におけるコール
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\n", mode, inode->i_sb->s_id,
inode->i_ino);
}
上記のコードを実装(青い部分)から、我々はi_rdevがRDEVに初期化、および構造ではmknodパラメータによってRDEVが来て、私たちは、これが同じであると仮定見ることができます。しかし、その後、私たちの驚きi_fopけど(def_chr_fopsは、以下に定義)def_chr_fopsを、&に初期化されるのではなく、私たちの驚きには、プロセス全体を検索して初期化i_cdevを見ていません。これは、最後にそれが起こったのかですか?今では、我々は他の法律はないとされている、上記の実装は明らかにi_cdevが初期化されていない、とi_fop初期化とデバイスの数がわずかな関係ではなく、初期設定値は、我々が達成file_operationsポインタ構造体ではない、私たちに語りました、iノード番号は、ああだけの機器をのみ保存されます!これはああを行う方法ですか?どのように我々は我々のドライバーああに呼び出すことができますか?友人は、落胆することはしないでください、ディ大人の経験は真実はいつものように長い間、私たちが保持できるように、光に来ることを教えてくれる!ハハ!それを破る次聞く......
const struct file_operations def_chr_fops = {
.open = chrdev_open,
.llseek = noop_llseek,
};
デバイスファイルの操作
Linuxのアプリケーション・プログラミング・友人に精通しているが存在しなければならないファイルに加えて、文書を操作するために知っておくべき、第一のハンドル追従動作で、オープンシステムコールを通じてファイルハンドルを取得する必要が場所を取ることができます。デバイスファイルの操作と同様の理由です。私たちは、おそらくオープンキャラクタデバイスファイルで動作し、いくつかの手がかりを見つけるように見える、我々は我々が推測しなければならないものを見ることができるように、彼らは完全なものではなく、完了するまでにはmknodを開くには、時間的に遅延します。さて、それの謎の層を明らかにするオープンシステムコールから始めましょう!
次のプロトタイプでのオープンシステムコールのCライブラリのヘッダファイル:
int型オープン(のconst char型のパス名、int型のフラグ、MODEをmode_t);
パス名により対応するiノードを見つけるためにファイルを開く必要があります間違いなく機能はありませんが(これが私たちのiノードデバイスノードを想定していますすでに)が存在します。inodeをあなた見つかったら、inodeはopenメソッドのi_fopメンバーと呼ばれる、キャラクタデバイスのために、chrdev_open関数を呼び出します。次のようにこの機能が実装されます。
--------------------------------------------- -----------------------------------------------
静的int型chrdev_open (iノード構造体のinodeは、ファイル構造体財投)を
{
CDEV構造体 P;
CDEV新しい新しい* = NULLをストラクト;
INT RET = 0。spin_lock(&cdev_lock); p = inode->i_cdev; if (!p) { struct kobject *kobj; int idx; spin_unlock(&cdev_lock); kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); if (!kobj) return -ENXIO; new = container_of(kobj, struct cdev, kobj); spin_lock(&cdev_lock); /* Check i_cdev again in case somebody beat us to it while we dropped the lock. */ p = inode->i_cdev; if (!p) { inode->i_cdev = p = new; list_add(&inode->i_devices, &p->list); new = NULL; } else if (!cdev_get(p)) ret = -ENXIO; } else if (!cdev_get(p)) ret = -ENXIO; spin_unlock(&cdev_lock); cdev_put(new); if (ret) return ret; ret = -ENXIO; filp->f_op = fops_get(p->ops); // 获取cdev的ops if (!filp->f_op) goto out_cdev_put; if (filp->f_op->open) { ret = filp->f_op->open(inode, filp); // 执行cdev的open if (ret) goto out_cdev_put; } return 0;
out_cdev_put:
cdev_put(P);
戻りRET;
}
次のように機能するプロセスです。
(1)(私たちが知る限り、まだそれに割り当てられた値を持っていなかった、とこれまでの空我々はmknode最初から、)iノードのi_cdevメンバーが空であるかどうかを判断します。
(2)空の場合、> i_rdev kobj_lookupの組み合わせによってcontainer_of対応inode-構造体CDEV構造を識別する。
(3)> i_cdevをinode-に割り当てられ見出さinode-> i_rdev構造体CDEV構造ポインタ(開きinodeが空ではない次回に注意)、次いで、構造体CDEV iノードリストを追加しました。
(4)受信chrdev_openによって送達i_fopの財投に割り当てinode-> f_op i_cdevメンバー。
inode-> i_cdev i_fopが空でない場合(5)、それはオープンメソッド呼び出しを指します。
上記のプロセスに対応して初期化プロセスのinode値は、我々は推測するプロセスを作成した後、オープンの最初の呼び出しであるinode-> i_cdev初期化推測のため、まだのうちビット、「デバイスファイルを作成します。」我々は右であり、inode->のために、我々はi_fopポインタ構造体CDEV構造のみ担当者に割り当てられているi_fop値iノード装置ノードは、&def_chr_fops(.open = chrdev_open)から作成されたと結論i_fop OPSの各オープン財投ファイル構造。
デバイスノードが作成されていない場合、最初のオープンと呼ばれ、その後、機能実行処理は、2つのステップ(4)〜(3)、(2)(1)行った後chrdev_openスキップあります。
この時点で、オープンは成功を返します!
:ユーザーのオープンスペースのために我々は、それがint型のハンドルを返し知っているし、それ以降のすべての操作は、読み出しや書き込みなど、ハンドルに従って行われる
ssize_tのリード(FD int型ボイド; bufは、size_tのCOUNT)
(int型ssize_tの書き込みFD、constの空 bufは、size_tのCOUNT);
直接のstruct file_operationsドライバが実現に実装され、これらの操作に対応するデバイスファイルの目的のために!私たちは今、再び問うことを、カーネルはfdで、対応する構造体file_operationsを見つける方法ですか?
空のファイルをユーザのファイルパス名を表し、そしてオープン機能は、FD、すなわち、複数のウィンドウが存在するファイルを操作することができる、オープンファイルの抽象化を表す返します。もちろん、これらは、ジョブのカーネルでサポートされなければならないので、カーネルのinode構造を持つファイルの代わりにスペースが、構造体のファイル構造では、開いているファイルを表します。各ユーザ空間ファイルのオープン、カーネルは、構造体のファイル構造のために生成されます。この構造体のメンバーは、2つだけのメンバーが多く存在している私たちの懸念のある
file_operations構造体のconst f_op;
無効 PRIVATE_DATA、
我々が見ることができるf_opメンバーは、割り当てられた値がinode-> i_cdev-> OPSで、chrdev_openに割り当てられ、そしてPRIVATE_DATA作用は、ドライバの実装において有用であろう使用に種々の方法との間でデータを通信するために使用されるf_opあります。
そのカーネルはユーザ空間に応じて、対応する構造体のファイル構造を見つける方法であることをfdは?実際には、プロセスごとに、リターンに(例えば、構造体task_structタスクとして)プロセスを表すカーネルデータ構造は、オープン・ファイル記述子のテーブル(task->ファイル- > fdt-> FD維持し 、構造体のファイル*のポインタの配列を)、プロセスで開いて、カーネルは、インデックスFDに取得する、すなわち、テーブルに新たに生成された構造体のファイルポインタのエントリである
[task->ファイル- > fdt-> FD FD] =財投;
従って他のファイル操作fdが非常に容易に対応する構造体のファイルに存在し、その後メソッド呼び出しを対応f_opすることができます。
オリジナル住所:http://blog.chinaunix.net/uid-25424552-id-3387451.html