Linux 中节点(inode)、设备(cdev)、驱动(ops)如何关联

inode与cdev结构体对比(片选)

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;
};

可能存在的联系

  1. inode中struct cdev *i_cdev;可能保存了设备(cdev)的指针
  2. inode\cdev 都有文件操作符:struct file_operations *fops;
  3. inode\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


调用顺序

  • 系统调用顺序
sys_mknod		${KERNEL_BOOT}/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);
            // 根据文件系统决定
                yaffs_mknod
  • yaffs2调用顺序
init_special_inode
	yaffs_FillInodeFromObject
		read_inode						// struct super_operations yaffs_super_ops = { .read_inode = yaffs_read_inode,
			iget
				yaffs_get_inode
					yaffs_get_inode
					yaffs_lookup
					yaffs_mknod			// struct file_operations yaffs_dir_inode_operations = { .mknod = yaffs_mknod
						yaffs_dir_inode_operations
						yaffs_mkdir
						yaffs_create
					yaffs_symlink

关于Yaffs2文件系统

static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, FS_REQUIRES_DEV);

static struct file_system_type yaffs2_fs_type = {
	.owner = THIS_MODULE,
	.name = "yaffs2",
	.get_sb = yaffs2_read_super,		// 重要入口
	.kill_sb = kill_block_super,
	.fs_flags = FS_REQUIRES_DEV,
};

static struct super_block *yaffs2_read_super(struct super_block *sb, void *data, int silent)
{
	return yaffs_internal_read_super(2, sb, data, silent);
}

static struct super_block *yaffs_internal_read_super(int yaffsVersion,
						     struct super_block *sb,
						     void *data, int silent)
{
	...
	sb->s_magic = YAFFS_MAGIC;
	sb->s_op = &yaffs_super_ops;
	...
}

关于yaffs2文件系统加载

fs_to_install
	register_filesystem(fsinst->fst);
		init_yaffs_fs	// 驱动加载入口
			module_init(init_yaffs_fs)

可见yaffs2文件系统也是以内核模块的形式加入内核的。

数据结构

/* file */
static struct file_operations yaffs_file_operations = {
/* file inode */
static struct file_operations yaffs_file_inode_operations = {
/* directory */
static struct file_operations yaffs_dir_operations = {
/* directory inode */
static struct file_operations yaffs_dir_inode_operations = {
	...
	.mknod = yaffs_mknod,
	...
}
/* symlink inode */
static struct inode_operations yaffs_symlink_inode_operations = {
/* super block */
static struct super_operations yaffs_super_ops = {
	...
	.read_inode = yaffs_read_inode,
	...
}

猜测,其他文件系统也需要填充这几个操作符。
对于目录类型的对象才存在mknod操作,即有:yaffs_dir_inode_operations.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);
	// 根据类型,创建相应yaffs_object
    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;
}

struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,
			      yaffs_Object * obj)
{
	struct inode *inode;

	if (!sb) {
		T(YAFFS_TRACE_OS,
		  (KERN_DEBUG "yaffs_get_inode for NULL super_block!!\n"));
		return NULL;

	}

	if (!obj) {
		T(YAFFS_TRACE_OS,
		  (KERN_DEBUG "yaffs_get_inode for NULL object!!\n"));
		return NULL;

	}

	T(YAFFS_TRACE_OS,
	  (KERN_DEBUG "yaffs_get_inode for object %d\n", obj->objectId));

	inode = iget(sb, obj->objectId);

	/* NB Side effect: iget calls back to yaffs_read_inode(). */
	/* iget also increments the inode's i_count */
	/* NB You can't be holding grossLock or deadlock will happen! */

	return inode;
}

linux-2.6.22.6\include\linux\fs.h

static inline struct inode *iget(struct super_block *sb, unsigned long ino)
{
	struct inode *inode = iget_locked(sb, ino);

	if (inode && (inode->i_state & I_NEW)) {
		sb->s_op->read_inode(inode);
		unlock_new_inode(inode);
	}

	return inode;
}

Documentation/filesystems/Locking
->read_inode() is not a method - it's a callback used in iget().

static struct super_block *yaffs_internal_read_super(int yaffsVersion,
						     struct super_block *sb,
						     void *data, int silent)
{
	...
	sb->s_magic = YAFFS_MAGIC;
	sb->s_op = &yaffs_super_ops;
	...
}

static struct super_operations yaffs_super_ops = {
	...
	.read_inode = yaffs_read_inode,
	...
}

static void yaffs_read_inode(struct inode *inode)
{
	/* NB This is called as a side effect of other functions, but
	 * we had to release the lock to prevent deadlocks, so
	 * need to lock again.
	 */

	yaffs_Object *obj;
	yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb);

	T(YAFFS_TRACE_OS,
	  (KERN_DEBUG "yaffs_read_inode for %d\n", (int)inode->i_ino));

	yaffs_GrossLock(dev);

	obj = yaffs_FindObjectByNumber(dev, inode->i_ino);

	yaffs_FillInodeFromObject(inode, obj);

	yaffs_GrossUnlock(dev);
}

