ptmx/pts

来源:互联网 发布:深圳软件开发外包公司 编辑:程序博客网 时间:2024/06/05 03:45

简介

ptmx,pts pseudo terminal master and slave
ptmx与pts配合实现pty(伪终端)
在telnet,ssh等远程终端工具中会使用到pty,通常的数据流是这样的
telnetd进程 ---> /dev/ptmx(master) ---> /dev/pts/?(slave) ---> getty
telnetd进程收到网络中的数据后,将数据丢给ptmx,ptmx像管道一样将数据丢给pts/?,getty进程从pts/?读取数据传递给shell去执行。

linux支持的两种pty
a. UNIX98 pseudoterminal,使用的是devpts文件系统,挂载在/dev /pts目录
b. 在UNIX98 pseudoterminal之前,master pseudoterminal名字为/dev/ptyp0,…,slave pseudoterminal名字为/dev/ttyp0,…,这个方法需要预先分配好很多的设备节点。

只有在open /dev/ptmx程序不退出的情况下,/dev/pts/目录下才会有对应的设备节点
在程序执行”open /dev/ptmx”的时候会在/dev/pts/目录下生成一个设备节点,比如0,1…,但是当程序退出的时候这个设备节点就消失了。可以通过如下一个例子演示在”open /dev/ptmx”的时候在/dev/pts目录下生成的设备节点

$ ls /dev/pts; ls /dev/pts </dev/ptmx0  1  2  ptmx0  1  2  3  ptmx

可见在重定向/dev/ptmx的时候在/dev/pts目录下多了个设备节点3,而当上面这个shell结束的时候再次ls /dev/pts目录,设备节点3又消失了。


程序

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <pty.h>int main(){        int fd_m, fd_s;        int len;        const char *pts_name;        char send_buf[64] = "abc\ndefghijk\nlmn";        char recv_buf[64] = {0};        fd_m = open("/dev/ptmx", O_RDWR | O_NOCTTY);        if (fd_m < 0) {                printf("open /dev/ptmx fail\n");                return -1;        }        if (grantpt(fd_m) < 0 || unlockpt(fd_m) < 0) {                printf("grantpt and unlockpt fail\n");                goto err;        }        pts_name = (const char *)ptsname(fd_m);        fd_s = open(pts_name, O_RDONLY | O_NOCTTY);        if (fd_s < 0) {                printf("open /dev/ptmx fail\n");                goto err;        }        len = write(fd_m, send_buf, strlen(send_buf));        printf("write len=%d\n", len);        len = read(fd_s, recv_buf, sizeof(recv_buf));        printf("read len=%d, recv_buf=[%s]\n", len, recv_buf);        len = read(fd_s, recv_buf, sizeof(recv_buf));        printf("read len=%d, recv_buf=[%s]\n", len, recv_buf);        close(fd_m);        close(fd_s);        return 0;err:        if (fd_m)                close(fd_m);        if (fd_s)                close(fd_s);        return -1;}

上面这段程序的输出如下:
这里写图片描述
read只有遇到换行符’\n’的时候才会返回,否则遇不到的话一直阻塞在那里。

每open /dev/ptmx就会得到一个新的文件描述符,并且在/dev/pts/目录下生成一个与这个文件描述符对应的新的设备节点
当进程open “/dev/ptmx”的时候,获得了一个新的pseudoterminal master(PTM)的文件描述符,同时会在/dev/pts目录下自动生成一个新的pseudoterminal slave(PTS)设备。每次open “/dev/ptmx”会得到一个不同的PTM文件描述符(多次open会得到多个文件描述符),并且有和这个PTM描述符关联的PTS。

grantpt, unlockpt: 在每次打开pseudoterminal slave的时候,必须传递对应的PTM的文件描述符。grantpt以获得权限,然后调用unlockpt解锁
ptsname: 将PTM的文件描述符作为参数,会得到该描述符对应的PTS的路径

向PTM写的数据可以从PTS读出来,向PTS写的数据可以从PTM读出来。


原理

对ptmx执行open操作,将创建一对tty主从设备

