linux虚拟串口及远程访问

跟風遠走 提交于 2019-11-26 11:02:45

 

1. 虚拟终端概念

 

linux中有很多终端,如下简单介绍下各种终端或串口的概念。

1.1 tty:终端设备的统称

ttyTeletypeTeletypeWriter的缩写,中文翻译为电传打字机。电传打字机通常有键盘、收发报器和印字机等组成,是传真机使用以前的通信设备,原理近似电报。后被显示器和键盘所取代所以现在叫终端比较合适。

终端是一种字符型设备,他有多种类型,通常使用tty来简称各种类型的终端设备。

目前,tty一般指控制终端(man 4 tty),设备文件是/dev/ttyx,常用的就是linux默认提供的6个命令行终端,可通过Ctrl+Alt+Fn切换图形界面或终端窗口。在Ubuntu命令行输入tty显示终端:

$ tty

/dev/tty2

1.2 pty:虚拟终端

A pseudoterminal缩写为pty,是虚拟终端或伪终端,可以在终端模拟器(terminal emulator)中运行,man pty查看。pty是成对的逻辑终端设备(即masterslave设备,对master的操作会反映到slave上,对slave的操作也会反映到master上),与实际物理设备无关。A pty is a pair of virtual character devices that provide a bidirectional communication channel. one end is called master; the other end is called the slave.

linux提供了两套虚拟终端接口,BSD-styleSystem V-styleSystem V-style终端也被称为UNIX 98 pseudoterminals,是目前使用的伪终端样式。

An unused UNIX 98 pseudoterminal master is opened by calling posix_openpt(3). (This function opens the master clone device, /dev/ptmx; see pts(4).) After performing any program-specific initializations, changing the ownership and permissions of the slave device using grantpt(3), and unlocking the slave using unlockpt(3)), the corresponding slave device can be opened by passing the name returned by ptsname(3) in a call to open(2).

PTMpseudoterminal masterPTSpseudoterminal slave

/dev/ptmx (UNIX 98 master clone device),所有主设备对应的设备文件都指向/dev/ptmx