static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object * obj)
{
	if (inode && obj) {


		/* Check mode against the variant type and attempt to repair if broken. */
 		__u32 mode = obj->yst_mode;
 		switch( obj->variantType ){
 		case YAFFS_OBJECT_TYPE_FILE :
 		        if( ! S_ISREG(mode) ){
 			        obj->yst_mode &= ~S_IFMT;
 			        obj->yst_mode |= S_IFREG;
 			}

 			break;
 		case YAFFS_OBJECT_TYPE_SYMLINK :
 		        if( ! S_ISLNK(mode) ){
 			        obj->yst_mode &= ~S_IFMT;
 				obj->yst_mode |= S_IFLNK;
 			}

 			break;
 		case YAFFS_OBJECT_TYPE_DIRECTORY :
 		        if( ! S_ISDIR(mode) ){
 			        obj->yst_mode &= ~S_IFMT;
 			        obj->yst_mode |= S_IFDIR;
 			}

 			break;
 		case YAFFS_OBJECT_TYPE_UNKNOWN :
 		case YAFFS_OBJECT_TYPE_HARDLINK :
 		case YAFFS_OBJECT_TYPE_SPECIAL :
 		default:
 		        /* TODO? */
 		        break;
 		}

		inode->i_ino = obj->objectId;
		inode->i_mode = obj->yst_mode;
		inode->i_uid = obj->yst_uid;
		inode->i_gid = obj->yst_gid;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
		inode->i_blksize = inode->i_sb->s_blocksize;
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))

		inode->i_rdev = old_decode_dev(obj->yst_rdev);
		inode->i_atime.tv_sec = (time_t) (obj->yst_atime);
		inode->i_atime.tv_nsec = 0;
		inode->i_mtime.tv_sec = (time_t) obj->yst_mtime;
		inode->i_mtime.tv_nsec = 0;
		inode->i_ctime.tv_sec = (time_t) obj->yst_ctime;
		inode->i_ctime.tv_nsec = 0;
#else
		inode->i_rdev = obj->yst_rdev;
		inode->i_atime = obj->yst_atime;
		inode->i_mtime = obj->yst_mtime;
		inode->i_ctime = obj->yst_ctime;
#endif
		inode->i_size = yaffs_GetObjectFileLength(obj);
		inode->i_blocks = (inode->i_size + 511) >> 9;

		inode->i_nlink = yaffs_GetObjectLinkCount(obj);

		T(YAFFS_TRACE_OS,
		  (KERN_DEBUG
		   "yaffs_FillInode mode %x uid %d gid %d size %d count %d\n",
		   inode->i_mode, inode->i_uid, inode->i_gid,
		   (int)inode->i_size, atomic_read(&inode->i_count)));

		switch (obj->yst_mode & S_IFMT) {
		default:	/* fifo, device or socket */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
			init_special_inode(inode, obj->yst_mode,
					   old_decode_dev(obj->yst_rdev));
#else
			init_special_inode(inode, obj->yst_mode,
					   (dev_t) (obj->yst_rdev));
#endif
			break;
		case S_IFREG:	/* file */
			inode->i_op = &yaffs_file_inode_operations;
			inode->i_fop = &yaffs_file_operations;
			inode->i_mapping->a_ops =
			    &yaffs_file_address_operations;
			break;
		case S_IFDIR:	/* directory */
			inode->i_op = &yaffs_dir_inode_operations;
			inode->i_fop = &yaffs_dir_operations;
			break;
		case S_IFLNK:	/* symlink */
			inode->i_op = &yaffs_symlink_inode_operations;
			break;
		}

		yaffs_InodeToObjectLV(inode) = obj;

		obj->myInode = inode;

	} else {
		T(YAFFS_TRACE_OS,
		  (KERN_DEBUG "yaffs_FileInode invalid parameters\n"));
	}

}

linux-2.6.22.6\fs\inode.c

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)\n",
		       mode);
}
EXPORT_SYMBOL(init_special_inode);

关于节点数据类型

需要判断节点类型,涉及到数据

struct kstat {
	u64		ino;
	dev_t		dev;
	umode_t		mode;
	unsigned int	nlink;
	uid_t		uid;
	gid_t		gid;
	dev_t		rdev;
	loff_t		size;
	...
};

其中,对于文件的类型和权限由传入的mode确定。
源码中具体值以8进制表示。

关于8进制

C/C++规定,一个数如果要指明它采用八进制,必须在它前面加上一个0,如:123是十进制,但0123则表示采用八进制。这就是八进制数在C、C++中的表达方法。

mode_t其实就是普通的unsigned int.
目前,st_mode使用了其低16bit. 0170000 => 1+ 3*5 = 16.

其中,最低的9位(0-8)是权限,9-11是id,12-15是类型。
具体定义如下:

S_IFMT   0170000 bitmask for the file type bitfields
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 fifo
S_ISUID 0004000 set UID bit
S_ISGID 0002000 set GID bit (see below)
S_ISVTX 0001000 sticky bit (see below)
S_IRWXU 00700     mask for file owner permissions
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070     mask for group permissions
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007     mask for permissions for others (not in group)
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permisson
S_IXOTH 00001 others have execute permission