tty_init    cdev_init(&ptmx_cdev, &ptmx_fops);//创建了/dev/ptmx设备节点    //此时/dev/ptmx设备节点的open函数为ptmx_fops.ptmx_open()static int ptmx_open(struct inode *inode, struct file *filp){    ...    idr_ret = idr_get_new(&allocated_ptys, NULL, &index);    ...    //NR_UNIX98_PTY_DEFAULT也就是4096if (index >= pty_limit) {        ...    }    ...    mutex_lock(&tty_mutex);    //以index为pts的设备索引号,创建成对的主从设备ptmx和pts    retval = init_dev(ptm_driver, index, &tty);    mutex_unlock(&tty_mutex);    ...    retval = ptm_driver->open(tty, filp);    ...

所以在fd_m = open("/dev/ptmx", O_RDWR)操作之后,将产生一个成对的ptmx和pts主从pty设备,并返回ptmx对应的文件描述符。

调用ioctl获得与ptmx对应的pts的设备节点

tty_ioctl    tty->driver->ioctl    //在unix98_pty_init()中,仅对ptmx主设备赋予了ioctl操作,`ptm_driver->ioctl =pty_unix98_ioctl;`    pty_unix98_ioctlstatic 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 */        //当前tty对应的为ptmx结构,它的index就是与之配对的pts        return put_user(tty->index, (unsigned int __user *)arg);    }    return -ENOIOCTLCMD;}

看看glibc库中如何封装ptsname函数

char* ptsname( int fd ){    unsigned int pty_num;    static char buff[64];    //最终调用上面的pty_unix98_ioctl获取当前ptmx主设备对应的pty从设备号.    if ( ioctl( fd, TIOCGPTN, &pty_num ) != 0 )        return NULL;    //格式化为/dev/pts/0,/dev/pts/1等,即:pts对应的文件全路径.    snprintf( buff, sizeof(buff), "/dev/pts/%u", pty_num );    return buff;}

adb中遇到的场景

我在移植adb到linux平台的时候,涉及到pts、ptmx,需要在内核中配置如下:

Device driver`    Character devices        [*]Enable TTY            [*]Unix98 PTY support                [*]Support multiple instances of devptsConfig busybox setting    Busybox Setting        General Configuration            Use the devpts filesystem for unix98 PTYs

选项选择好编译,再次去除这些选项之后再编译会出问题,报如下错误:

    WARNING: arch/rlx/bsp/built-in.o(.text+0x4): Section mismatch in reference from the function >disable_early_printk() to the variable .init.data:promcons_output    The function disable_early_printk() references    the variable __initdata promcons_output.    This is often because disable_early_printk lacks a __initdata    annotation or the annotation of promcons_output is wrong.    WARNING: arch/rlx/bsp/built-in.o(.text+0x10): Section mismatch in reference from the function >disable_early_printk() to the variable .init.data:promcons_output    The function disable_early_printk() references    the variable __initdata promcons_output.    This is often because disable_early_printk lacks a __initdata    annotation or the annotation of promcons_output is wrong.    WARNING: arch/rlx/bsp/built-in.o(.data+0x1710): Section mismatch in reference from the variable >rts1_camera_device to the variable .init.rodata:rts1_soc_camera_pdata    The variable rts1_camera_device references    the variable __initconst rts1_soc_camera_pdata    If the reference is valid then annotate the    variable with __init* or __refdata (see linux/init.h) or name the variable:    _template, _timer, _sht, _ops, _probe, _probe_one, *_console

解决办法:
不用去修改这些文件的错误,虽然这些文件本身编译可能有问题。
可以这么做,在busybox和linux内核目录下做make clean和make distclean
然后再重新编译。
实际上后来发现,busybox中不需要配置,只要在内核中配置就行了。

内核配置并且编译之后,系统支持pts
进入终端执行:mount -t devpts none /dev/pts
如果没有/dev/pts这个目录的话,执行mkdir -p /dev/pts创建一个目录


参考文章

  1. ptmx(4) - Linux man page
  2. Linux中的伪终端编程
  3. 浅析terminal创建时ptmx和pts关系
  4. linux-3.14/Documentation/filesystems/devpts.txt
  5. 5.
0 0