/dev/pts/* (UNIX 98 slave devices)

sshTelnet登录远程主机时的终端就是pty,运行tty查看:

$ tty

/dev/pts/11

伪终端是一对虚拟的字符设备,linux内核使用一种符合tty线规程(line discipline)的双向管道连接伪终端的主从设备。主设备上的任何写入操作都会反映到从设备上,反之亦然。从设备上的应用进程可以像使用传统终端一样读取来自主设备上应用程序的输入,以及向主设备应用输出信息。伪终端从设备应用通常是主设备应用的子进程,主应用打开一对伪终端并fork一个子进程,然后子进程打开并使用从设备。

1.3 串行端口终端

与计算机串行端口(RS-232)连接的终端设备,对应的设备文件名称为/dev/tty+类型+设备编号,如/dev/ttyS0S表示设备类型,0为指定类型下的设备编号。这里的串行端口可以是通过硬件或软件模拟的,如USB转串口,虚拟串口。

 

2. 多个虚拟终端

Unix 98伪终端使用流程如下:

1.使用posix_openpt打开master

  1. 使用grantpt设置调用进程为slave的属主并允许其对slave进行读写操作;

  2. 使用unlockptslave解锁;

  3. 使用ptsname返回slave的设备名;

  4. 使用open打开slave设备并进行读写操作。

上述函数都来自glibc库。伪终端编程更常用的APIopenpty,直接实现了上述流程的所有步骤。login_tty函数用于实现在指定的终端上启动登录会话。forkpty函数整合了openptyforklogin_tty,在网络服务程序可用于为新登录用户打开一对伪终端,并创建相应的会话子进程。

注意:使用openttylogin_ptyforkpty需要链接util库。

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pty.h>
#include <utmp.h>
#include <errno.h>

#define SLAVE_DEV_NAME_MAX_LEN    128
#define PTY_BUFF_MAX_LEN        1024

int main(int argc, char *argv[])
{
    int mpty = 0, spty = 0;
    int rv = 0, n = 0;
    char spty_name[SLAVE_DEV_NAME_MAX_LEN]={0};
    char buf[PTY_BUFF_MAX_LEN] = {0};

    fd_set rfds;

    rv = openpty(&mpty, &spty, spty_name, NULL, NULL);
    if(-1 == rv){
        perror("Failed to get a pty");
        return -1;
    }

    printf("Get a pty pair, FD -- master[%d], slave[%d]\n", mpty, spty);
    printf("Slave name is %s\n", spty_name);

    FD_ZERO(&rfds);
    FD_SET(mpty, &rfds);

    while(1){
        rv = select(mpty+1, &rfds, NULL, NULL, NULL);
        if(rv < 0){
            perror("Failed to select");
            return -1;
        }

        if(FD_ISSET(mpty, &rfds)){
            n = read(mpty, buf, PTY_BUFF_MAX_LEN);
            if(n > 0){
            //    memset(buf+n, 0, PTY_BUFF_MAX_LEN-n);
                printf("recv [%d] bytes:[%s]\n", n, buf);
            } else if (n == 0){
                printf("Slave closed\n");
                break;
            } else {
                if(errno == EINTR){
                    continue;
                }
                perror("Failed to read the master\n");
                return -1;
            }
        }
    }

    close(mpty);
    close(spty);
    return 0;
}

 

编译及运行:

 

gcc pty_test.c -o pty_test -lutil -Wall
$ ./pty_test
Get a pty pair, FD -- master[3], slave[4]
Slave name is /dev/pts/6
recv [1] bytes:[1]
recv [11] bytes:[hello world]

 

 

 

另一终端:

 

$ echo -n "1" > /dev/pts/6
$ echo -n "hello world" > /dev/pts/6

 

 

 

每次运行上述程序,生成一个虚拟终端口(slave),由此同一主机可运行多个虚拟终端口(slave)。可通过文件/proc/sys/kernel/pty/max查询或修改伪终端数量。

 

 

 

 

$ cat /proc/sys/kernel/pty/max
4096
  1. 远程访问串口

通过网络远程访问串口,首先需要把串口虚拟化网络端口,之后在网络中的另外一个主机上通过Telnet等工具直接访问该网络端口,或者把网络端口逆向为一个虚拟串口,进而通过minicom等工具进行访问。

socat工具可以实现上述功能。如本地(虚拟串口)/dev/pts6,主机IP192.168.134.144,主机端口54321,对端主机虚拟串口文件tty.virt001,可通过如下步骤测试。

主机1串口转TCP端口:

sudo socat tcp-l:54321,reuseaddr,fork file:/dev/pts/6,waitlock=/var/run/ttypts.lock,clocal=1,cs8,nonblock=1,ixoff=0,ixon=0,ispeed=9600,ospeed=9600,raw,echo=0,crtscts=0

主机2TCP端口转虚拟串口:

sudo socat pty,link=/dev/tty.virt001,waitslave tcp:192.168.134.144:54321

主机2远程访问串口:

sudo minicom -D /dev/tty.virt001

telnet 192.168.134.144 54321

  1. 虚拟终端双向收发

上述程序测试示例中由于ptmpts在一个程序中,没有控制ptm的发送,不便于测试观察,网上有程序实现用两组虚拟终端中两个slave配对,从而基于串口的双向数据收发。

 

#!/usr/bin/env python3
#--coding = utf-8 --

import pty
import os 
import select

def mkpty():
    master1, slave = pty.openpty()
    slaveName1 = os.ttyname(slave)
    master2, slave = pty.openpty()
    slaveName2 = os.ttyname(slave)
    print ('\nslave device names: ', slaveName1, slaveName2)
    return master1, master2

if __name__ == "__main__":
    master1, master2 = mkpty()
    while True:
        rl, wl, el = select.select([master1, master2], [], [], 1)
        for master in rl:
            data = os.read(master, 128)
            print ("read %d data:" %len(data))
            if master == master1:
                os.write(master2, data)
            else :
                os.write(master1, data)

 

上述程序用python实现了两个虚拟终端slave双向收发。


参考:

    1. Linux终端简介与pty编程

    2. 串口虚拟化:通过网络访问串口

    3. Linux下的虚拟终端(可用于在本机上模拟串口进行调试)

    4. Ubuntu 下使用虚拟串口进行开发测试

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!