当我们需要快速获得文件类型或访问权限时,最好的方法就是使用glibc定义的宏。
如:S_ISDIR,S_IRWXU等, 定义如下:
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
对于字符设备,

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,而rdev是由mknod中的参数构造而来的,这和我们设想的是一样的;但是接下来出乎我们意料的是i_fop却初始化为&def_chr_fops(def_chr_fops定义如下),而更出乎我们意料的是查遍了整个流程也没有看到i_cdev的初始化。这到底是怎么回事呢?事到如今,我们也是被无他法了,上面的实现明确的告诉我们,i_cdev并没有被初始化,而i_fop的初始化与设备号没有丁点关系而且初始化的值也不是我们所实现的那个struct file_operations指针,inode仅仅是保存了设备号而已啊!这可怎么办啊?我们怎么该调用到我们的驱动程序啊?朋友们请别泄气,狄大人的经验告诉我们,真相总是会水落石出的,只要我们能坚持住!哈哈!且听下回分解吧……

const struct file_operations def_chr_fops = {
       .open = chrdev_open,
       .llseek = noop_llseek,
};

Open

熟悉linux应用编程的朋友们应该都知道,要操作一个文件,除了该文件必须存在外,还需要先通过open系统调用去得到一个文件句柄,有了这个句柄后续的操作才能进行。对于设备文件的操作也是同样的道理。于是我们似乎又可以找到一些线索了,也许在字符设备文件的open操作中,我们能够看到我们曾经猜测的东西,它们没有在mknod中被完成而是延后到了open的时候来完成了。好吧,让我们从open系统调用开始来揭开这层层的谜团吧!
open系统调用在C库头文件中的原型如下:
int open(const char *pathname, int flags, mode_t mode);
毫无疑问open函数必须通过pathname去找到该文件对应的inode(这里假设我们的inode设备节点已经存在)。找到inode节点后,将调用inode里i_fop成员的open方法,对于字符设备而言,将调用chrdev_open函数。该函数实现如下:


static int chrdev_open(struct inode *inode, struct file *filp)
{
       struct cdev *p;
       struct cdev *new = 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);
       return ret;
}

该函数流程如下:
(1) 判断inode的i_cdev成员是否为空(据我们所知,从我们mknode开始到现在还没有谁给它赋过值,因此到目前为止还是空的)。
(2) 如果为空,将通过kobj_lookup与container_of的组合找出inode->i_rdev所对应的struct cdev结构。
(3) 将通过inode->i_rdev查找到的struct cdev结构指针赋值给inode->i_cdev(注意下次open时inode将不为空),然后将inode加入struct cdev链表中。
(4) 将inode->i_cdev中的i_fop赋值给由chrdev_open传递进来的filp的f_op成员。
(5) 如果inode->i_cdev中的i_fop不为空,则调用其指向的open方法。

以上流程是对应创建设备节点后第一次调用open的流程,该流程与我们在“设备文件的创建”一节中猜测的inode值的初始化过程还是有点出入的,对于inode->i_cdev初始化的猜测,我们是正确的,而对于inode->i_fop我们得出的结论是设备节点的inode的i_fop值从创建后一直是&def_chr_fops(.open= chrdev_open), 而struct cdev结构的i_fop指针只是赋给了代表每个打开文件的filp结构中的ops。
如果open不是创建设备节点后第一次被调用,则chrdev_open函数的执行流程是执行完(1)后直接跳过(2)、(3)两个步骤到(4)。
至此,open成功返回!
对于用户空间的open我们知道它返回一个int型的句柄,而后续的所有操作都是根据该句柄进行的,如read和write:
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
对于设备文件而言这些操作直接对应于驱动程序实现的struct file_operations里的实现!那我们现在的问题又来了,内核是怎么通过fd找到与其对应的struct file_operations的?
在用户空用文件路径名代表着一个文件,而open函数返回的fd则代表着一个打开文件的抽象,即对于一个文件可以同时存在对其进行操作的多个窗口。当然这些都必须得到内核的支持才行,因此在内核空间用一个inode结构代表一个文件,而用struct file结构代表一个打开的文件。用户空间每open一个文件,内核都会为其生成一个struct file结构。该结构的成员也不少,这里只有两个成员是我们关心的
const struct file_operations *f_op;
void *private_data;
f_op成员我们可以看到在chrdev_open被赋值了,赋的值是inode->i_cdev->ops;而private_data的作用则是用于f_op里各个方法间传递数据用的,这在驱动程序的实现过程中将会非常有用。
那内核是如何根据用户空间的fd找到其对应的struct file结构的呢?其实对于每个进程而言,内核中表示一个进程的数据结构(如struct task_struct task)里回维护一张打开的文件描述符表(task->files->fdt->fd,struct file *指针数组),在open的过程中,内核会以得到的fd为下标,将新生成的struct file的指针填入表中,即
task->files->fdt->fd[fd] = filp;
因此以fd的其他文件操作可以很轻易的找到其对应的struct file,进而调用f_op对应的方法。
Open 段参考,文章:http://blog.chinaunix.net/uid-25424552-id-3387451.html

你可能感兴趣的:(Linux)