ptty.html

来自「这是一个介绍 linux 编程知识的文章。」· HTML 代码 · 共 297 行

HTML
297
字号
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD>    <TITLE>伪终端</TITLE>    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312-80">    <META NAME="COPYRIGHT" CONTENT="魏永明">    <META NAME="AUTHOR" CONTENT="魏永明">    <STYLE>    <!--        H1 { color: #ffff00 }        H2 { color: #ffff00 }        H3 { color: #ff00ff }        H4 { color: #ffff00 }        TD P { color: #b880b8 }        LI { color: #ffffff }        P { color: #00ffff }        PRE { color: #ffffff; font-family: "fixed" }        A:link { color: #00b8ff }        A:visited { color: #ff3366 }    -->    </STYLE></HEAD><BODY TEXT="#ffff00" LINK="#00b8ff" VLINK="#ff3366" BACKGROUND="images/velvet.jpg"><A HREF="terminal.html"><IMG SRC="prev.gif" ALT="Previous"></A><A HREF="graphics.html"><IMG SRC="next.gif" ALT="Next"></A><A HREF="index.html"><IMG SRC="toc.gif" ALT="Contents"></A><H1 ALIGN=CENTER>6.7&nbsp;&nbsp;伪终端<BR><BR></H1><UL>    <LI>概念</LI>    <LI>编程</LI></UL><H3>6.7.1&nbsp;&nbsp;概念</H3><UL>    <LI>为什么需要伪终端</LI><PRE>===============================================================================    * 像 CCE, xterm, vcongui 等程序, 需要截获字符应用程序的输出, 以便在自己的设备上      绘制终端输出.    * 像 xterm, vcongui 等程序, 还要将自己的输入转换成字符应用程序能够理解的输入.     * 利用伪终端, 上述这些程序就可以对字符应用程序的终端输入和输出进行过虑, 从而      实现与普通终端一样的输入和输出处理.===============================================================================</PRE>    <LI>伪终端机制</LI><PRE>===============================================================================    * 伪终端由两部分组成: 主控终端和从属终端, 分别由两个进程处理, 这两个进程往往      是父子进程.    * 父进程打开伪终端的主控终端, 然后调用 fork 派生子进程.    * 子进程建立新的会话, 并打开对应的从属终端, 并将该从属终端复制为标准输入,      标准输出和标准错误.     * 子进程调用 exec 执行新的程序, 该从属终端就形成了新会话的控制终端.    * 对于子进程来说, 从属终端就是它们的标准输入, 标准输出和标准错误, 同时也是      一个终端设备. 因此, 可以采用 6.6 小节中的终端属性进行控制, 但因为并不是	  真正的终端, 因此波特率, 线路控制函数等等将被忽略.    * 任何写入主控终端的数据将成为从属终端的输入; 任何写入从属终端的数据将成为      主控终端的输入. 这样, 主控终端上的进程 (父进程) 就能够为从属终端生成输入,      而且还能够处理从属终端上的输出.===============================================================================</PRE></UL><H3>6.7.2&nbsp;&nbsp;编程</H3><UL>    <LI>演示: vcongui</LI>    <LI>vcongui 的伪终端机制</LI><PRE>===============================================================================        int TermFork (PCONINFO con, PCHILDINFO pChildInfo)        {-------------------------------------------------------------------------------    * vcongui 的窗口线程打开主控终端:            char ls, ln;            // 1. Looking for unused PTY/TTY Master/Slave            /* Open PTY(master) from [pqrs][5-F], in fact p-z is ok? */            /* MasterPty:  pty[p-z][5-f] pty[a-e][5-f]  16*16=256               SlaveTty:   tty[p-z][5-f] tty[a-e][5-f]  16*16=256 */            for (ls = 'p'; ls &lt;= 's'; ls ++) {                for (ln = 5; ln &lt;= 0xF; ln ++) {                    sprintf(con-&gt;ptyName, "/dev/pty%1c%1x", ls, ln);                    if ((con-&gt;masterPty = open (con-&gt;ptyName, O_RDWR)) &gt;= 0)                        break;                }                if (con-&gt;masterPty &gt;= 0)                    break;            }            if (con-&gt;masterPty &lt; 0) {                myMessage ("can not get master pty!\n");                Perror (con-&gt;ptyName);                return -1;            }            con-&gt;ptyName[5] = 't';   /* slave tty */-------------------------------------------------------------------------------    * 调用 fork 派生子进程            if ((con-&gt;childPid = fork()) &lt; 0) {                Perror ("fork");                return -1;            }            else if (con-&gt;childPid == 0) {                // in child process                int   errfd, slavePty;                FILE *errfp;                struct winsize twinsz;                errfd = dup (2);                errfp = fdopen (errfd, "w");                /* I'm child, make me process leader */                setsid ();                // close any no used fd here!!                close (con-&gt;masterPty);                /* Open TTY(slave) */                if ((slavePty = open (con-&gt;ptyName, O_RDWR)) &lt; 0)                    PerrorExit (con-&gt;ptyName);                // Set new TTY's termio with parent's termio.                tcsetattr (slavePty, TCSANOW, (struct termios*)GetOriginalTermIO ());                // Set new terminal window size                twinsz.ws_row = con-&gt;rows;                twinsz.ws_col = con-&gt;cols;                ioctl (slavePty, TIOCSWINSZ, &twinsz);                /* Set std??? to pty, dup2 (oldfd, newfd) */                dup2 (slavePty, 0);                dup2 (slavePty, 1);                dup2 (slavePty, 2);                close (slavePty);                // execute the shell                ChildStart (con, errfp, pChildInfo-&gt;startupMessage,                                   pChildInfo-&gt;startupStr,                                   pChildInfo-&gt;execProg,                                   pChildInfo-&gt;execArgs);            }            return 1; // parent process        }-------------------------------------------------------------------------------    * 窗口欧线程在适当的时候读取主控终端上的数据, 即子进程写入从属终端的数据.        void ReadMasterPty (PCONINFO con)        {            BYTE buff [BUFSIZ + 1];            int nRead;            struct pollfd fd = {con-&gt;masterPty, POLLIN, 0};            int ret;            ret = poll (&fd, 1, 10);   // about 0.01s            if (ret == 0) {                return;            }            else if (ret &gt; 0) {                if ((nRead = read (con-&gt;masterPty, buff, BUFSIZ)) &gt; 0) {                    VtWrite (con, buff, nRead);             /* 后面讲解 */                    TextRefresh (con, true);                /* 后面讲解 */                }            }        }-------------------------------------------------------------------------------    * 在窗口的主消息循环中读取主控终端, 并处理窗口消息            ...            // Enter message loop.            do {                // It is time to read from master pty, and output.                ReadMasterPty (pConInfo);                if (pConInfo-&gt;terminate)                    break;                while (HavePendingMessage (hMainWnd)) {                    if (!GetMessage (&Msg, hMainWnd))                        break;                    DispatchMessage (&Msg);                }            } while (TRUE);            ...-------------------------------------------------------------------------------    * 将用户在窗口中的输入写入主控终端.        void HandleInputChar (PCONINFO con, WPARAM wParam, LPARAM lParam)        {            u_char ch;            if (HIBYTE (wParam)) {                u_char buff [2];                buff [0] = LOBYTE (wParam);                buff [1] = HIBYTE (wParam);                write (con-&gt;masterPty, buff, 2);            }            else {                ch = LOBYTE (wParam);                write (con-&gt;masterPty, &ch, 1);            }        }        void HandleInputKeyDown (PCONINFO con, WPARAM wParam, LPARAM lParam)        {            char buff [50];            con-&gt;kinfo.state = lParam;            con-&gt;kinfo.buff  = buff;            con-&gt;kinfo.pos   = 0;            handle_scancode_on_keydown (wParam, &con-&gt;kinfo);            if (con-&gt;kinfo.pos != 0)                write (con-&gt;masterPty, buff, con-&gt;kinfo.pos);        }        void HandleInputKeyUp (PCONINFO con, WPARAM wParam, LPARAM lParam)        {            char buff [50];            con-&gt;kinfo.state = lParam;            con-&gt;kinfo.buff  = buff;            con-&gt;kinfo.pos   = 0;            handle_scancode_on_keyup (wParam, &con-&gt;kinfo);            if (con-&gt;kinfo.pos != 0)                write (con-&gt;masterPty, buff, con-&gt;kinfo.pos);            con-&gt;kinfo.oldstate = con-&gt;kinfo.state;        }-------------------------------------------------------------------------------    * 在窗口的消息处理函数中处理用户输入                ...                case MSG_KEYUP:                    HandleInputKeyUp (pConInfo, wParam, lParam);                break;                case MSG_KEYDOWN:                    HandleInputKeyDown (pConInfo, wParam, lParam);                break;                case MSG_CHAR:                    HandleInputChar (pConInfo, wParam, lParam);                break;                ...-------------------------------------------------------------------------------    * 在 VtWrite 函数中处理终端的 ESC 序列        o 解释 ESC 序列含义: 位置, 写入字符, 滚屏, 删除字符等.        o 更新终端窗口的数据, 包括指定位置 (行, 列) 上的字符及其属性 (颜色等)-------------------------------------------------------------------------------    * 在 TextRefresh 函数中更新窗口显示===============================================================================</PRE></UL><P><BR><BR></P><P ALIGN=CENTER><IMG SRC="images/striped.gif" NAME="Ruler" ALIGN=BOTTOM WIDTH=532 HEIGHT=13 BORDER=0></P><P><BR><BR></P><A HREF="terminal.html"><IMG SRC="prev.gif" ALT="Previous"></A><A HREF="graphics.html"><IMG SRC="next.gif" ALT="Next"></A><A HREF="index.html"><IMG SRC="toc.gif" ALT="Contents"></A></BODY></HTML>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?