精华内容
下载资源
问答
  • Open CAS通过将活动(热)数据缓存到服务器内部的本地闪存设备来加速Linux应用程序。 Open CAS在服务器级别实现缓存,它利用本地高性能闪存介质作为应用服务器内尽可能靠近CPU的缓存驱动器介质,从而尽可能减少存储...
  • Pentium III FXSR
  • BroadVoice® is a family of speech coding algorithms created by Broadcom and standardized by CableLabs®
  • BTL Linux open source
  • 本文主要参考《深入理解Linux内核》,结合2.6.11.1版的内核代码,分析内核文件子系统中的打开设备文件函数,梳理了关于内核块...3、源码摘自Linux内核2.6.11.1版 1、open_bdev_excl 函数功能: 打开设备名为path的

    本文主要参考《深入理解Linux内核》,结合2.6.11.1版的内核代码,分析内核文件子系统中的打开设备文件函数,梳理了关于内核块设备文件打开的处理流程。

    注意:

    1、不描述内核同步、错误处理相关的内容

    2、参考信息除具体说明外,包含在《深入理解Linux内核》第三版中

    3、源码摘自Linux内核2.6.11.1版

    1、open_bdev_excl

    函数功能:

    打开设备名为path的块设备,返回块设备描述符的地址

    函数参数:

    Path:块设备文件的路径名(如:/dev/sda1);

    Flags:mount系统调用的的flag参数

    Holder: 指向类型为file_system_type的文件系统类型对象的指针

    函数源码及处理流程:

    struct block_device*open_bdev_excl(const char *path, int flags, void *holder)

    {

        structblock_device *bdev;

        mode_tmode = FMODE_READ;

        interror = 0;

     

        bdev= lookup_bdev(path);

        if(IS_ERR(bdev))

           returnbdev;

     

        if(!(flags & MS_RDONLY))

           mode|= FMODE_WRITE;

        error= blkdev_get(bdev, mode, 0);

        if(error)

           returnERR_PTR(error);

        error= -EACCES;

        if(!(flags & MS_RDONLY) && bdev_read_only(bdev))

           gotoblkdev_put;

        error= bd_claim(bdev, holder);

        if(error)

           gotoblkdev_put;

     

        returnbdev;

       

    blkdev_put:

        blkdev_put(bdev);

        returnERR_PTR(error);

    }

    函数处理流程:

    1、调用lookup_bdev函数,根据设备文件名path,查找或分配一个block_device对象,地址存入局部变量bdev中

    2、根据flags参数设置文件系统权限为只读(FMODE_READ)或读写(FMODE_READ| FMODE_WRITE)

    3、调用blkdev_get函数,初始化bdev中和分区、磁盘相关的数据,参见后面分析

    4、调用bd_claim函数,更新bdev对象和其包含对象的持有者信息

     

    2、lookup_bdev

    函数功能:

    打开或查找设备名为path的块设备。

    函数参数:

    函数源码:

    /**

     * lookup_bdev - lookup a struct block_device by name

     *

     * @path:  specialfile representing the block device

     *

     * Get a reference to the blockdevice at @pathin the current

     * namespace if possible and return it.  Return ERR_PTR(error)

     * otherwise.

     */

    struct block_device *lookup_bdev(constchar *path)

    {

        structblock_device *bdev;

        structinode *inode;

        structnameidata nd;

        interror;

     

        if(!path || !*path)

           returnERR_PTR(-EINVAL);

     

        error= path_lookup(path, LOOKUP_FOLLOW, &nd);

        if(error)

           returnERR_PTR(error);

     

        inode= nd.dentry->d_inode;

        error= -ENOTBLK;

        if(!S_ISBLK(inode->i_mode))

           gotofail;

        error= -EACCES;

        if(nd.mnt->mnt_flags & MNT_NODEV)

           gotofail;

        error= -ENOMEM;

        bdev= bd_acquire(inode);

        if(!bdev)

           gotofail;

    out:

        path_release(&nd);

        returnbdev;

    fail:

        bdev= ERR_PTR(error);

        gotoout;

    }

    函数处理流程:

    1、调用path_lookup函数(参见p495“路径名查找”),查找块设备文件名对应的nameidata对象并存入nd局部变量中,设备文件对应的节点对象地址存入inode类型的inode局部变量中

    2、调用bd_acquire函数,打开或查找设备文件名为path的块设备,返回块设备描述符

    3、blkdev_get

    函数功能:

    初始化块设备描述符中和分区、磁盘相关的字段

    函数参数:

    函数源码:

    int blkdev_get(struct block_device*bdev, mode_t mode, unsigned flags)

    {

        /*

         * This crockload is due to bad choice of->open() type.

         * It will go away.

         * For now, block device ->open() routinemust _not_

         * examine anything in 'inode' argument except->i_rdev.

         */

        structfile fake_file = {};

        structdentry fake_dentry = {};

        fake_file.f_mode= mode;

        fake_file.f_flags= flags;

        fake_file.f_dentry= &fake_dentry;

        fake_dentry.d_inode= bdev->bd_inode;

     

        returndo_open(bdev, &fake_file);

    }

     

    static int do_open(struct block_device*bdev, struct file *file)

    {

        structmodule *owner = NULL;

        structgendisk *disk;

        intret = -ENXIO;

        intpart;

     

        file->f_mapping= bdev->bd_inode->i_mapping;

        lock_kernel();

        disk= get_gendisk(bdev->bd_dev, &part);

        if(!disk) {

           unlock_kernel();

           bdput(bdev);

           returnret;

        }

        owner= disk->fops->owner;

     

        down(&bdev->bd_sem);

        if(!bdev->bd_openers) { //第一次打开

           bdev->bd_disk= disk;

           bdev->bd_contains= bdev;

           if(!part) { //如果不是分区,是磁盘

               structbacking_dev_info *bdi;

               if(disk->fops->open) {

                  ret= disk->fops->open(bdev->bd_inode, file);

                  if (ret)

                      gotoout_first;

               }

               if(!bdev->bd_openers) { //第一次打开

                  bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);

                  bdi= blk_get_backing_dev_info(bdev); //磁盘的IO数据流量的信息,如预读和请求队列拥塞状态的信息

                  if(bdi == NULL)

                      bdi= &default_backing_dev_info;

                  bdev->bd_inode->i_data.backing_dev_info= bdi;

               }

               if(bdev->bd_invalidated)

                  rescan_partitions(disk,bdev);

           }else { //如果是分区

               structhd_struct *p;

               structblock_device *whole;

               whole= bdget_disk(disk, 0);

               ret= -ENOMEM;

               if(!whole)

                  gotoout_first;

               ret= blkdev_get(whole, file->f_mode, file->f_flags);//对磁盘递归了

               if(ret)

                  gotoout_first;

               bdev->bd_contains= whole; //包含分区的块设备

               down(&whole->bd_sem);

               whole->bd_part_count++;//块设备中的打开的分区数

               p= disk->part[part - 1];//分区表描述符

               bdev->bd_inode->i_data.backing_dev_info=

                  whole->bd_inode->i_data.backing_dev_info;磁盘的IO数据流量的信息,如预读和请求队列拥塞状态的信息

               if(!(disk->flags & GENHD_FL_UP) || !p || !p->nr_sects) {//磁盘将被初始化、分区描述符为空、分区中的扇区数为0

                  whole->bd_part_count--;

                  up(&whole->bd_sem);

                  ret= -ENXIO;

                  gotoout_first;

               }

               kobject_get(&p->kobj);

               bdev->bd_part= p;

               bd_set_size(bdev,(loff_t) p->nr_sects << 9);

               up(&whole->bd_sem);

           }//endfor else { //如果是分区

        }else { //if (!bdev->bd_openers)

           put_disk(disk);

           module_put(owner);

           if(bdev->bd_contains == bdev) { //不是分区

               if(bdev->bd_disk->fops->open) {

                  ret= bdev->bd_disk->fops->open(bdev->bd_inode, file);

                  if(ret)

                      gotoout;

               }

               if(bdev->bd_invalidated)

                  rescan_partitions(bdev->bd_disk,bdev);

           }else { //是分区

               down(&bdev->bd_contains->bd_sem);

               bdev->bd_contains->bd_part_count++;

               up(&bdev->bd_contains->bd_sem);

           }

        }

        bdev->bd_openers++;

        up(&bdev->bd_sem);

        unlock_kernel();

        return0;

     

    out_first:

        bdev->bd_disk= NULL;

        bdev->bd_inode->i_data.backing_dev_info= &default_backing_dev_info;

        if(bdev != bdev->bd_contains)

           blkdev_put(bdev->bd_contains);

        bdev->bd_contains= NULL;

        put_disk(disk);

        module_put(owner);

    out:

        up(&bdev->bd_sem);

        unlock_kernel();

        if(ret)

           bdput(bdev);

        returnret;

    }

    函数处理流程:

    从源码可以看出,blkdev_get把相关参数放入file对象后,调用do_open函数完成具体的处理工作,do_open函数的处理流出如下:

    1、把file的地址空间初始化为块设备在bdev文件系统中的索引节点的地址空间

    2、根据块设备文件的标识符,调用函数get_gendisk返回类型为gendisk磁盘描述符和块设备对应的分区索引(从1开始,0表示打开的块设备是一个磁盘)

    3、剩下的处理流出参见代码注释

    4、bd_claim

    函数功能:

    更新bdev对象和其包含对象的持有者信息

    函数参数:

    函数源码:

    int bd_claim(struct block_device *bdev,void *holder)

    {

        intres;

        spin_lock(&bdev_lock);

     

        /*first decide result */

        if(bdev->bd_holder == holder)

           res= 0;   /* already a holder */

        elseif (bdev->bd_holder != NULL)

           res= -EBUSY;      /* held by someone else */

        elseif (bdev->bd_contains == bdev)

           res= 0;   /* is a whole device which isn't held */

     

        elseif (bdev->bd_contains->bd_holder == bd_claim)

           res= 0;    /* is a partition of a device that is beingpartitioned */

        elseif (bdev->bd_contains->bd_holder != NULL)

           res= -EBUSY; /* is a partition of a helddevice */

        else

           res= 0;   /* is a partition of an un-helddevice */

     

        /*now impose change */

        if(res==0) {

           /*note that for a whole device bd_holders

            * will be incremented twice, and bd_holderwill

            * be set to bd_claim before being set toholder

            */

           bdev->bd_contains->bd_holders++;

           bdev->bd_contains->bd_holder= bd_claim;

           bdev->bd_holders++;

           bdev->bd_holder= holder;

        }

        spin_unlock(&bdev_lock);

        returnres;

    }

     

    函数处理流程:

    从代码可见,在两种情况下(1、bdev->bd_holder不等于holder且不为空时,即bdev 有其它持有者2、bdev->bd_contains->bd_holder不等于bd_claim且不为空时,即bdev->bd_contains的持有者不为bd_claim)返回错误;在三种情况下(1、bdev->bd_holder为holder,即bdev的持有者再次持有2、bdev->bd_contains等于bdev,即bdev是整个磁盘描述符且没有持有者3、bdev->bd_contains->bd_holder等于bd_claim)

     

    5、bd_acquire

    函数功能:

    打开或查找设备文件名为path的块设备,返回块设备描述符。

    函数参数:

    inode:块设备文件的路径名的设备节点。

    函数源码:

    static struct block_device*bd_acquire(struct inode *inode)

    {

        structblock_device *bdev;

        spin_lock(&bdev_lock);

        bdev= inode->i_bdev;

        if(bdev && igrab(bdev->bd_inode)) {

           spin_unlock(&bdev_lock);

           returnbdev;

        }

        spin_unlock(&bdev_lock);

        bdev= bdget(inode->i_rdev);

        if(bdev) {

           spin_lock(&bdev_lock);

           if(inode->i_bdev)  //如果块设备文件对应的索引节点已与另外一个块设备描述符相关联,消除关联

               __bd_forget(inode);

           inode->i_bdev= bdev; //块设备描述符

           inode->i_mapping= bdev->bd_inode->i_mapping; //块设备在bdev文件系统中的索引节点的地址空间对象的地址

           list_add(&inode->i_devices,&bdev->bd_inodes); //把索引节点加入块设备描述符的索引节点链表

           spin_unlock(&bdev_lock);

        }

        returnbdev;

    }

    函数处理流程:

    1、把inode的i_bdev存放在类型为block_device的局部变量bdev中,如果bdev不为NULL(即块设备文件已经被打开),通过调用函数igrab增加bdev->bd_inode索引节点的引用计数器,并返回bdev

    2、如果bdev等于NULL或该节点已被释放(igrab返回零值),调用bdget函数获取块设备描述符的地址,具体流程参见下面描述

    3、如果成功获取块设备描述符(bdev不等于NULL),处理流出参见代码注释

     

    6、bdget

    函数功能:

    根据块设备文件对应的设备标识符,在bdev文件系统中查找或新建一个索引节点、块设备描述符,返回块设备描述符的地址

    函数参数:

    dev:块设备文件对应的设备标识符(含主设备号和次设备号)

    函数源码:

    struct block_device *bdget(dev_t dev)

    {

        structblock_device *bdev;

        structinode *inode;

        inode= iget5_locked(bd_mnt->mnt_sb, hash(dev),

               bdev_test,bdev_set, &dev);

        if(!inode)

           returnNULL;

        bdev= &BDEV_I(inode)->bdev;

        if(inode->i_state & I_NEW) {

           bdev->bd_contains= NULL; // bdev是特殊块设备文件系统的超级块,所以该对象初始化为NULL

           bdev->bd_inode= inode; //bdev文件系统中的索引节点

           bdev->bd_block_size= (1 << inode->i_blkbits); //块的字节大小,默认是1024,参见函数get_sb_pseudo

           bdev->bd_part_count= 0; //分区被打开的次数

           bdev->bd_invalidated= 0; //需要读块设备的分区表时设置的标志

           inode->i_mode= S_IFBLK; //文件类型与访问权限,块设备

           inode->i_rdev= dev;  //块设备文件标识符

           inode->i_bdev= bdev; //块设备描述符

           inode->i_data.a_ops= &def_blk_aops; //bdev块设备文件地址空间的默认操作函数

           mapping_set_gfp_mask(&inode->i_data,GFP_USER);//设置bdev文件节点的页分配标志,参见p305

           inode->i_data.backing_dev_info= &default_backing_dev_info;

           spin_lock(&bdev_lock);

           list_add(&bdev->bd_list,&all_bdevs); //把块设备描述符插入块设备描述符全局链表

           spin_unlock(&bdev_lock);

           unlock_new_inode(inode);

        }

        returnbdev;

    }

    函数处理流程:

    1、vfsmount*类型的全局变量bd_mnt中,存放类型名为”bdev”的块设备文件系统类型的安装文件系统描述符的地址,调用函数iget5_locked获得与块设备文件标识符对应的在bd_mnt文件系统中的索引节点的地址,存入局部变量inode中,函数的具体分析参加下面具体描述

    2、通过宏BDEV_I从inode导出块设备描述符的地址,存入bdev局部变量中

    3、如果inode是调用iget5_locked时新分配的索引节点,初始化bdev和inode相关信息,具体信息参见代码注释

     

    7、iget5_locked

    函数功能:

     

    函数参数:

    sb:

     

    函数源码:

    struct inode *iget5_locked(structsuper_block *sb, unsigned long hashval,

           int(*test)(struct inode *, void *),

           int(*set)(struct inode *, void *), void *data)

    {

        structhlist_head *head = inode_hashtable + hash(sb, hashval);

        structinode *inode;

     

        inode= ifind(sb, head, test, data);

        if(inode)

           returninode;

        /*

         * get_new_inode() will do the right thing,re-trying the search

         * in case it had to block at any point.

         */

        returnget_new_inode(sb, head, test, set, data);

    }

    函数处理流程:

    调用函数ifind从inode_hashtable中查找inode,找到则返回

    未找到调用函数get_new_inode

     

    8、get_new_inode

    函数功能:

     

    函数参数:

     

    函数源码:

    /*

     * This is called without the inode lock held..Be careful.

     *

     * We no longer cache the sb_flags in i_flags -see fs.h

     *  --rmk@arm.uk.linux.org

     */

    static struct inode *get_new_inode(struct super_block *sb, struct hlist_head *head, int(*test)(struct inode *, void *), int (*set)(struct inode *, void *), void*data)

    {

        structinode * inode;

     

        inode= alloc_inode(sb);

        if(inode) {

           structinode * old;

     

           spin_lock(&inode_lock);

           /*We released the lock, so.. */

           old= find_inode(sb, head, test, data);

           if(!old) {

               if(set(inode, data))

                  gotoset_failed;

     

               inodes_stat.nr_inodes++;

               list_add(&inode->i_list,&inode_in_use);

               list_add(&inode->i_sb_list,&sb->s_inodes);

               hlist_add_head(&inode->i_hash,head);

               inode->i_state= I_LOCK|I_NEW;

               spin_unlock(&inode_lock);

     

               /*Return the locked inode with I_NEW set, the

                * caller is responsible for filling in thecontents

                */

               returninode;

           }

     

           /*

            * Uhhuh, somebody else created the same inodeunder

            * us. Use the old inode instead of the one wejust

            * allocated.

             */

           __iget(old);

           spin_unlock(&inode_lock);

           destroy_inode(inode);

           inode= old;

           wait_on_inode(inode);

        }

        returninode;

     

    set_failed:

        spin_unlock(&inode_lock);

        destroy_inode(inode);

        returnNULL;

    }

    函数处理流程:

    1、调用alloc_inode函数分配一个新的索引节点,地址存入局部变量inode中

    2、获取锁后,调用find_inode函数在inode_hashtable中搜索块设备文件对应的节点,如果未找到则没有其他进程打开该设备文件,调用set初始化块设备描述符的设备标识符字段,把该设备索引节点加入inode_in_use和超级块的索引节点链表,设置索引节点的I_LOCK和I_NEW标识并返回

    3、如果已有其他进程打开了该设备文件,增加已打开的索引节点引用计数器,并释放新分配的索引节点,返回找到的索引节点地址

     

    9、alloc_inode

    函数功能:

     

    函数参数:

    sb:“bdev”块设备的超级块对象指针

    函数源码:

    static struct inode *alloc_inode(structsuper_block *sb)

    {

        staticstruct address_space_operations empty_aops;

        staticstruct inode_operations empty_iops;

        staticstruct file_operations empty_fops;

        structinode *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) {

           structaddress_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));

               returnNULL;

           }

     

           mapping->a_ops= &empty_aops; //文件地址空间操作函数

           mapping->host = inode;   //文件地址空间持有者

           mapping->flags= 0;  //错误位和内存分配器的标志

           mapping_set_gfp_mask(mapping,GFP_HIGHUSER);//初始化分配器标志,参见p305

           mapping->assoc_mapping= NULL;//指向间接块所在块设备的address_space对象的地址

           mapping->backing_dev_info= &default_backing_dev_info;

     

           /*

            * If the block_device provides abacking_dev_info for client

             *inodes then use that.  Otherwise theinode share the bdev's

            * backing_dev_info.

            */

           if(sb->s_bdev) {

               structbacking_dev_info *bdi;

     

               bdi= sb->s_bdev->bd_inode_backing_dev_info;

               if(!bdi)

                  bdi= sb->s_bdev->bd_inode->i_mapping->backing_dev_info;

               mapping->backing_dev_info= bdi;

           }

           memset(&inode->u,0, sizeof(inode->u));

           inode->i_mapping= mapping;

        }

        returninode;

    }

    函数处理流程:

    1、如果超级块的操作包含alloc_inode操作函数,调用该函数分配一个索引节点对象,对”bdev”文件系统来说,实现该指针的函数是bdev_alloc_inode,该函数从slab高速缓存bdev_cachep中分配一个bdev_inode对象,并返回其包含的inode对象的地址;如果不包含,从inode_cachep slab高速缓存中分配一个索引节点对象;把对象的地址存入局部变量inode中

    2、初始化inode和其address_space对象,具体描述参见代码注释

    展开全文
  • linuxopen,write源码何在?

    千次阅读 2013-12-29 22:45:24
    linux下面的open和write源码在哪来,当然这个源码就是glibc,glibc在编译器中直接以库形式面向用户。 那简单的open其实也会有一个执行过程,在glibc里面有他的源码,最终是做一个SWI软中断的汇编执行过程,调用...

    linux下面的open和write源码在哪来,当然这个源码就是glibc,glibc在编译器中直接以库形式面向用户。

    那简单的open其实也会有一个执行过程,在glibc里面有他的源码,最终是做一个SWI软中断的汇编执行过程,调用寄存器。这个过程执行完后,就触发了内核进行系统调用sys_xxx的执行。故glibc里面为系统调用创建条件,一个SWI触发系统调用。

    展开全文
  • linux内核源码下载

    千次阅读 2019-05-14 18:08:15
    linux kernel open source code download ftp: http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/ 官网: https://www.kernel.org/
    展开全文
  • 近期在看Linux内核相关,且之前面试有遇到问**“open函数调用原理”**的问题,今天在这里做一下总结记录。 open函数 open函数主要包含以下两类: int open(const char *pathName, int flags); // 打开文件 int open...

    理论是灰色的,实践之树长青🌲 ——恩格斯


    前述

    近期在看Linux内核相关,且之前面试有遇到问**“open函数调用原理”**的问题,今天在这里做一下总结记录。

    open函数

    open函数主要包含以下两类:

    int open(const char *pathName, int flags); // 打开文件
    int open(const char *pathName, int flags, mode_t mode); // 创建文件

    open函数参数介绍:

    • pathname:表示要打开的文件路径。
    • flags:用于指示打开文件的选项,常用的有O_RDONLY、O_WRONLY和O_RDWR。这三个选项必须有且只能有一个被指定。为什么O_RDWR!=O_RDONLY|O_WRONLY呢?Linux环境中,O_RDONLY被定义为0,O_WRONLY被定义为1,而O_RDWR却被定义为2。除了以上三个选项,Linux平台还支持更多的选项,APUE中对此也进行了介绍。
    • ·mode:只在创建文件时需要,用于指定所创建文件的权限位(还要受到umask环境变量的影响)。

    open操作流程

    open函数就是打开一个文件,在操作系统中就是确定进程操作哪个文件,这个过程主要由两件事构成:

    1. 将用户进程 task_struct 中的filp[20] 与内核中的file_table[64] 进行挂接。
    2. 将用户进程需要打开的文件对应的inode节点在file_table[64] 中进行登记。

    task_struct :进程结构
    // 硬编码部分(一般不做更改)
    long state;
    long counter;
    long priority;
    long signal;
    struct sigaction sigaction[32];
    long blocked; // 信号的位图
    // 各种类型字段
    int exit_code;
    unsigned long start_code, end_code, end_data, brk, start_stack;
    long pid, father, pgrp, session, leader;
    unsigned short uid, euid, suid;
    unsigned short gid, egid, sgid;
    long alarm;
    long utime, stime, cutime, cstime, start_time;
    unsigned short used_match;
    // 文件系统信息
    int tty;
    unsigned short umask;
    struct m_inode * pwd;
    struct m_inode * root;
    struct m_inode * executable;
    unsigned long close_on_exec;
    struct file * filp[NR_OPEN]
    // tss字段
    struct tss_struct tss;

    • 操作系统根据用户进程的需求来操作文件,内核通过 *filp[20] 掌控每一个进程可以打开的文件,既可以打开多个不同的文件,也可以多次打开同一个文件,每打开一次文件(不论是否是同一个文件),就要在 *filp[20] 占用一个项记录指针,所以每一个进程可以同时打开文件的次数不能超过20次;
    • 操作系统中的file_table[64] 是管理所有进程打开文件的数据结构, 不但记录了不同进程打开不同的文件,也记录了不同进程打开同一个文件,甚至记录了同一个进程多次打开一个文件。与filp[20] 类似,只要打开一次文件,就要在file_table[64] 中记录;
    • 文件中的i节点是记载文件属性的最关键的数据结构,在操作系统中i节点和文件是一一对应的,找到i节点,就意味着找到唯一的文件。内核通过inode_table[32] 掌控正在使用的文件i节点,每个被使用的文件i节点都要记录在其中。

    所以打开文件的本质就是要建立 *filp[20] file_table[64] inode_table[32] 三者之间的关系。

    在这里插入图片描述

    open源码

    打开文件的具体操作是在进程中调用open()函数实现,该函数最终映射到sys_open()系统调用函数执行,接下来我们看一下该函数的源码。

    long do_sys_open(int dfd, const char _user *filename, int flags, int mode){
    	struct open_flags op;
    	// flags为用户层传递的参数,内核会对flags进行合法性检查,根据mode生成新的flags赋值给lookup
    	int lookup = build_open_flags(flags, mode, &op);
    	// 将用户空间的文件名复制到内核空间
    	char *tmp = getname(filename);
    	int fd = PTR_ERR(tmp);
    	if (!IS_ERR(tmp)){
    		// 未出错,申请新的文件描述符
    		fd = get_unused_fd_flags(flags);
    		if(fd >= 0){
    			// 申请新的文件管理文件结构file(file_table[64])
    			struct file *f = do_filp_open(dfd, tmp, &op, lookup);
    			if(IS_ERR(f)){
    				put_unused_fd(fd);
    				fd = PTR_ERR(fd);
    			} else {
    				// 打开文件的通知事件
    				fsnotify_open(f);
    				// 将文件描述符和文件管理结构file对应起来(file_table[64])
    				fd_install(fd, f);
    			}
    		}
    		putname(tmp);
    	}
    	return fd;
    }
    
    // 这里的struct file结构如下:
    struct file {
    	unsigned short f_mode;  // 文件操作模式
    	unsigned short f_flags;  //文件打开、控制标志
    	unsigned short f_count;  //文件句柄数
    	struct m_inode * f_inode;  //指向文件对应的i节点
    	off_t f_pos;   // 文件位置(读写偏移值) 
    }
    

    所以sys_open 函数返回的是文件描述符fd,当用户使用fd与内核交互时,内核可以用fd从fdt->fd[fd]中得到内部管理文件的结构struct file,然后通过i节点进一步获取到对应的文件;

    欢迎大家一起关注交流学习哈!
    个人GitHub:https://github.com/SpecialAll

    展开全文
  • linux3.0_1c300b:openloongson v3.0开发板开龙3.0主板的linux3.0源码
  • OpenVPN-Linux-kernel 在内核处理OpenVPN数据通道 控制通道依然在OpenVPN本身进行处理,数据通道被移植进了Linux内核。 增加了几个tun的ioctl命令,用来: 1.将一个UDP socket和tun连接起来,用于数据通道的短路操作...
  • OpenContrail Linux网络名称空间设置 该软件包包含两个脚本(netns-daemon-start,netns-daemon-stop),旨在用作从init.d之外启动的应用程序的启动前脚本和停止后脚本。 然后,应用程序本身应按以下方式执行: ...
  • OpenProject Docker 开发 Dockerfile 和 docker-compose 脚本,用于构建 OpenProject 的开发容器以及 PostgreSQL 9.4.X 服务器。 ✓ 使用 docker-compose 来分离 OpenProject 和 PostgreSQL / Data 容器。 ✓ ...
  • 操作系统:Linux x64 / Ubuntu 14.04 研究领域:软件定义网络SDN (Software-defined Networking) 开发组件:OpenDaylight 本文原文链接:...
  • GitHub Ubuntu 安裝 GNU Global(gtags) 阅读Linux内核源码 AderXCoding/system/tools/global 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作因本人技术...
  • OVR_SLDO OpenVR简单Linux桌面叠加
  • linux openssh源码安装

    2015-10-09 14:27:54
    linux openssh安装
  • eurocom openairinterface物理层源码
  • 元LMP Linux microPlatform项目使用的层的集合。
  • 我们将先根据Linux源码的目录结构进行分析,到本文章发布前,Linux 4.19的最新版本为Linux 4.19.94,我们将依据openEuler开源社区源码并参考Linux 4.19.94版内核源码进行分析。 一、Linux内核源码的目录结构分析 .....
  • toolbox for java t open
  • linux c源码之top源码

    万次阅读 2015-02-03 18:16:59
    * Copyright (c) 2008, The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that
  • Linux pipe 源码分析

    千次阅读 2015-03-20 17:13:38
    Linux pipe 源码分析  管道pipe作为Unix中历史最悠久的IPC机制,存在各个版本的Unix中,主要用于父子进程之间的通信(使用fork,从而子进程会获得父进程的打开文件表),pipe()系统调用底层的实现就相当于一个...
  •  软件资源:qt-embedded-linux-opensource-src-4.5.1.tar.gz  qt-x11-opensource-src-4.5.1.tar.gz  因为一般嵌入式设备上都是在framebuffer的方式,在X11上就是用qvfb 来模拟framebuffer的,我们平时开发也都...
  • 开放式 Linux 强化检查 (OLHC) 帮助 Linux 系统安全检查的工具。 OLHC 是一个对 Linux 操作系统进行多项安全检查的框架。 该框架主要写入中央“平面文件”日志记录结构。 logstash 实现可用于对大量 Linux 服务器...
  • 使用 Source Insight 阅读 Linux 内核源码

    千次阅读 多人点赞 2020-09-23 09:37:40
    如果下次你想打开工程,启动 Souce Insight 后,点击菜单“Project -> Open Porject”就可以在一个列表中选择以前建立的工程,如下图: 在工程中打开文件 点击"P"图标打开文件列表,双击文件打开文件,也可以输入...
  • linux 内核源码 系统调用宏定义

    千次阅读 2015-09-28 17:07:05
    linux 内核源码中关于系统调用源码阅读
  • Linux内核源码阅读工具——source insight4.0Source insight4.0工具的使用入门一、Souce insight建立工程、导入源码二、遍历所有源码文件建立符号索引 Source insight4.0工具的使用入门 本篇博客为学习同步更新博客...
  • linux内核源码目录

    千次阅读 2017-01-04 20:03:32
    2015年9月刚入学,就开始学习有关内核源码的知识,那时候定期上课,定期讨论,定期交作业,中间遇到了很多困难,虽然最后还是没有坚持下来,但是看到这些东西的时候,还是觉得很有感觉。来和大家一起分享。 一....
  • Open vSwitch(OVS)源码编译Ubuntu16.04,Linux内核版本4.15
  • 公开的 Open Silicon Tracer是ARM / Linux系统调用跟踪程序原型

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 102,010
精华内容 40,804
关键字:

linuxopen源码

linux 订阅