<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Farseerfc的小窩 - tty</title><link href="//farseerfc.me/" rel="alternate"></link><link href="//farseerfc.me/feeds/tag-tty.atom.xml" rel="self"></link><id>//farseerfc.me/</id><updated>2014-12-12T17:06:00+09:00</updated><entry><title>從非緩衝輸入流到 Linux 控制檯的歷史</title><link href="//farseerfc.me/from-unbuffered-stdin-to-history-of-linux-tty.html" rel="alternate"></link><published>2014-12-12T17:06:00+09:00</published><updated>2014-12-12T17:06:00+09:00</updated><author><name>farseerfc</name></author><id>tag:farseerfc.me,2014-12-12:/from-unbuffered-stdin-to-history-of-linux-tty.html</id><summary type="html">
&lt;p&gt;這篇也是源自於水源C板上板友的一個問題，涉及Linux上的控制檯的實現方式和歷史原因。因爲內容比較長，所以在這裏再排版一下發出來。
&lt;a class="reference external" href="http://bbs.sjtu.edu.cn/bbstcon,board,C,reid,1418138991,file,M.1418138991.A.html"&gt;原帖在這裏&lt;/a&gt; 。&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;可以設置不帶緩衝的標準輸入流嗎？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WaterElement(UnChanged) 於 2014年12月09日23:29:51 星期二 問到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;請問對於標準輸入流可以設置不帶緩衝嗎？比如以下程序&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="code-line"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fdopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN_FILENO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;setvbuf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_IONBF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;fgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"buffer …&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;</summary><content type="html">
&lt;p&gt;這篇也是源自於水源C板上板友的一個問題，涉及Linux上的控制檯的實現方式和歷史原因。因爲內容比較長，所以在這裏再排版一下發出來。
&lt;a class="reference external" href="http://bbs.sjtu.edu.cn/bbstcon,board,C,reid,1418138991,file,M.1418138991.A.html"&gt;原帖在這裏&lt;/a&gt; 。&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id7"&gt;可以設置不帶緩衝的標準輸入流嗎？&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WaterElement(UnChanged) 於 2014年12月09日23:29:51 星期二 問到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;請問對於標準輸入流可以設置不帶緩衝嗎？比如以下程序&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="code-line"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fdopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN_FILENO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;setvbuf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_IONBF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;fgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"buffer is:%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;似乎還是需要在命令行輸入後按回車纔會讓 &lt;code class="code"&gt;
fgets&lt;/code&gt;
 返回，不帶緩衝究竟體現在哪裏？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id8"&gt;這和緩存無關，是控制檯的實現方式的問題。&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;再講細節一點，這裏有很多個程序和設備。以下按 linux 的情況講：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;終端模擬器窗口（比如xterm）收到鍵盤事件&lt;/li&gt;
&lt;li&gt;終端模擬器(xterm)把鍵盤事件發給虛擬終端 pty1&lt;/li&gt;
&lt;li&gt;pty1 檢查目前的輸入狀態，把鍵盤事件轉換成 stdin 的輸入，發給你的程序&lt;/li&gt;
&lt;li&gt;你的程序的 c 庫從 stdin 讀入一個輸入，處理&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;標準庫說的輸入緩存是在 4 的這一步進行的。而行輸入是在 3 的這一步被緩存起來的。&lt;/p&gt;
&lt;p&gt;終端pty有多種狀態，一般控制檯程序所在的狀態叫「回顯行緩存」狀態，這個狀態的意思是:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;所有普通字符的按鍵，會回顯到屏幕上，同時記錄在行緩存區裏。&lt;/li&gt;
&lt;li&gt;處理退格( &lt;kbd class="kbd"&gt;
BackSpace&lt;/kbd&gt;
 )，刪除( &lt;kbd class="kbd"&gt;
Delete&lt;/kbd&gt;
 )按鍵爲刪掉字符，左右按鍵移動光標。&lt;/li&gt;
