Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1811035
  • 博文数量: 272
  • 博客积分: 1272
  • 博客等级: 少尉
  • 技术积分: 1866
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-09 15:51
文章分类

全部博文(272)

文章存档

2016年(16)

2015年(28)

2014年(97)

2013年(59)

2012年(25)

2011年(47)

分类: LINUX

2013-11-15 16:07:26

tty设备的读操作tty_write首先对读操作的需求做检查,然后调用ldisc->write操作默认即write_chain函数。wrtie_chain通过tty->ops->write或者tty->ops->flush_chars把数据写入到设备中,两者都实现时后者有限。其中write_room函数是用来检测缓存

于空间.


/**
* tty_write  - write method for tty device file
* @file: tty file pointer
* @buf: user data to write
* @count: bytes to write
* @ppos: unused
*
* Write data to a tty device via the line discipline.
*
* Locking:
*  Locks the line discipline as required
*  Writes to the tty driver are serialized by the atomic_write_lock
* and are then processed in chunks to the device. The line discipline
* write method will not be involked in parallel for each device
*  The line discipline write method is called under the big
* kernel lock for historical reasons. New code should not rely on this.
*/

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 ;
(!tty || !tty->ops->write ||
  (test_bit(TTY_IO_ERROR, &tty->flags)))
   return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
  printk(KERN_ERR "tty driver %s lacks a write_room method./n",
   tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
  ret = -EIO;
else
  ret = do_tty_write(ld->ops->write, tty, file, buf, count); //调用线路规程write函数即write_chain写入,下面分析
tty_ldisc_deref(ld);
return ret;
}


分配临时缓存给tty->write_buf并把用户空间数据拷贝进去然后调用线路规程写函数即write_chain。

/*
* Split writes up in sane blocksizes to avoid
* denial-of-service type attacks
*/
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 < 0)
  return ret;

/*
  * We chunk up writes into a temporary buffer. This
  * simplifies low-level drivers immensely, since they
  * don't have locking issues and user mode accesses.
  *
  * But if TTY_NO_WRITE_SPLIT is set, we should use a
  * big chunk-size..
  *
  * The default chunk-size is 2kB, because the NTTY
  * layer has problems with bigger chunks. It will
  * claim to be able to handle more characters than
  * it actually does.
  *
  * FIXME: This can probably go away now except that 64K chunks
  * are too likely to fail unless switched to vmalloc...
  */
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
  chunk = 65536;
if (count < chunk)
  chunk = count;

/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
  unsigned char *buf;

  if (chunk < 1024)
   chunk = 1024;

  buf = kmalloc(chunk, GFP_KERNEL);
  if (!buf) {
   ret = -ENOMEM;
   goto out;
  }
  kfree(tty->write_buf);
  tty->write_cnt = chunk;
  tty->write_buf = buf;
}

/* 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;
  ret = write(tty, file, tty->write_buf, size);
  if (ret <= 0)
   break;
  written += ret;
  buf += ret;
  count -= ret;
  if (!count)
   break;
  ret = -ERESTARTSYS;
  if (signal_pending(current))
   break;
  cond_resched();
}
if (written) {
  struct inode *inode = file->f_path.dentry->d_inode;
  inode->i_mtime = current_fs_time(inode->i_sb);
  ret = written;
}
out:
tty_write_unlock(tty);
return ret;
}

//write_chain主要根据数据是否是经过加工的调用tty->ops->flush_chars或者tty->ops->write把数据写入设备

//当写入的空间不足时,且数据没有完全写完则调用schedule()把写操作加入写等待队列


/**
* write_chan  - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
* @nr: size of I/O
*
* Write function of the terminal device. This is serialized with
* respect to other write callers but not to termios changes, reads
* and other such events. We must be careful with N_TTY as the receive
* code will echo characters, thus calling driver write methods.
*
* This code must be sure never to sleep through a hangup.
*/

static ssize_t write_chan(struct tty_struct *tty, struct file *file,
     const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;

/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */


//TOSTOP标志设置时若后台进程试图写控制台时将发出SIGTTOU信号,也即执行下面的操作
if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) { 
  retval = tty_check_change(tty); //进程控制终端相关设置
  if (retval)
   return retval;
}

add_wait_queue(&tty->write_wait, &wait);  //代表写进程的等待队列项加入到写等待队列中
while (1) {
  set_current_state(TASK_INTERRUPTIBLE);
  if (signal_pending(current)) {
   retval = -ERESTARTSYS;
   break;
  }
  if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
   retval = -EIO;
   break;
  }


//OPOST设置,则操作可以选择加工过的输入

//TTY_HW_COOK_OUT若设置通知线路加工其输出的数据,否则只做拷贝

  if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
   while (nr > 0) {

    //opost_block申请写入空间,并把数据根据加工要求成然后调用tty_operations->write成块写入tty设备
    ssize_t num = opost_block(tty, b, nr);
    if (num < 0) {
     if (num == -EAGAIN)
      break;
     retval = num;
     goto break_out;
    }
    b += num;
    nr -= num;
    if (nr == 0)
     break;
    c = *b;
    if (opost(c, tty) < 0)
     break;
    b++; nr--;
   }
   if (tty->ops->flush_chars)
    tty->ops->flush_chars(tty);
  } else {
   while (nr > 0) {
    c = tty->ops->write(tty, b, nr);
    if (c < 0) {
     retval = c;
     goto break_out;
    }
    if (!c)
     break;
    b += c;
    nr -= c;
   }
  }
  if (!nr)
   break;
  if (file->f_flags & O_NONBLOCK) {
   retval = -EAGAIN;
   break;
  }
  schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
return (b - buf) ? b - buf : retval;
}

阅读(805) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~