欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    Linux设备模型之tty驱动架构分析.pdf

    • 资源ID:69631082       资源大小:366.45KB        全文页数:16页
    • 资源格式: PDF        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Linux设备模型之tty驱动架构分析.pdf

    Linux 设备模型之设备模型之 tty 驱动架构分析驱动架构分析 一:前言 Tty 这个名称源于电传打字节的简称。在 linux 表示各种终端。终端通常都跟硬件相对应。比如对应于输入设备键盘鼠标。输出设备显示器的控制终端和串口终端.也有对应于不存在设备的 pty 驱动。在如此众多的终端模型之中,linux 是怎么将它们统一建模的呢?这就是我们今天要讨论的问题.二:tty 驱动概貌 Tty 架构如下所示:如上图所示,用户空间主要是通过设备文件同 tty_core 交互.tty_core 根据用空间操作的类型再选择跟 line discipline 和tty_driver 交互.例如设置硬件的 ioctl 指令就直接交给 tty_driver 处理。Read 和 write 操作就会交给 line discipline 处理.Line discipline 是线路规程的意思。正如它的名字一样,它表示的是这条终端”线程”的输入与输出规范设置.主要用来进行输入/输出数据的预处理。处理之后。就会将数据交给 tty_driver Tty_driver 就是终端对应的驱动了。它将字符转换成终端可以理解的字串.将其传给终端设备。值得注意的是,这个架构没有为 tty_drivero 提供 read 操作。也就是说 tty_core 和 line discipline 都没有办法从 tty_driver里直接读终端信息。这是因为 tty_driver 对就的 hardware 并不一定是输入数据和输出数据的共同负载者。例如控制终端,输出设备是显示器。输入设备是键盘。基于这样的原理。在 line discipline 中有一个输入缓存区。并提供了一个名叫 receive_buf()的接口函数。对应的终端设备只要调用 line discipine 的 receiver_buf 函数,将数据写入到输入缓存区就可以了。如果一个设备同时是输入设备又是输出设备。那在设备的中断处理中调用 receive_buf()将数据写入即可.三:tty 驱动接口分析 具体的 tty 驱动设计可以参考 LDD3。这里只对它的接口实现做一个分析.tty driver 的所有操作都包含在 tty_driver 中。内核即供了一个名叫 alloc_tty_driver()来分配这个 tty_driver。当然我们也可以在自己的驱动中将它定义成一个静态的结构。对tty_driver 进行一些必要的初始化之后,调用 tty_register_driver()将其注册.alloc_tty_driver()接口代码如下所示:struct tty_driver*alloc_tty_driver(int lines)struct tty_driver*driver;driver=kzalloc(sizeof(struct tty_driver),GFP_KERNEL);if(driver)driver-magic=TTY_DRIVER_MAGIC;driver-num=lines;/*later well move allocation of tables here*/return driver;这个函数只有一个参数。这个参数的含义为 line 的个数。也即次设备号的个数。注意每个设备文件都会对应一个 line.在这个接口里为 tty_driver 分配内存,然后将 driver-mage.driver-num 初始化之后就返回了.tty_register_driver()用来注册一个 tty_driver。代码如下:int tty_register_driver(struct tty_driver*driver)int error;int i;dev_t dev;void*p=NULL;/TTY_DRIVER_INSTALLED:已安装的 if(driver-flags&TTY_DRIVER_INSTALLED)return 0;/TTY_DRIVER_DEVPTS_MEM:使用 devpts 进行动态内存映射 if(!(driver-flags&TTY_DRIVER_DEVPTS_MEM)&driver-num)p=kzalloc(driver-num*3*sizeof(void*),GFP_KERNEL);if(!p)return-ENOMEM;/注册字符设备号 /如果没有指定 driver-major if(!driver-major)error=alloc_chrdev_region(&dev,driver-minor_start,driver-num,driver-name);if(!error)driver-major=MAJOR(dev);driver-minor_start=MINOR(dev);else dev=MKDEV(driver-major,driver-minor_start);error=register_chrdev_region(dev,driver-num,driver-name);if(error ttys=(struct tty_struct*)p;driver-termios=(struct ktermios*)(p+driver-num);driver-termios_locked=(struct ktermios*)(p+driver-num*2);else driver-ttys=NULL;driver-termios=NULL;driver-termios_locked=NULL;/注册字符设备 cdev_init(&driver-cdev,&tty_fops);driver-cdev.owner=driver-owner;error=cdev_add(&driver-cdev,dev,driver-num);if(error)unregister_chrdev_region(dev,driver-num);driver-ttys=NULL;driver-termios=driver-termios_locked=NULL;kfree(p);return error;/指定默认的 put_char if(!driver-put_char)driver-put_char=tty_default_put_char;mutex_lock(&tty_mutex);list_add(&driver-tty_drivers,&tty_drivers);mutex_unlock(&tty_mutex);/如果没有指定 TTY_DRIVER_DYNAMIC_DEV.即动态设备管理 if(!(driver-flags&TTY_DRIVER_DYNAMIC_DEV)for(i=0;i num;i+)tty_register_device(driver,i,NULL);proc_tty_register_driver(driver);return 0;这个函数操作比较简单。就是为 tty_driver 创建字符设备。然后将字符设备的操作集指定为 tty_fops.并且将 tty_driver 挂载到 tty_drivers 链表中.其实这个链表的作用跟我们之前分析的 input 子系统中的 input_dev 数组类似。都是以设备号为关键字找到对应的 driver.特别的。如果没有定义 TTY_DRIVER_DYNAMIC_DEV.还会在 sysfs 中创建一个类设备.这样主要是为了 udev 管理设备.以流程图的方式将上述操作表示如下:四:设备文件的操作 设备文件的操作是本节分析的重点。它的主要操作是将各项操作对应到 ldsic 或者是 tty_driver.41:打开 tty 设备的操作 从注册的过程可以看到,所有的操作都会对应到 tty_fops 中。Open 操作对应的操作接口是 tty_open()。代码如下:static int tty_open(struct inode*inode,struct file*filp)struct tty_struct*tty;int noctty,retval;struct tty_driver*driver;int index;dev_t device=inode-i_rdev;unsigned short saved_flags=filp-f_flags;nonseekable_open(inode,filp);retry_open:/O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端 /noctty:需不需要更改当前进程的控制终端 noctty=filp-f_flags&O_NOCTTY;index =-1;retval=0;mutex_lock(&tty_mutex);/设备号(5,0)即/dev/tty.表示当前进程的控制终端 if(device=MKDEV(TTYAUX_MAJOR,0)tty=get_current_tty();/如果当前进程的控制终端不存在,退出 if(!tty)mutex_unlock(&tty_mutex);return-ENXIO;/取得当前进程的 tty_driver driver=tty-driver;index=tty-index;filp-f_flags|=O_NONBLOCK;/*Dont let/dev/tty block*/*noctty=1;*/goto got_driver;#ifdef CONFIG_VT /设备号(4,0).即/dev/tty0:表示当前的控制台 if(device=MKDEV(TTY_MAJOR,0)extern struct tty_driver*console_driver;driver=console_driver;/fg_console:表示当前的控制台 index=fg_console;noctty=1;goto got_driver;#endif /设备号(5,1).即/dev/console.表示外接的控制台.通过 regesit_console()if(device=MKDEV(TTYAUX_MAJOR,1)driver=console_device(&index);if(driver)/*Dont let/dev/console block*/filp-f_flags|=O_NONBLOCK;noctty=1;goto got_driver;mutex_unlock(&tty_mutex);return-ENODEV;/以文件的设备号为关键字,到 tty_drivers 中搜索所注册的 driver driver=get_tty_driver(device,&index);if(!driver)mutex_unlock(&tty_mutex);return-ENODEV;got_driver:/index 表示它的次设备号 retval=init_dev(driver,index,&tty);mutex_unlock(&tty_mutex);if(retval)return retval;filp-private_data=tty;file_move(filp,&tty-tty_files);check_tty_count(tty,tty_open);if(tty-driver-type=TTY_DRIVER_TYPE_PTY&tty-driver-subtype=PTY_TYPE_MASTER)noctty=1;#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG opening%s.,tty-name);#endif if(!retval)if(tty-driver-open)retval=tty-driver-open(tty,filp);else retval=-ENODEV;filp-f_flags=saved_flags;if(!retval&test_bit(TTY_EXCLUSIVE,&tty-flags)&!capable(CAP_SYS_ADMIN)retval=-EBUSY;if(retval)#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG error%d in opening%s.,retval,tty-name);#endif release_dev(filp);if(retval!=-ERESTARTSYS)return retval;if(signal_pending(current)return retval;schedule();/*Need to reset f_op in case a hangup happened.*/if(filp-f_op=&hung_up_tty_fops)filp-f_op=&tty_fops;goto retry_open;mutex_lock(&tty_mutex);spin_lock_irq(&current-sighand-siglock);/设置当前进程的终端 if(!noctty&current-signal-leader&!current-signal-tty&tty-session=NULL)_proc_set_tty(current,tty);spin_unlock_irq(&current-sighand-siglock);mutex_unlock(&tty_mutex);tty_audit_opening();return 0;注意在这里有个容易忽略的操作:init_dev()。Init_dev()-initialize_tty_struct()tty_ldisc_assign(tty,tty_ldisc_get(N_TTY);看一下 tty_ldisc_assign(tty,tty_ldisc_get(N_TTY)的操作:Tty_ldisc_get():struct tty_ldisc*tty_ldisc_get(int disc)unsigned long flags;struct tty_ldisc*ld;if(disc=NR_LDISCS)return NULL;spin_lock_irqsave(&tty_ldisc_lock,flags);ld=&tty_ldiscsdisc;/*Check the entry is defined*/if(ld-flags&LDISC_FLAG_DEFINED)/*If the module is being unloaded we cant use it*/if(!try_module_get(ld-owner)ld=NULL;else/*lock it*/ld-refcount+;else ld=NULL;spin_unlock_irqrestore(&tty_ldisc_lock,flags);return ld;这个函数的操作为到 tty_ldiscs 找到对应项.这个数组中的成员是调用 tty_register_ldisc()将其设置进去的.tty_ldisc_assign 操作如下:static void tty_ldisc_assign(struct tty_struct*tty,struct tty_ldisc*ld)tty-ldisc=*ld;tty-ldisc.refcount=0;即将取出来的 idisc 作为 tty-ldisc 字段.在这段代码中涉及到了 tty_driver,tty_struct,struct tty_ldisc.这三者之间的关系用下图表示如下:在这里,为 tty_struct 的 ldisc 是默认指定为 tty_ldiscsN_TTY.该 ldisc 对应的是控制终端的线路规范。可以在用空间用带TIOCSETD 的 ioctl 调用进行更改.将上述 open 用流程图的方式表示如下:4.2:设备文件的 write 操作 设备文件的 write 操作对应 tty_fops-write 即 tty_write().代码如下:static ssize_t tty_write(struct file*file,const char _user*buf,size_t count,loff_t*ppos)struct tty_struct*tty;struct inode*inode=file-f_path.dentry-d_inode;ssize_t ret;struct tty_ldisc*ld;tty=(struct tty_struct*)file-private_data;if(tty_paranoia_check(tty,inode,tty_write)return-EIO;if(!tty|!tty-driver-write|(test_bit(TTY_IO_ERROR,&tty-flags)return-EIO;ld=tty_ldisc_ref_wait(tty);if(!ld-write)ret=-EIO;else ret=do_tty_write(ld-write,tty,file,buf,count);tty_ldisc_deref(ld);return ret;在 open 的过程中,将 tty_struct 存放在 file 的私有区。在 write 中,从 file 的私有区中就可以取到要操作的 tty_struct.如果 tty_driver 中没有 write.如果 tty 有错误都会有效性判断失败返回。如果一切正常,递增 ldsic 的引用计数。将用do_tty_wirte()再行写操作。写完之后,再递减 ldsic 的引用计数.Do_tty_write 代码分段分析如下:static inline ssize_t do_tty_write(ssize_t(*write)(struct tty_struct*,struct file*,const unsigned char*,size_t),struct tty_struct*tty,struct file*file,const char _user*buf,size_t count)ssize_t ret,written=0;unsigned int chunk;ret=tty_write_lock(tty,file-f_flags&O_NDELAY);if(ret flags)chunk=65536;if(count write_cnt chunk)unsigned char*buf;if(chunk write_buf);tty-write_cnt=chunk;tty-write_buf=buf;默认一次写数据的大小为 2K.如果设置了 TTY_NO_WRITE_SPLIT.则将一次写的数据量扩大为 65536.Tty-write_buf 是写操作的临时缓存区。即将用户空的数据暂时存放到这里 Tty-write_cnt 是临时缓存区的大小。在这里,必须要根据一次写的数据量对这个临时缓存区做调整 /*Do the write.*/for(;)size_t size=count;if(size chunk)size=chunk;ret=-EFAULT;if(copy_from_user(tty-write_buf,buf,size)break;lock_kernel();ret=write(tty,file,tty-write_buf,size);unlock_kernel();if(ret f_path.dentry-d_inode;inode-i_mtime=current_fs_time(inode-i_sb);ret=written;out:tty_write_unlock(tty);return ret;后面的操作就比较简单了。先将用户空间的数据 copy 到临时缓存区,然后再调用 ldisc-write()完成这次写操作.最后再更新设备结点的时间戳.Write 操作的流程图如下示:在这里,我们只看到将数据写放到了 ldisc-write().没有看到与 tty_driver 相关的部份。实际上在 ldisc 中对写入的数据做预处理过后,还是会调用 tty_driver-write()将其写入硬件.4.3:设备文件的 read 操作 static ssize_t tty_read(struct file*file,char _user*buf,size_t count,loff_t*ppos)int i;struct tty_struct*tty;struct inode*inode;struct tty_ldisc*ld;tty=(struct tty_struct*)file-private_data;inode=file-f_path.dentry-d_inode;if(tty_paranoia_check(tty,inode,tty_read)return-EIO;if(!tty|(test_bit(TTY_IO_ERROR,&tty-flags)return-EIO;/*We want to wait for the line discipline to sort out in this situation*/ld=tty_ldisc_ref_wait(tty);lock_kernel();if(ld-read)i=(ld-read)(tty,file,buf,count);else i=-EIO;tty_ldisc_deref(ld);unlock_kernel();if(i 0)inode-i_atime=current_fs_time(inode-i_sb);return i;这个 read 操作就更简单。直接调用 ldsic-read()完成工作 流程图如下:五:小结 在 tty 设备文件的操作中。Open 操作会进行一系统初始化。然后调用 ldsic-open tty_driver-open。在 write 和 read 调用中只 tty_core 只会用到 ldisc-wirte/ldisc-read.除了上面分析的几个操作之外,还有一个 ioctl 操作,以及它封装的几个 termios。这些 ioctl 类的操作会直接和 tty_driver 相关联.在这一节里,只对 tty 的构造做一个分析,具体 ldisc 的操作我们之后以控制终端为例进行分析.

    注意事项

    本文(Linux设备模型之tty驱动架构分析.pdf)为本站会员(qwe****56)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开