&lt;li&gt;收到回車的時候把整個一行的內容發給stdin。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;參考： &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Cooked_mode"&gt;http://en.wikipedia.org/wiki/Cooked_mode&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;同時在Linux/Unix下可以發特殊控制符號給pty讓它進入「raw」狀態，這種狀態下按鍵
不會被回顯，顯示什麼內容都靠你程序自己控制。
如果你想得到每一個按鍵事件需要用raw狀態，這需要自己控制回顯自己處理緩衝，
簡單點的方法是用 readline 這樣的庫（基本就是「回顯行緩存」的高級擴展，支持了
Home/End，支持歷史）或者 ncurses 這樣的庫（在raw狀態下實現了一個簡單的窗口/
事件處理框架）。&lt;/p&gt;
&lt;p&gt;參考： &lt;a class="reference external" href="http://en.wikipedia.org/wiki/POSIX_terminal_interface#History"&gt;http://en.wikipedia.org/wiki/POSIX_terminal_interface#History&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;除此之外， &lt;kbd class="kbd"&gt;
Ctrl-C&lt;/kbd&gt;
 轉換到 SIGINT ， &lt;kbd class="kbd"&gt;
Ctrl-D&lt;/kbd&gt;
 轉換到 EOF 這種也是在 3 這一步做的。&lt;/p&gt;
&lt;p&gt;以及，有些終端模擬器提供的 &lt;kbd class="kbd"&gt;
Ctrl-Shift-C&lt;/kbd&gt;
 表示複製這種是在 2 這一步做的。&lt;/p&gt;
&lt;p&gt;以上是 Linux/unix 的方式。 Windows的情況大體類似，只是細節上有很多地方不一樣：&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;窗口事件的接收者是創建 cmd 窗口的 Win32 子系統。&lt;/li&gt;
&lt;li&gt;Win32子系統接收到事件之後，傳遞給位於 命令行子系統 的 cmd 程序&lt;/li&gt;
&lt;li&gt;cmd 程序再傳遞給你的程序。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Windows上同樣有類似行緩存模式和raw模式的區別，只不過實現細節不太一樣。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="strace"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id9"&gt;strace查看了下&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;WaterElement(UnChanged) 於 2014年12月10日21:53:54 星期三 回復：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;感謝FC的詳盡解答。&lt;/p&gt;
&lt;p&gt;用strace查看了下，設置標準輸入沒有緩存的話讀每個字符都會調用一次 &lt;code class="code"&gt;
read&lt;/code&gt;
 系統調用，
