




已阅读5页,还剩44页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux tty core 源码分析日期:2011-02-08来源:CSDN 作者:sirzjp本文以linux 2.6.27内核为基础,阅读tty core 源码并作注解,自己接触时间不长,希望与爱好者共同分享,错误之处还望指正。linux tty core 是建立在字符设备驱动的基础之上,并为tty类型设备(串口、控制台、虚拟终端)提供一个公用的平台。所以任何一个tty设备驱动的注册都是作为一个字符设备驱动而操作的。下面我们看看代码中是如何处理的:/* 3/2004 jmc: why do these devices exist? */tty核心默认在内核中实现的字符型tty设备驱动static struct cdev tty_cdev, console_cdev;#ifdef CONFIG_UNIX98_PTYSstatic struct cdev ptmx_cdev;#endif#ifdef CONFIG_VTstatic struct cdev vc0_cdev;#endif/* Ok, now we can initialize the rest of the tty devices and can count* on memory allocations, interrupts etc.*/static int _init tty_init(void)/在字符设备模型中加入注册tty_cdev驱动并加入/dev/tty这样的设备cdev_init(&tty_cdev, &tty_fops);if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) | register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, /dev/tty) 0) panic(Couldnt register /dev/tty drivern);device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, tty);/在字符设备模型中加入注册console_cdev驱动并加入/dev/console这样的设备cdev_init(&console_cdev, &console_fops);if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) | register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, /dev/console) 0) panic(Couldnt register /dev/console drivern);device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, console);/在字符设备模型中加入注册ptmx_cdev驱动并加入/dev/ptmx这样的设备#ifdef CONFIG_UNIX98_PTYScdev_init(&ptmx_cdev, &ptmx_fops);if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) | register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, /dev/ptmx) 0) panic(Couldnt register /dev/ptmx drivern);device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, ptmx);#endif/在字符设备模型中加入注册vc0_cdev驱动并加入/dev/tty0这样的设备#ifdef CONFIG_VTcdev_init(&vc0_cdev, &console_fops);if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) | register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, /dev/vc/0) private_data,为后续的操作做准备/* tty_open - open a tty device* inode: inode of device file* filp: file pointer to tty* tty_open and tty_release keep up the tty count that contains the* number of opens done on a tty. We cannot use the inode-count, as* different inodes might point to the same tty.* Open-counting is needed for pty masters, as well as for keeping* track of serial lines: DTR is dropped when the last close happens.* (This is not done solely through tty-count, now. - Ted 1/27/92)* The termios state of a pty is reset on first open so that* settings dont persist across reuse.* Locking: tty_mutex protects tty, get_tty_driver and init_dev work.* tty-count should protect the rest.* -siglock protects -signal/-sighand*/ 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);/主要是设置file-f_mode的一些标志使文件不可定位retry_open: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; driver = tty-driver; index = tty-index;/设备在驱动中的driver-ttys中的位置 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; index = fg_console;/表示当前控制台索引 noctty = 1; goto got_driver;#endif/这里表示的是设备号为(5,1)及/dev/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;/依据设备的设备号来确定设备在具体驱动中的索引。driver = get_tty_driver(device, &index);if (!driver) mutex_unlock(&tty_mutex); return -ENODEV;/*到此位置我们的目的是确定设备驱动和设备在驱动中的位置以及设备是否和进程绑定*/got_driver:/init_dev 初始化一个tty_struct 结构 具体在下面分析retval = init_dev(driver, index, &tty);mutex_unlock(&tty_mutex);if (retval) return retval;filp-private_data = tty;/把tty_struct结构作为file-private_data这也是open函数的主要目的file_move(filp, &tty-tty_files);check_tty_count(tty, tty_open);/统计tty设备打开的次数if (tty-driver-type = TTY_DRIVER_TYPE_PTY & tty-driver-subtype = PTY_TYPE_MASTER) noctty = 1;#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG opening %s., tty-name);#endifif (!retval) if (tty-ops-open) retval = tty-ops-open(tty, filp);/调用tty_operations中的回调函数tty驱动中我们要实现的% 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);/init_dev的反操作 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(¤t-sighand-siglock);if (!noctty & /设备和进程绑定作为进程回话的控制终端 current-signal-leader & !current-signal-tty & tty-session = NULL) _proc_set_tty(current, tty);spin_unlock_irq(¤t-sighand-siglock);mutex_unlock(&tty_mutex);return 0;/* BKL pushdown: scary code avoidance wrapper */static int tty_open(struct inode *inode, struct file *filp)int ret;lock_kernel();ret = _tty_open(inode, filp);unlock_kernel();return ret;/* init_dev - initialise a tty device* driver: tty driver we are opening a device on* idx: device index* tty: returned tty structure* Prepare a tty device. This may not be a new clean device but* could also be an active device. The pty drivers require special* handling because of this.* Locking:* The function is called under the tty_mutex, which* protects us from the tty struct or driver itself going away.* On exit the tty device has the line discipline attached and* a reference count of 1. If a pair was created for pty/tty use* and the other was a pty master then it too has a reference count of 1.* WSH 06/09/97: Rewritten to remove races and properly clean up after a* failed open. The new code protects the open with a mutex, so its* really quite straightforward. The mutex locking can probably be* relaxed for the (most common) case of reopening a tty.*/static int init_dev(struct tty_driver *driver, int idx,struct tty_struct *ret_tty)struct tty_struct *tty, *o_tty;struct ktermios *tp, *tp_loc, *o_tp, *o_tp_loc;struct ktermios *ltp, *ltp_loc, *o_ltp, *o_ltp_loc;int retval = 0;/* check whether were reopening an existing tty */if (driver-flags & TTY_DRIVER_DEVPTS_MEM) /伪终端初始化,从设备。 tty = devpts_get_tty(idx); /* * If we dont have a tty here on a slave open, its because * the master already started the close process and theres * no relation between devpts file and tty anymore. */ if (!tty & driver-subtype = PTY_TYPE_SLAVE) retval = -EIO; goto end_init; /* * Its safe from now on because init_dev() is called with * tty_mutex held and release_dev() wont change tty-count * or tty-flags without having to grab tty_mutex */ if (tty & driver-subtype = PTY_TYPE_MASTER) tty = tty-link; else tty = driver-ttysidx;if (tty) goto fast_track;/fast_track表示设备不是第一次打开/* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released. (Except that the termios * and locked termios may be retained.) */if (!try_module_get(driver-owner) retval = -ENODEV; goto end_init;o_tty = NULL;tp = o_tp = NULL;ltp = o_ltp = NULL;tty = alloc_tty_struct();if (!tty) goto fail_no_mem;initialize_tty_struct(tty);tty-driver = driver;tty-ops = driver-ops;tty-index = idx;tty_line_name(driver, idx, tty-name);if (driver-flags & TTY_DRIVER_DEVPTS_MEM) tp_loc = &tty-termios; ltp_loc = &tty-termios_locked; else tp_loc = &driver-termiosidx; ltp_loc = &driver-termios_lockedidx;if (!*tp_loc) tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); if (!tp) goto free_mem_out; *tp = driver-init_termios;if (!*ltp_loc) ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); if (!ltp) goto free_mem_out;if (driver-type = TTY_DRIVER_TYPE_PTY) o_tty = alloc_tty_struct(); if (!o_tty) goto free_mem_out; initialize_tty_struct(o_tty); o_tty-driver = driver-other; o_tty-ops = driver-ops; o_tty-index = idx; tty_line_name(driver-other, idx, o_tty-name); if (driver-flags & TTY_DRIVER_DEVPTS_MEM) o_tp_loc = &o_tty-termios; o_ltp_loc = &o_tty-termios_locked; else o_tp_loc = &driver-other-termiosidx; o_ltp_loc = &driver-other-termios_lockedidx; if (!*o_tp_loc) o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); if (!o_tp) goto free_mem_out; *o_tp = driver-other-init_termios; if (!*o_ltp_loc) o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); if (!o_ltp) goto free_mem_out; /* * Everything allocated . set up the o_tty structure. */ if (!(driver-other-flags & TTY_DRIVER_DEVPTS_MEM) driver-other-ttysidx = o_tty; if (!*o_tp_loc) *o_tp_loc = o_tp; if (!*o_ltp_loc) *o_ltp_loc = o_ltp; o_tty-termios = *o_tp_loc; o_tty-termios_locked = *o_ltp_loc; driver-other-refcount+; if (driver-subtype = PTY_TYPE_MASTER) o_tty-count+; /* Establish the links in both directions */ tty-link = o_tty; o_tty-link = tty;/* * All structures have been allocated, so now we install them. * Failures after this point use release_tty to clean up, so * theres no need to null out the local pointers. */if (!(driver-flags & TTY_DRIVER_DEVPTS_MEM) driver-ttysidx = tty;if (!*tp_loc) *tp_loc = tp;if (!*ltp_loc) *ltp_loc = ltp;tty-termios = *tp_loc;tty-termios_locked = *ltp_loc;/* Compatibility until drivers always set this */tty-termios-c_ispeed = tty_termios_input_baud_rate(tty-termios);tty-termios-c_ospeed = tty_termios_baud_rate(tty-termios);driver-refcount+;tty-count+;/* * Structures all installed . call the ldisc open routines. * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesnt care. */retval = tty_ldisc_setup(tty, o_tty);/打开线路规程if (retval) goto release_mem_out; goto success;/* * This fast open can be used if the tty is already open. * No memory is allocated, and the only failures are from * attempting to open a closing tty or attempting multiple * opens on a pty master. */fast_track:if (test_bit(TTY_CLOSING, &tty-flags) retval = -EIO; goto end_init;if (driver-type = TTY_DRIVER_TYPE_PTY & driver-subtype = PTY_TYPE_MASTER) /* * special case for PTY masters: only one open permitted, * and the slave side open count is incremented as well. */ if (tty-count) retval = -EIO; goto end_init; tty-link-count+;tty-count+;tty-driver = driver; /* N.B. why do this every time? */* FIXME */if (!test_bit(TTY_LDISC, &tty-flags) printk(KERN_ERR init_dev but no ldiscn);success:*ret_tty = tty;/* All paths come through here to release the mutex */end_init:return retval;/* Release locally allocated memory . nothing placed in slots */free_mem_out:kfree(o_tp);if (o_tty) free_tty_struct(o_tty);kfree(ltp);kfree(tp);free_tty_struct(tty);fail_no_mem:module_put(driver-owner);retval = -ENOMEM;goto end_init;/* call the tty release_tty routine to clean out this slot */release_mem_out:if (printk_ratelimit() printk(KERN_INFO init_dev: ldisc open failed, clearing slot %dn, idx);release_tty(tty, idx);goto end_init;Linux tty core 源码分析日期:2011-02-08来源:CSDN 作者:sirzjp前面分析了open操作,现在分析读操作tty_read。tty_read直接调用线路规程中的读操作从tty-read_buf中读取数据到用户空间。其中tty.read_head记录已读数据的起始位置,tty.read_tail记录已读数据的末尾位置,tty.read_cnt记录已读数据的数量。至于所读数据从何而来我们在下一篇中分析,下面看具体代码:/* tty_read - read method for tty device files* file: pointer to tty file* buf: user buffer* count: size of user buffer* ppos: unused* Perform the read system call function on this terminal device. Checks* for hung up devices before calling the line discipline method.* Locking:* Locks the line discipline internally while needed. Multiple* read calls may be outstanding in parallel.*/ 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;/得到open中设置的tty结构inode = file-f_path.dentry-d_inode;if (tty_paranoia_check(tty, inode, tty_read) /tty及其幻数检查 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); /TTY_LDISC 表示tty和线路规程绑定if (ld-ops-read) i = (ld-ops-read)(tty, file, buf, count);else i = -EIO;tty_ldisc_deref(ld);if (i 0) inode-i_atime = current_fs_time(inode-i_sb);return i;读操作的具体细节都在线路规程中实现的,默认的线路规程的读操作时read_chan函数,下面看具体源码:/* read_chan - read function for tty* tty: tty device* file: file object* buf: userspace buffer pointer* nr: size of I/O* Perform reads for the line discipline. We are guaranteed that the* line discipline will not be closed under us but we may get multiple* parallel readers and must handle this ourselves. We may also get* a hangup. Always called in user context, may sleep.* This code must be sure never to sleep through a hangup.*/static ssize_t read_chan(struct tty_struct *tty, struct file *file, unsigned char _user *buf, size_t nr)unsigned char _user *b = buf;DECLARE_WAITQUEUE(wait, current); /声明等待队列项,每次读操作都加入tty.read_wait等待队列int c;int minimum, time;ssize_t retval = 0;ssize_t size;long timeout;unsigned long flags;int packet;do_it_again:if
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 离婚财产分割与共同债务处理补充协议样本
- 租赁别墅退房协议范本及环境恢复要求
- 琴行专业教师团队聘用及教学成果分享协议
- 离婚协议中子女抚养权变更及监护权调整执行细节合同
- 互联网科技公司股权转让与用户数据共享合同
- 课件制作大赛开场
- 汽车测试技术与试验试题及答案
- 辅警安全知识培训心得
- 工商银行2025眉山市小语种岗笔试题及答案
- 工商银行2025柳州市小语种岗笔试题及答案
- 创伤性窒息护理课件
- 人口老龄化对寿险产品需求结构的影响
- 最常用2000个英语单词-电子表格版
- 老年人常见疾病预防知识讲座
- 《解决方案营销》节选版
- 流感传染的预防与护理知识培训课件
- 秋季慢性病知识讲座
- 2024年全国高考体育单招考试语文试卷试题(含答案详解)
- 《西方经济学》(下册)课程教案
- 电力系统运行方式分析和计算
- 法院送法进校园讲座
评论
0/150
提交评论