linux tty core code,linux tty core 源码分析(5)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值