比如輸入abc：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="code-line"&gt;&lt;span&gt;&lt;/span&gt;read(0, abc&lt;/span&gt;
&lt;span class="code-line"&gt;"a", 1)                         = 1&lt;/span&gt;
&lt;span class="code-line"&gt;read(0, "b", 1)                         = 1&lt;/span&gt;
&lt;span class="code-line"&gt;read(0, "c", 1)                         = 1&lt;/span&gt;
&lt;span class="code-line"&gt;read(0, "\n", 1)                        = 1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果有緩存的話就只調用一次了 &lt;code class="code"&gt;
read&lt;/code&gt;
 系統調用了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="code-line"&gt;&lt;span&gt;&lt;/span&gt;read(0, abc&lt;/span&gt;
&lt;span class="code-line"&gt;"abc\n", 1024)                  = 4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="raw-mode"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id10"&gt;如果想感受一下 raw mode&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;沒錯，這個是你的進程內C庫做的緩存，tty屬於字符設備所以是一個一個字符塞給你的
程序的。&lt;/p&gt;
&lt;p&gt;如果想感受一下 raw mode 可以試試下面這段程序（沒有檢測錯誤返回值）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="code-line"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;termios.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ttyfd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STDIN_FILENO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;termios&lt;/span&gt; &lt;span class="n"&gt;orig_termios&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;/* reset tty - useful also for restoring the terminal when this process&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;   wishes to temporarily relinquish the tty&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;tty_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* flush and reset */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tcsetattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttyfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;TCSAFLUSH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;orig_termios&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;/* put terminal in raw mode - see termio(7I) for modes */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;tty_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;termios&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orig_termios&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* copy original and then modify below */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* input modes - clear indicated ones giving: no break, no CR to NL,&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;       no parity check, no strip char, no start/stop output (sic) control */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_iflag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BRKINT&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ICRNL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;INPCK&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ISTRIP&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;IXON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* output modes - clear giving: no post processing such as NL to CR+NL */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_oflag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OPOST&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* control modes - set 8 bit chars */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cflag&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CS8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* local modes - clear giving: echoing off, canonical off (no erase with&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;       backspace, ^U,...),  no extended functions, no signal chars (^Z,^C) */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_lflag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ECHO&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ICANON&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;IEXTEN&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ISIG&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* control chars - set return condition: min number of bytes and timer */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VMIN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VTIME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* after 5 bytes or .8 seconds&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="cm"&gt;                                                after first byte seen      */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VMIN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VTIME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* immediate - anything       */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VMIN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VTIME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* after two bytes, no timer  */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VMIN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_cc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VTIME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* after a byte or .8 seconds */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="cm"&gt;/* put terminal in raw mode after flushing */&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;tcsetattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttyfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;TCSAFLUSH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;atexit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tty_reset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;tty_raw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fdopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttyfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;setvbuf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_IONBF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;fgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"buffer is:%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="code-line"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#id11"&gt;終端上的字符編程&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;vander(大青蛙) 於 2014年12月12日08:52:20 星期五 問到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;學習了！&lt;/p&gt;
&lt;p&gt;進一步想請教一下fc大神。如果我在Linux上做終端上的字符編程，是否除了用ncurses庫
之外，也可以不用該庫而直接與終端打交道，就是你所說的直接在raw模式？
另外，終端類型vt100和linux的差別在哪裏？爲什麼Kevin Boone的KBox配置手冊裏面說必
須把終端類型設成linux，而且要加上terminfo文件，才能讓終端上的vim正常工作？term
info文件又是幹什麼的？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#id12"&gt;Linux控制檯的歷史&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;嗯理論上可以不用 ncurses 庫直接在 raw 模式操縱終端。&lt;/p&gt;
&lt;p&gt;這裏稍微聊一下terminfo/termcap的歷史，詳細的歷史和吐槽參考
&lt;a class="reference external" href="http://web.mit.edu/~simsong/www/ugh.pdf"&gt;Unix hater's Handbook&lt;/a&gt;
第6章 Terminal Insanity。&lt;/p&gt;
&lt;p&gt;首先一個真正意義上的終端就是一個輸入設備（通常是鍵盤）加上一個輸出設備（打印
機或者顯示器）。很顯然不同的終端的能力不同，比如如果輸出設備是打印機的話，顯
示出來的字符就不能刪掉了（但是能覆蓋），而且輸出了一行之後就不能回到那一行了
。再比如顯示器終端有的支持粗體和下劃線，有的支持顏色，而有的什麼都不支持。
早期Unix工作在電傳打字機（TeleTYpe）終端上，後來Unix被port到越來越多的機器上
，然後越來越多類型的終端會被連到Unix上，很可能同一臺Unix主機連了多個不同類型
的終端。由於是不同廠商提供的不同的終端，能力各有不同，自然控制他們工作的方式
也是不一樣的。所有終端都支持回顯行編輯模式，所以一般的面向行的程序還比較好寫
，但是那時候要撰寫支持所有終端的「全屏」程序就非常痛苦，這種情況就像現在瀏覽
器沒有統一標準下寫HTML要測試各種瀏覽器兼容性一樣。
通常的做法是&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;使用最小功能子集&lt;/li&gt;
&lt;li&gt;假設終端是某個特殊設備，不管別的設備。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;水源的代碼源頭 Firebird2000 就是那樣的一個程序，只支持固定大小的vt102終端。&lt;/p&gt;
&lt;p&gt;這時有一個劃時代意義的程序出現了，就是 vi，試圖要做到「全屏可視化編輯」。這在
現在看起來很簡單，但是在當時基本是天方夜譚。
vi 的做法是提出一層抽象，記錄它所需要的所有終端操作，然後有一個終端類型數據庫
，把那些操作映射到終端類型的具體指令上。當然並不是所有操作在所有終端類型上都
支持，所以會有一堆 fallback，比如要「強調」某段文字，在彩色終端上可能
fallback 到紅色，在黑白終端上可能 fallback 到粗體。&lt;/p&gt;
&lt;p&gt;vi 一出現大家都覺得好頂讚，然後想要寫更多類似 vi 這樣的全屏程序。然後 vi 的作
者就把終端抽象的這部分數據庫放出來形成一個單獨的項目，叫 termcap （Terminal
Capibility），對應的描述終端的數據庫就是 termcap 格式。然後 termcap 只是一個
數據庫（所以無狀態）還不夠方便易用，所以後來又有人用 termcap 實現了 curses 。&lt;/p&gt;
&lt;p&gt;再後來大家用 curses/termcap 的時候漸漸發現這個數據庫有一點不足：它是爲 vi 設
計的，所以只實現了 vi 需要的那部分終端能力。然後對它改進的努力就形成了新的
terminfo 數據庫和 pcurses 和後來的 ncurses 。 然後 VIM 出現了自然也用
terminfo 實現這部分終端操作。&lt;/p&gt;
&lt;p&gt;然後麼就是 X 出現了， xterm 出現了，大家都用顯示器了，然後 xterm 爲了兼容各種
老程序加入了各種老終端的模擬模式。不過因爲最普及的終端是 vt100 所以 xterm 默
認是工作在兼容 vt100 的模式下。然後接下來各種新程序（偷懶不用*curses的那些）
都以 xterm/vt100 的方式寫。&lt;/p&gt;
&lt;p&gt;嗯到此爲止是 Unix 世界的黑歷史。&lt;/p&gt;
&lt;p&gt;知道這段歷史的話就可以明白爲什麼需要 TERM 變量配合 terminfo 數據庫纔能用一些
Unix 下的全屏程序了。類比一下的話這就是現代瀏覽器的 user-agent。&lt;/p&gt;
&lt;p&gt;然後話題回到 Linux 。 大家知道 Linux 早期代碼不是一個 OS， 而是 Linus 大神想
在他的嶄新蹭亮的 386-PC 上遠程登錄他學校的 Unix 主機，接收郵件和逛水源（咳咳
）。於是 Linux 最早的那部分代碼並不是一個通用 OS 而只是一個 bootloader 加一個
終端模擬器。所以現在 Linux 內核裏還留有他當年實現的終端模擬器的部分代碼，而這
個終端模擬器的終端類型就是 linux 啦。然後他當時是爲了逛水源嘛所以 linux 終端
基本上是 vt102 的一個接近完整子集。&lt;/p&gt;
&lt;p&gt;說到這裏脈絡大概應該清晰了， xterm終端類型基本模擬 vt100，linux終端類型基本模
擬 vt102。這兩個的區別其實很細微，都是同一個廠商的兩代產品嘛。有差別的地方差
不多就是 &lt;kbd class="kbd"&gt;
Home&lt;/kbd&gt;
 / &lt;kbd class="kbd"&gt;
