linux 返回上一终端,Linux 为终端使用

本文系本站原创,欢迎转载!

转载请注明出处:http://sjj0412.cublog.cn/

-----------------------------------------------------------

Linux下伪终端是对应于/dev/pts/x,这个/dev/pts其实也只是devpts的挂载目录,通过如下命令挂在的。mount devpts /dev/pts -t devpts

伪终端(Pseudo Terminal)是成对的逻辑终设备(即master和slave设备, 对master的操作会反映到slave上)。

例如/dev/ptyp3和/dev/ttyp3(或者在设备文件系统中分别是/dev/pty/m3和 /dev/pty/s3)。它们与实际物理设备并不直接相关。如果一个程序把ptyp3(master设备)看作是一个串行端口设备,则它对该端口的读/ 写操作会反映在该逻辑终端设备对应的另一个ttyp3(slave设备)上面。而ttyp3则是另一个程序用于读写操作的逻辑设备。

这样,两个程序就可以通过这种逻辑设备进行互相交流,而其中一个使用ttyp3的程序则认为自己正在与一个串行端口进行通信。这很象是逻辑设备对之间的管道操作。对于ttyp3(s3),任何设计成使用一个串行端口设备的程序都可以使用该逻辑设备。但对于使用ptyp3的程序,则需要专门设计来使用 ptyp3(m3)逻辑设备。

例如,如果某人在网上使用telnet程序连接到你的计算机上,则telnet程序就可能会开始连接到设备 ptyp2(m2)上(一个伪终端端口上)。此时一个getty程序就应该运行在对应的ttyp2(s2)端口上。当telnet从远端获取了一个字符 时,该字符就会通过m2、s2传递给 getty程序,而getty程序就会通过s2、m2和telnet程序往网络上返回”login:”字符串信息。这样,登录程序与telnet程序就通 过“伪终端”进行通信。通过使用适当的软件,就可以把两个甚至多个伪终端设备连接到同一个物理串行端口上。

但是到了现在,/dev/pty/m不再存在,只是以file(/dev/ptmx的一次打开)存在,/dev/pty/s对应到了/dev/pts/x里。

故要创建伪终端,就必须打开/dev/ptmx文件。

一个典型的例子如下:

int main(){

int fdm fds;

char *slavename;

extern char *ptsname();

fdm = open("/dev/ptmx", O_RDWR); /* open master */

grantpt(fdm); /* change permission of slave */

unlockpt(fdm); /* unlock slave */

slavename = ptsname(fdm); /* get name of slave */

fds = open(slavename, O_RDWR); /* open slave */

ioctl(fds, I_PUSH, "ptem"); /* push ptem */

ioctl(fds, I_PUSH, "ldterm"); /* push ldterm */

glib中的ptsname实现大致如下:

char* ptsname( int fd )

{

unsigned int pty_num;

static char buff[64];

if ( ioctl( fd, TIOCGPTN, &pty_num ) != 0 )//最终调用上面的pty_unix98_ioctl获取当前ptmx主设备对应的pty从设备号.

return NULL;

snprintf( buff, sizeof(buff), "/dev/pts/%u", pty_num );//格式化为/dev/pts/0,/dev/pts/1等,即:pts对应的文件全路径.

return buff;

}

这样就获得了pty的master,slave.master由打开/dev/ptmx得到file,slave打开/dev/pts/x得到file.

下面从源码角度分析伪终端机制:

首先看下/dev/ptmx的驱动:

static struct file_operations ptmx_fops = {

.llseek           = no_llseek,

.read             = tty_read,

.write            = tty_write,

.poll        = tty_poll,

.ioctl              = tty_ioctl,

.open            =

ptmx_open,

.release  = tty_release,

.fasync          = tty_fasync,

};

static int __init tty_init(void)

{

cdev_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("Couldn't

register /dev/ptmx driver\n");

devfs_mk_cdev(MKDEV(TTYAUX_MAJOR,

2), S_IFCHR|S_IRUGO|S_IWUGO, "ptmx");

class_device_create(tty_class,

MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");

这样当我们打开/dev/ptmx时就会执行ptmx_open:

static int ptmx_open(struct inode

* inode, struct file * filp)

{

/*

find a device that is not in use. */

down(&allocated_ptys_lock);

if

(!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {

up(&allocated_ptys_lock);

return

-ENOMEM;

}

idr_ret = idr_get_new(&allocated_ptys, NULL, &index);

//获得设备编号

if

(idr_ret < 0) {

up(&allocated_ptys_lock);

if

(idr_ret == -EAGAIN)

return

-ENOMEM;

return

-EIO;

}

if

(index >= pty_limit) {

idr_remove(&allocated_ptys,

index);

up(&allocated_ptys_lock);

return

-EIO;

}

up(&allocated_ptys_lock);

down(&tty_sem);

retval = init_dev(ptm_driver, index, &tty);

//以index为pts的设备索引号,创建成对的主从设备ptmx和pts

up(&tty_sem);

if

(retval)

goto

out;

set_bit(TTY_PTY_LOCK,

&tty->flags); /* LOCK THE SLAVE */

filp->private_data

= tty;

file_move(filp,

&tty->tty_files);

retval

= -ENOMEM;

if

(devpts_pty_new(tty->link))

goto

out1;

check_tty_count(tty,

"tty_open");

retval

= ptm_driver->open(tty, filp);

static

void initialize_tty_struct(struct tty_struct *tty){

memset(tty,

0, sizeof(struct tty_struct));

}

这个将tty和devpts绑定,并创建对应设备文件/dev/pts/x。

int

devpts_pty_new(struct tty_struct *tty)

{

int number = tty->index;

struct tty_driver *driver =

tty->driver;

dev_t device = MKDEV(driver->major,

driver->minor_start+number);

struct dentry *dentry;

struct inode *inode =

new_inode(devpts_mnt->mnt_sb);

/* We're supposed to be given the slave

end of a pty */

BUG_ON(driver->type !=

TTY_DRIVER_TYPE_PTY);

BUG_ON(driver->subtype !=

PTY_TYPE_SLAVE);

if (!inode)

return -ENOMEM;

inode->i_ino = number+2;

inode->i_blksize = 1024;

inode->i_uid = config.setuid ?

config.uid : current->fsuid;

inode->i_gid = config.setgid ?

config.gid : current->fsgid;

inode->i_mtime = inode->i_atime =

inode->i_ctime = CURRENT_TIME;

init_special_inode(inode,

S_IFCHR|config.mode, device);

inode->i_op =

&devpts_file_inode_operations;

inode->u.generic_ip = tty;

dentry = get_node(number);

if (!IS_ERR(dentry) &&

!dentry->d_inode)

d_instantiate(dentry, inode);

up(&devpts_root->d_inode->i_sem);

return 0;

}

init_dev是初始化伪终端tty对:

static int

init_dev(struct tty_driver *driver, int idx,

struct tty_struct **ret_tty)

{

struct tty_struct *tty, *o_tty;

struct termios *tp, **tp_loc, *o_tp,

**o_tp_loc;

struct termios *ltp, **ltp_loc, *o_ltp,

**o_ltp_loc;

int retval=0;

/* check whether we're reopening an

existing tty */

//查看是否已经存在此终端。

if (driver->flags &

TTY_DRIVER_DEVPTS_MEM) {

tty = devpts_get_tty(idx);//这个是从终端,所以要获的主终端。

if (tty &&

driver->subtype == PTY_TYPE_MASTER)

tty = tty->link;

} else {

tty = driver->ttys[idx];

}

if (tty) goto 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;

}

tty = alloc_tty_struct();

//分配终端

if(!tty)

goto fail_no_mem;

initialize_tty_struct(tty);

//初始化终端

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->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->termios[idx];

o_ltp_loc =

&driver->other->termios_locked[idx];

}

if (!*o_tp_loc) {

o_tp = (struct termios *)

kmalloc(sizeof(struct

termios), GFP_KERNEL);

if (!o_tp)

goto free_mem_out;

*o_tp =

driver->other->init_termios;

}

if (!*o_ltp_loc) {

o_ltp = (struct termios *)

kmalloc(sizeof(struct

termios), GFP_KERNEL);

if (!o_ltp)

goto free_mem_out;

memset(o_ltp, 0,

sizeof(struct termios));

}

}

static void

initialize_tty_struct(struct tty_struct *tty)

{

memset(tty, 0, sizeof(struct

tty_struct));

tty->magic = TTY_MAGIC;

tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));

//会赋值默认的行规则。

tty->pgrp = -1;

tty->overrun_time = jiffies;

tty->flip.char_buf_ptr =

tty->flip.char_buf;

tty->flip.flag_buf_ptr =

tty->flip.flag_buf;

INIT_WORK(&tty->flip.work,

flush_to_ldisc, tty);

init_MUTEX(&tty->flip.pty_sem);

init_MUTEX(&tty->termios_sem);

init_waitqueue_head(&tty->write_wait);

init_waitqueue_head(&tty->read_wait);

INIT_WORK(&tty->hangup_work,

do_tty_hangup, tty);

sema_init(&tty->atomic_read, 1);

sema_init(&tty->atomic_write, 1);

spin_lock_init(&tty->read_lock);

INIT_LIST_HEAD(&tty->tty_files);

INIT_WORK(&tty->SAK_work, NULL,

NULL);

}

默认规则生成:

struct tty_ldisc *tty_ldisc_get(int disc)

{

ld = &tty_ldiscs[disc];

/* Check the entry is defined */

return ld;

}

tty_ldiscs是全局变量。

通过 tty_register_ldisc注册。

int tty_register_ldisc(int disc, struct

tty_ldisc *new_ldisc)

{

tty_ldiscs[disc] = *new_ldisc;

tty_ldiscs[disc].num = disc;

tty_ldiscs[disc].flags |=

LDISC_FLAG_DEFINED;

tty_ldiscs[disc].refcount = 0;

spin_unlock_irqrestore(&tty_ldisc_lock,

flags);

return ret;

}

N_TTY是在console_int注册的:

void __init

console_init(void)

{

initcall_t *call;

/* Setup the default TTY line discipline.

*/

(void) tty_register_ldisc(N_TTY,

&tty_ldisc_N_TTY);

struct tty_ldisc

tty_ldisc_N_TTY = {

TTY_LDISC_MAGIC,      /* magic */

"n_tty",         /* name */

0,                  /*

num */

0,                  /*

flags */

n_tty_open,         /* open */

n_tty_close,         /* close */

n_tty_flush_buffer,     /* flush_buffer */

n_tty_chars_in_buffer,      /* chars_in_buffer */

当对ptm或pts写时会执行tty_write(所有的tty设备文件的操作都会执行tty_ops的相应的函数,这个将在后面讲):

tty_write()

tty_write(struct

inode * inode, struct file * file, const char * buf, int count)

首先从file->private_data里面取出一个tty_struct结构,得到相关的tty的信息,

此函数中调用do_tty_write()

return do_tty_write(tty->ldisc.write,

inode, tty, file,

(const unsigned char *)buf,

(unsigned int)count);

do_tty_write(tty->ldisc.write,

tty, file,//最后调用do_tty_write()

注:大概考虑到一次不能写太多字符do_tty_write中将字符分批输出。

反复调用传来的write函数:ret = write(tty,

file, buf, size);

因为默认是N_TTY行规则。

这个函数其实就是tty->ldisc.write,即write_chan()

static ssize_t

write_chan(struct tty_struct * tty, struct file * file,

const unsigned char * buf, size_t nr)

{

/* Job control check -- must be done at

start (POSIX.1 7.1.1.4). */

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;

}

if (O_OPOST(tty) &&

!(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {

while (nr > 0) {

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->driver->flush_chars)

tty->driver->flush_chars(tty);

} else {

while (nr > 0) {

c =

tty->driver->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;

}

然后就执行driver->write即

ptm的driver是ptm_driver,pts的driver是pts_driver,这两个都是在 unix98_pty_init初始化。

static void __init unix98_pty_init(void)

{

devfs_mk_dir("pts");

ptm_driver->init_termios.c_iflag = 0;

ptm_driver->init_termios.c_oflag = 0;

ptm_driver->init_termios.c_cflag =

B38400 | CS8 | CREAD;

ptm_driver->init_termios.c_lflag = 0;

ptm_driver->flags =

TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |

TTY_DRIVER_NO_DEVFS |

TTY_DRIVER_DEVPTS_MEM;

ptm_driver->other = pts_driver;

tty_set_operations(ptm_driver, &pty_ops);

ptm_driver->ioctl = pty_unix98_ioctl;

pts_driver->type =

TTY_DRIVER_TYPE_PTY;

pts_driver->subtype = PTY_TYPE_SLAVE;

pts_driver->init_termios =

tty_std_termios;

pts_driver->init_termios.c_cflag =

B38400 | CS8 | CREAD;

pts_driver->flags =

TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |

TTY_DRIVER_NO_DEVFS |

TTY_DRIVER_DEVPTS_MEM;

pts_driver->other = ptm_driver;

tty_set_operations(pts_driver, &pty_ops);

if (tty_register_driver(ptm_driver))

panic("Couldn't register

Unix98 ptm driver");

if (tty_register_driver(pts_driver))

panic("Couldn't register Unix98 pts driver");

static struct

tty_operations pty_ops = {

.open = pty_open,

.close = pty_close,

.write = pty_write,

.write_room = pty_write_room,

.flush_buffer = pty_flush_buffer,

.chars_in_buffer = pty_chars_in_buffer,

.unthrottle = pty_unthrottle,

.set_termios = pty_set_termios,

};

故执行driver->write就会执行pty_write

pty_write;

static int pty_write(struct tty_struct *

tty, const unsigned char *buf, int count)

{

struct tty_struct *to = tty->link;

int   c;

if (!to || tty->stopped)

return 0;

c = to->ldisc.receive_room(to);

if (c > count)

c = count;

to->ldisc.receive_buf(to, buf, NULL,

c);

//这个的效果是将数据放到终端队中另一个终端的read_Buf,就实现了虚拟设备的作用,即pts的输出会放到ptm的read_buf,ptm的输出会放到pts的read_buf。

return c;

}

当执行read时,调用tty_read

tty_read直接是调用N_TTY行规的read,是read_chan.

当对ptmx执行ioctl时:

会调用

int tty_ioctl(struct

inode * inode, struct file * file,

unsigned int cmd, unsigned long arg)

{

switch (cmd) {

case TIOCSTI:

return tiocsti(tty, p);

case TIOCGWINSZ:

return tiocgwinsz(tty, p);

}

if (tty->driver->ioctl) {

retval = (tty->driver->ioctl)(tty,

file, cmd, arg);

if (retval != -ENOIOCTLCMD)

return retval;

}

ld = tty_ldisc_ref_wait(tty);

retval = -EINVAL;

if (ld->ioctl) {

retval = ld->ioctl(tty, file,

cmd, arg);

if (retval == -ENOIOCTLCMD)

retval = -EINVAL;

}

}

然后就会执行drvier->ioctl,就是 pty_unix98_ioctl,

static int

pty_unix98_ioctl(struct tty_struct *tty, struct file *file,

unsigned int cmd, unsigned long arg)

{

switch

(cmd) {

case

TIOCSPTLCK: /* Set PT Lock (disallow slave open) */

return

pty_set_lock(tty, (int __user *)arg);

case TIOCGPTN: /* Get PT Number */

return put_user(tty->index, (unsigned int __user

*)arg);

//获取对应的pts编号。

}

}

当最tty设备文件进行操作时:file->f_ops=tty_fops

static struct file_operations

tty_fops = {

.llseek           = no_llseek,

.read             = tty_read,

.write            = tty_write,

.poll        = tty_poll,

.ioctl              = tty_ioctl,

.open            = tty_open,

.release  = tty_release,

.fasync          = tty_fasync,

};

因为tty_regiser_driver会将tty字符设备的fops赋值为tty_fops:

int

tty_register_driver(struct tty_driver *driver)

{

cdev_init(&driver->cdev, &tty_fops);

driver->cdev.owner = driver->owner;

error = cdev_add(&driver->cdev,

dev, driver->num);

}

当执行close时:

asmlinkage long sys_close(unsigned int fd)

{

….....................

return filp_close(filp, files);

….......................

}

int filp_close(struct

file *filp, fl_owner_t id)

{

if (filp->f_op &&

filp->f_op->flush)

retval =

filp->f_op->flush(filp);

fput(filp);

return retval;

}

void fastcall

__fput(struct file *file)

{

if (file->f_op &&

file->f_op->release)

file->f_op->release(inode, file);

}

static int

tty_release(struct inode * inode, struct file * filp)

{

lock_kernel();

release_dev(filp);

unlock_kernel();

return 0;

}

static void

release_dev(struct file * filp)

{

if (tty->driver->close)

tty->driver->close(tty, filp);

}

static void

pty_close(struct tty_struct * tty, struct file * filp)

{

if (tty->driver->subtype ==

PTY_TYPE_MASTER) {

set_bit(TTY_OTHER_CLOSED,

&tty->flags);

#ifdef

CONFIG_UNIX98_PTYS

if (tty->driver == ptm_driver)

devpts_pty_kill(tty->index);

#endif

tty_vhangup(tty->link);

}

}

void

devpts_pty_kill(int number)

{

struct dentry *dentry = get_node(number);

if (!IS_ERR(dentry)) {

struct inode *inode =

dentry->d_inode;

if (inode) {

inode->i_nlink--;

d_delete(dentry);

dput(dentry);

}

dput(dentry);

}

up(&devpts_root->d_inode->i_sem);

}

从上面可以看出,当关闭/dev/ptmx的file时,会关闭其对应的pts设备文件/dev/pts/x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值