End&lt;/kbd&gt;
 / &lt;kbd class="kbd"&gt;
PageUp&lt;/kbd&gt;
 / &lt;kbd class="kbd"&gt;
PageDown&lt;/kbd&gt;
 / &lt;kbd class="kbd"&gt;
Delete&lt;/kbd&gt;

這些不在 ASCII 控制字符表裏的按鍵的映射關係不同。&lt;/p&gt;
&lt;p&gt;嗯這也就解釋了爲什麼在linux環境的圖形界面的終端裏 telnet 上水源的話，上面這些
按鍵會錯亂…… 如果設置終端類型是 linux/vt102 的話就不會亂了。在 linux 的
TTY 裏 telnet 也不會亂的樣子。&lt;/p&gt;
&lt;p&gt;寫到這裏纔發現貌似有點長…… 總之可以參考
&lt;a class="reference external" href="http://web.mit.edu/~simsong/www/ugh.pdf"&gt;Unix hater's Handbook&lt;/a&gt;
裏的相關歷史評論和吐槽，那一段非常有意思。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="tech"></category><category term="c"></category><category term="linux"></category><category term="stdio"></category><category term="tty"></category><category term="unix"></category><category term="ugh"></category><category term="ncurses"></category><category term="termcap"></category><category term="terminfo"></category></entry></feed>