linux tty pty pts tts概念 区别
1. tty(終端設(shè)備的統(tǒng)稱):
tty一詞源于Teletypes,或者teletypewriters,原來指的是電傳打字機,是通過串行線用打印機鍵盤通過閱讀和發(fā)送信息的東西,后 來這東西被鍵盤與顯示器取代,所以現(xiàn)在叫終端比較合適。
終端是一種字符型設(shè)備,它有多種類型,通常使用tty來簡稱各種類型的終端設(shè)備。
2. pty(虛擬終端):
但是如果我們遠程telnet到主機或使用xterm時不也需要一個終端交互么?是的,這就是虛擬終端pty(pseudo-tty)
3. pts/ptmx(pts/ptmx結(jié)合使用,進而實現(xiàn)pty):
pts(pseudo-terminal slave)是pty的實現(xiàn)方法,與ptmx(pseudo-terminal master)配合使用實現(xiàn)pty。
Linux終端:
在Linux系統(tǒng)的設(shè)備特殊文件目錄/dev/下,終端特殊設(shè)備文件一般有以下幾種:
1、串行端口終端(/dev/ttySn)
串行端口終端(Serial Port Terminal)是使用計算機串行端口連接的終端設(shè)備。計算機把每個串行端口都看作是一個字符設(shè)備。有段時間這些串行端口設(shè)備通常被稱為終端設(shè)備,因為那時它的最大用途就是用來連接終端。這些串行端口所對應(yīng)的設(shè)備名稱是/dev/tts/0(或/dev/ttyS0), /dev/tts/1(或/dev/ttyS1)等,設(shè)備號分別是(4,0), (4,1)等,分別對應(yīng)于DOS系統(tǒng)下的COM1、COM2等。若要向一個端口發(fā)送數(shù)據(jù),可以在命令行上把標(biāo)準(zhǔn)輸出重定向到這些特殊文件名上即可。例如,在命令行提示符下鍵入:echo test > /dev/ttyS1會把單詞”test”發(fā)送到連接在ttyS1(COM2)端口的設(shè)備上。可接串口來實驗。
2、偽終端(/dev/pty/)
偽終端(Pseudo Terminal)是成對的邏輯終端設(shè)備(即master和slave設(shè)備, 對master的操作會反映到slave上)。
例如/dev/ptyp3和/dev/ttyp3(或者在設(shè)備文件系統(tǒng)中分別是/dev/pty/m3和 /dev/pty/s3)。它們與實際物理設(shè)備并不直接相關(guān)。如果一個程序把ptyp3(master設(shè)備)看作是一個串行端口設(shè)備,則它對該端口的讀/ 寫操作會反映在該邏輯終端設(shè)備對應(yīng)的另一個ttyp3(slave設(shè)備)上面。而ttyp3則是另一個程序用于讀寫操作的邏輯設(shè)備。
這樣,兩個程序就可以通過這種邏輯設(shè)備進行互相交流,而其中一個使用ttyp3的程序則認(rèn)為自己正在與一個串行端口進行通信。這很象是邏輯設(shè)備對之間的管道操作。對于ttyp3(s3),任何設(shè)計成使用一個串行端口設(shè)備的程序都可以使用該邏輯設(shè)備。但對于使用ptyp3的程序,則需要專門設(shè)計來使用 ptyp3(m3)邏輯設(shè)備。
例如,如果某人在網(wǎng)上使用telnet程序連接到你的計算機上,則telnet程序就可能會開始連接到設(shè)備 ptyp2(m2)上(一個偽終端端口上)。此時一個getty程序就應(yīng)該運行在對應(yīng)的ttyp2(s2)端口上。當(dāng)telnet從遠端獲取了一個字符時,該字符就會通過m2、s2傳遞給 getty程序,而getty程序就會通過s2、m2和telnet程序往網(wǎng)絡(luò)上返回”login:”字符串信息。這樣,登錄程序與telnet程序就通過“偽終端”進行通信。通過使用適當(dāng)?shù)能浖?#xff0c;就可以把兩個甚至多個偽終端設(shè)備連接到同一個物理串行端口上。
在使用設(shè)備文件系統(tǒng) (device filesystem)之前,為了得到大量的偽終端設(shè)備特殊文件,使用了比較復(fù)雜的文件名命名方式。因為只存在16個ttyp(ttyp0—ttypf) 的設(shè)備文件,為了得到更多的邏輯設(shè)備對,就使用了象q、r、s等字符來代替p。例如,ttys8和ptys8就是一個偽終端設(shè)備對。不過這種命名方式目前仍然在RedHat等Linux系統(tǒng)中使用著。
但Linux系統(tǒng)上的Unix98并不使用上述方法,而使用了”pty master”方式,例如/dev/ptm3。它的對應(yīng)端則會被自動地創(chuàng)建成/dev/pts/3。這樣就可以在需要時提供一個pty偽終端。目錄 /dev/pts是一個類型為devpts的文件系統(tǒng),并且可以在被加載文件系統(tǒng)列表中看到。雖然“文件”/dev/pts/3看上去是設(shè)備文件系統(tǒng)中的一項,但其實它完全是一種不同的文件系統(tǒng)。
即: TELNET ---> TTYP3(S3: slave) ---> PTYP3(M3: master) ---> GETTY
=========================================================================
實驗:
1、在X下打開一個或N個終端窗口
2、#ls /dev/pt*
3、關(guān)閉這個X下的終端窗口,再次運行;比較兩次輸出信息就明白了。
在RHEL4環(huán)境下: 輸出為/dev/ptmx /dev/pts/1存在一(master)對多(slave)的情況
=========================================================================
3、控制終端(/dev/tty)
如 果當(dāng)前進程有控制終端(Controlling Terminal)的話,那么/dev/tty就是當(dāng)前進程的控制終端的設(shè)備特殊文件。可以使用命令”ps –ax”來查看進程與哪個控制終端相連。對于你登錄的shell,/dev/tty就是你使用的終端,設(shè)備號是(5,0)。使用命令”tty”可以查看它具體對應(yīng)哪個實際終端設(shè)備。/dev/tty有些類似于到實際所使用終端設(shè)備的一個聯(lián)接。
4、控制臺終端(/dev/ttyn, /dev/console)
在Linux 系統(tǒng)中,計算機顯示器通常被稱為控制臺終端 (Console)。它仿真了類型為Linux的一種終端(TERM=Linux),并且有一些設(shè)備特殊文件與之相關(guān)聯(lián):tty0、tty1、tty2 等。當(dāng)你在控制臺上登錄時,使用的是tty1。使用Alt+[F1—F6]組合鍵時,我們就可以切換到tty2、tty3等上面去。tty1–tty6等稱為虛擬終端,而tty0則是當(dāng)前所使用虛擬終端的一個別名,系統(tǒng)所產(chǎn)生的信息會發(fā)送到該終端上。因此不管當(dāng)前正在使用哪個虛擬終端,系統(tǒng)信息都會發(fā)送到控制臺終端上。你可以登錄到不同的虛擬終端上去,因而可以讓系統(tǒng)同時有幾個不同的會話期存在。只有系統(tǒng)或超級用戶root可以向 /dev/tty0進行寫操作 即下例:
1、# tty(查看當(dāng)前TTY)
/dev/tty1
2、#echo "test tty0" > /dev/tty0
test tty0
5 虛擬終端(/dev/pts/n)
在Xwindows模式下的偽終端.
6 其它類型
Linux系統(tǒng)中還針對很多不同的字符設(shè)備存在有很多其它種類的終端設(shè)備特殊文件。例如針對ISDN設(shè)備的/dev/ttyIn終端設(shè)備等。這里不再贅述。
FAQ: 終端和控制臺
RROM:http://blog.footoo.org/?p=73
Posted on Tuesday, November 28th, 2006 by CLIFF
吳晉 (cliffwoo@gmail.com)
FoOTOo OpenSource Lab
由于在很多朋友對終端的概念一直不是很清楚,因此寫了這個FAQ,希望能夠幫助大家理解這些概念。不妥之處,還請大家來信指出。
Q:/dev/console 是什么?
A:/dev/console即控制臺,是與操作系統(tǒng)交互的設(shè)備,系統(tǒng)將一些信息直接輸出到控制臺上。目前只有在單用戶模式下,才允許用戶登錄控制臺。
Q:/dev/tty是什么?
A:tty設(shè)備包括虛擬控制臺,串口以及偽終端設(shè)備。
/dev/tty代表當(dāng)前tty設(shè)備,在當(dāng)前的終端中輸入 echo “hello” > /dev/tty ,都會直接顯示在當(dāng)前的終端中。
Q:/dev/ttyS*是什么?
A:/dev/ttyS*是串行終端設(shè)備。
Q:/dev/pty*是什么?
A:/dev/pty*即偽終端,所謂偽終端是邏輯上的終端設(shè)備,多用于模擬終端程序。例如,我們在X Window下打開的終端,以及我們在Windows使用telnet 或ssh等方式登錄Linux主機,此時均在使用pty設(shè)備(準(zhǔn)確的說應(yīng)該pty從設(shè)備)。
Q:/dev/tty0與/dev/tty1 …/dev/tty63是什么?它們之間有什么區(qū)別?
A:/dev/tty0代表當(dāng)前虛擬控制臺,而/dev/tty1等代表第一個虛擬控制臺,例如當(dāng)使用ALT+F2進行切換時,系統(tǒng)的虛擬控制臺為 /dev/tty2 ,當(dāng)前的控制臺則指向/dev/tty2
Q:如何確定當(dāng)前所在的終端(或控制臺)?
A:使用tty命令可以確定當(dāng)前的終端或者控制臺。
Q:/dev/console是到/dev/tty0的符號鏈接嗎?
A: 目前的大多數(shù)文本中都稱/dev/console是到/dev/tty0的鏈接(包括《Linux內(nèi)核源代碼情景分析》),但是這樣說是不確切的。根據(jù)內(nèi)核文檔,在2.1.71之前,/dev/console根據(jù)不同系統(tǒng)的設(shè)定可以鏈接到/dev/tty0或者其他tty*上,在2.1.71版本之后則完全由內(nèi)核控制。目前,只有在單用戶模式下可以登錄/dev/console(可以在單用戶模式下輸入tty命令進行確認(rèn))。
Q:/dev/tty0與/dev/fb*有什么區(qū)別?
A: 在Framebuffer設(shè)備沒有啟用的系統(tǒng)中,可以使用/dev/tty0訪問顯卡。
Q:關(guān)于終端和控制臺的區(qū)別可以參考哪些文本
A: 可以參考內(nèi)核文檔中的 Documents/devices.txt 中關(guān)于”TERMINAL DEVICES” 的章節(jié)。另外,《Linux內(nèi)核源代碼情景分析》的8.7節(jié) 以及《Operating Systems : Design and Implementation》中的3.9節(jié)(第3版中為3.8節(jié))都對終端設(shè)備的概念和歷史做了很好的介紹。另外在《Modern Operating system》中也有對終端設(shè)備的介紹,由于與《Operating Systems : Design and Implementation》的作者相同,所以文本內(nèi)容也大致相同。需要注意的一點是《Operating Systems : Design and Implementation》中將終端設(shè)備分為3類,而《Modern Operating system》將終端硬件設(shè)備分為2類,差別在于前者將 X Terminal作為一個類別。
tts 與tty:
? ? com1或com2。。。在linux kernel下是dev/ttySn(n= 0,1,2分別為com對應(yīng)的口),在linux kernel with devfs 中是dev/tts/n(n = 0, 1, 2分別為對應(yīng)的com口)。 ??
串口:
串行口是計算機一種常用的接口,具有連接線少,通訊簡單,得到廣泛的使用。常用的串口是RS-232-C接口(又稱EIA RS-232-C)它是在1970年由美國電子工業(yè)協(xié)會(EIA)聯(lián)合貝爾系統(tǒng)、調(diào)制解調(diào)器廠家及計算機終端生產(chǎn)廠家共同制定的用于串行通訊的標(biāo)準(zhǔn)。串口通訊指的是計算機依次以位(bit)為單位來傳送數(shù)據(jù),串行通訊使用的范圍很廣,在嵌入式系統(tǒng)開發(fā)過程中串口通訊也經(jīng)常用到通訊方式之一。
Linux對所有設(shè)備的訪問是通過設(shè)備文件來進行的,串口也是這樣,為了訪問串口,只需打開其設(shè)備文件即可操作串口設(shè)備。在linux系統(tǒng)下面,每一個串口設(shè)備都有設(shè)備文件與其關(guān)聯(lián),設(shè)備文件位于系統(tǒng)的/dev目錄下面。如linux下的/ttyS0,/ttyS1分別表示的是串口1和串口2。下面來詳細介紹linux下是如何使用串口的:
??
1.??????串口操作需要用到的頭文件
#include?????<stdio.h>??????/*標(biāo)準(zhǔn)輸入輸出定義*/
#include?????<stdlib.h>?????/*標(biāo)準(zhǔn)函數(shù)庫定義*/
#include?????<unistd.h>?????/*Unix?標(biāo)準(zhǔn)函數(shù)定義*/
#include?????<sys/types.h>?
#include?????<sys/stat.h>??
#include?????<fcntl.h>??????/*文件控制定義*/
#include?????<termios.h>????/*POSIX?終端控制定義*/
#include?????<errno.h>??????/*錯誤號定義*/
#include???<string.h>???????/*字符串功能函數(shù)*/
2.??????串口通訊波特率設(shè)置
波特率的設(shè)置定義在<asm/termbits.h>,其包含在頭文件<termios.h>里。
常用的波特率常數(shù)如下:
B0-------à0??????????????????? ?B1800-------à1800
B50-----à50????????????????????B2400------à2400
B75-----à75????????????????????B4800------à4800
B110----à110?????????????????B9600------à9600
B134----à134.5??????????????B19200-----à19200
B200----à200?????????????????B38400------à38400
B300----à300?????????????????B57600------à57600
B600----à600?????????????????B76800------à76800
B1200---à1200??????????????B115200-----à115200
假定程序中想要設(shè)置通訊的波特率,使用cfsetispeed( )和cfsetospeed( )函數(shù)來操作,獲取波特率信息是通過cfgetispeed()和cfgetospeed()函數(shù)來完成的。比如可以這樣來指定串口通訊的波特率:
#include <stdio.h>????//頭文件定義
........
........
.......
struct termios opt;???????????/*定義指向termios?結(jié)構(gòu)類型的指針opt*/
?
/***************以下設(shè)置通訊波特率****************/
cfsetispeed(&opt,B9600 );?/*指定輸入波特率,9600bps*/
cfsetospeed(&opt,B9600);/*指定輸出波特率,9600bps*/
/************************************************/
.........
..........
一般來說,輸入、輸出的波特率應(yīng)該是一致的。
3.??????串口屬性配置
在程序中,很容易配置串口的屬性,這些屬性定義在結(jié)構(gòu)體struct termios中。為在程序中使用該結(jié)構(gòu)體,需要包含文件<termbits.h>,該頭文件定義了結(jié)構(gòu)體struct termios。該結(jié)構(gòu)體定義如下:
#define NCCS 19
struct termios {
?????????????tcflag_t c_iflag;???????????????/*?輸入?yún)?shù)?*/
?????????????tcflag_t c_oflag;???????????????/*?輸出參數(shù)?*/
?????????????tcflag_t c_cflag;???????????????/*?控制參數(shù)*/
?????????????tcflag_t c_ispeed;??????????????/*?輸入波特率?*/
tcflag_t c_ospeed;??????????????/*?輸出波特率?*/
?????????????cc_t c_line;???????????????????/*?線控制?*/
?????????????cc_t c_cc[NCCS];??????????????/*?控制字符*/
};
其中成員c_line在POSIX(Portable Operating System Interface for UNIX)系統(tǒng)中不使用。對于支持POSIX終端接口的系統(tǒng)中,對于端口屬性的設(shè)置和獲取要用到兩個重要的函數(shù)是:
(1).int tcsetattr(int fd,int opt_DE,*ptr)
該函數(shù)用來設(shè)置終端控制屬性,其參數(shù)說明如下:
l????????fd:待操作的文件描述符
l????????opt_DE:選項值,有三個選項以供選擇:
TCSANOW:??不等數(shù)據(jù)傳輸完畢就立即改變屬性
TCSADRAIN:等待所有數(shù)據(jù)傳輸結(jié)束才改變屬性
TCSAFLUSH:清空輸入輸出緩沖區(qū)才改變屬性
l????????*ptr:指向termios結(jié)構(gòu)的指針
函數(shù)返回值:成功返回0,失敗返回-1。
(2).int tcgetattr(int fd,*ptr)
該函數(shù)用來獲取終端控制屬性,它把串口的默認(rèn)設(shè)置賦給了termios數(shù)據(jù)數(shù)據(jù)結(jié)構(gòu),其參數(shù)說明如下:
l?????fd:待操作的文件描述符
l????????*ptr:指向termios結(jié)構(gòu)的指針
函數(shù)返回值:成功返回0,失敗返回-1。
4.??????打開串口
在前面已經(jīng)提到linux下的串口訪問是以設(shè)備文件形式進行的,所以打開串口也即是打開文件的操作。函數(shù)原型可以如下所示:
int open(“DE_name”,int open_Status)
參數(shù)說明:
(1).DE_name:要打開的設(shè)備文件名
比如要打開串口1,即為/dev/ttyS0。
(2).open_Status:文件打開方式,可采用下面的文件打開模式:
l??????????O_RDONLY:以只讀方式打開文件
l??????????O_WRONLY:以只寫方式打開文件
l??????????O_RDWR:以讀寫方式打開文件
l??????????O_APPEND:寫入數(shù)據(jù)時添加到文件末尾
l??????????O_CREATE:如果文件不存在則產(chǎn)生該文件,使用該標(biāo)志需要設(shè)置訪問權(quán)限位mode_t
l??????????O_EXCL:指定該標(biāo)志,并且指定了O_CREATE標(biāo)志,如果打開的文件存在則會產(chǎn)生一個錯誤
l??????????O_TRUNC:如果文件存在并且成功以寫或者只寫方式打開,則清除文件所有內(nèi)容,使得文件長度變?yōu)?
l??????????O_NOCTTY:如果打開的是一個終端設(shè)備,這個程序不會成為對應(yīng)這個端口的控制終端,如果沒有該標(biāo)志,任何一個輸入,例如鍵盤中止信號等,都將影響進程。
l??????????O_NONBLOCK:該標(biāo)志與早期使用的O_NDELAY標(biāo)志作用差不多。程序不關(guān)心DCD信號線的狀態(tài),如果指定該標(biāo)志,進程將一直在休眠狀態(tài),直到DCD信號線為0。
函數(shù)返回值:
成功返回文件描述符,如果失敗返回-1
例如假定以可讀寫方式打開/dev/ttyS0設(shè)備,就可以這樣操作:
#include<stdio.h>????//頭文件包含
......
......
int fd; /*?文件描述符?*/
fd = open("/dev/ttyS0", O_RDWR | 0_NOCTTY);??/*以讀寫方式打開設(shè)備*/
if(fd == -1)
perror("Can not open Serial_Port 1/n!");/*打開失敗時的錯誤提示*/
........
........
?
5.??????串口讀操作(接收端)
用open函數(shù)打開設(shè)備文件,函數(shù)返回一個文件描述符(file descriptors,fd),通過文件描述符來訪問文件。讀串口操作是通過read函數(shù)來完成的。函數(shù)原型如下:
int read(int fd, *buffer,length);
參數(shù)說明:
(1).int fd:文件描述符
(2).*buffer:數(shù)據(jù)緩沖區(qū)
(3).length:要讀取的字節(jié)數(shù)
函數(shù)返回值:
讀操作成功讀取返回讀取的字節(jié)數(shù),失敗則返回-1。
6.??????串口寫操作(發(fā)送端)
寫串口操作是通過write函數(shù)來完成的。函數(shù)原型如下:
write(int fd, *buffer,length);
參數(shù)說明:
(1).fd:文件描述符
(2).*buffer:存儲寫入數(shù)據(jù)的數(shù)據(jù)緩沖區(qū)
(3).length:寫入緩沖去的數(shù)據(jù)字節(jié)數(shù)
函數(shù)返回值:
成功返回寫入數(shù)據(jù)的字節(jié)數(shù),該值通常等于length,如果寫入失敗返回-1。
例如:向終端設(shè)備發(fā)送初始化命令
#include<stdio.h>????//頭文件包含
......
......
?
int n
sbuf[]={Hello,this is a Serial_Port test!/n };//待發(fā)送數(shù)據(jù)
int len_send="sizeof"(sbuf);//發(fā)送緩沖區(qū)字節(jié)數(shù)定義
n = write(fd,sbuf,len_send); //寫緩沖區(qū)
if(n == -1)
{
printf("Wirte sbuf error./n");
}
......
......
7.??????關(guān)閉串口
對設(shè)備文件的操作與對普通文件的操作一樣,打開操作之后還需要關(guān)閉,關(guān)閉串口用函數(shù)close( )來操作,函數(shù)原型為:
int close(int fd);
參數(shù)說明:
fd:文件描述符
函數(shù)返回值:
成功返回0,失敗返回-1。
NAME
termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed - 獲取和設(shè)置終端屬性,行控制,獲取和設(shè)置波特率??
SYNOPSIS 總覽
#include <termios.h>?
#include <unistd.h>
int tcgetattr(int?fd, struct termios *termios_p);
int tcsetattr(int?fd, int?optional_actions, struct termios *termios_p);
int tcsendbreak(int?fd, int?duration);
int tcdrain(int?fd);
int tcflush(int?fd, int?queue_selector);
int tcflow(int?fd, int?action);
int cfmakeraw(struct termios *termios_p);
speed_t cfgetispeed(struct termios *termios_p);
speed_t cfgetospeed(struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t?speed);
int cfsetospeed(struct termios *termios_p, speed_t?speed);??
DESCRIPTION 描述
termios 函數(shù)族提供了一個常規(guī)的終端接口,用于控制非同步通信端口。
這里描述的大部分屬性有一個?termios_p?類型的參數(shù),它是指向一個?termios?結(jié)構(gòu)的指針。這個結(jié)構(gòu)包含了至少下列成員:
?
c_iflag?標(biāo)志常量:
IGNBRKPOSIX.1 中定義的?c_oflag?標(biāo)志常量:
OPOST其余?c_oflag?標(biāo)志常量定義在 POSIX 1003.1-2001 中,除非另外說明。
OLCUCc_cflag?標(biāo)志常量:
CBAUD(POSIX 規(guī)定波特率存儲在 termios 結(jié)構(gòu)中,并未精確指定它的位置,而是提供了函數(shù)?cfgetispeed()?和cfsetispeed()?來存取它。一些系統(tǒng)使用?c_cflag?中 CBAUD 選擇的位,其他系統(tǒng)使用單獨的變量,例如sg_ispeed?和?sg_ospeed?。)
CSIZEc_lflag?標(biāo)志常量:
ISIGc_cc?數(shù)組定義了特殊的控制字符。符號下標(biāo) (初始值) 和意義為:
VINTR這些符號下標(biāo)值是互不相同的,除了 VTIME,VMIN 的值可能分別與 VEOL,VEOF 相同。 (在 non-canonical 模式下,特殊字符的含義更改為延時含義。MIN 表示應(yīng)當(dāng)被讀入的最小字符數(shù)。TIME 是以十分之一秒為單位的計時器。如果同時設(shè)置了它們,read 將等待直到至少讀入一個字符,一旦讀入 MIN 個字符或者從上次讀入字符開始經(jīng)過了 TIME 時間就立即返回。如果只設(shè)置了 MIN,read 在讀入 MIN 個字符之前不會返回。如果只設(shè)置了 TIME,read 將在至少讀入一個字符,或者計時器超時的時候立即返回。如果都沒有設(shè)置,read 將立即返回,只給出當(dāng)前準(zhǔn)備好的字符。) (?)
tcgetattr()?得到與?fd?指向的對象相關(guān)的參數(shù),將它們保存于?termios_p?引用的?termios?結(jié)構(gòu)中。函數(shù)可以從后臺進程中調(diào)用;但是,終端屬性可能被后來的前臺進程所改變。
tcsetattr()?設(shè)置與終端相關(guān)的參數(shù) (除非需要底層支持卻無法滿足),使用?termios_p?引用的?termios?結(jié)構(gòu)。optional_actions?指定了什么時候改變會起作用:
TCSANOWtcsendbreak()?傳送連續(xù)的 0 值比特流,持續(xù)一段時間,如果終端使用異步串行數(shù)據(jù)傳輸?shù)脑挕H绻?duration是 0,它至少傳輸 0.25 秒,不會超過 0.5 秒。如果?duration?非零,它發(fā)送的時間長度由實現(xiàn)定義。
如果終端并非使用異步串行數(shù)據(jù)傳輸,tcsendbreak()?什么都不做。
tcdrain()?等待直到所有寫入?fd?引用的對象的輸出都被傳輸。
tcflush()?丟棄要寫入 引用的對象,但是尚未傳輸?shù)臄?shù)據(jù),或者收到但是尚未讀取的數(shù)據(jù),取決于queue_selector?的值:
TCIFLUSHtcflow()?掛起?fd?引用的對象上的數(shù)據(jù)傳輸或接收,取決于?action?的值:
TCOOFF打開一個終端設(shè)備時的默認(rèn)設(shè)置是輸入和輸出都沒有掛起。
波特率函數(shù)被用來獲取和設(shè)置?termios?結(jié)構(gòu)中,輸入和輸出波特率的值。新值不會馬上生效,直到成功調(diào)用了tcsetattr()?函數(shù)。
設(shè)置速度為?B0?使得 modem "掛機"。與?B38400?相應(yīng)的實際比特率可以用?setserial(8) 調(diào)整。
輸入和輸出波特率被保存于?termios?結(jié)構(gòu)中。
cfmakeraw?設(shè)置終端屬性如下:
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);termios_p->c_oflag &= ~OPOST;termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);termios_p->c_cflag &= ~(CSIZE|PARENB);termios_p->c_cflag |= CS8;cfgetospeed()?返回?termios_p?指向的?termios?結(jié)構(gòu)中存儲的輸出波特率
cfsetospeed()?設(shè)置?termios_p?指向的?termios?結(jié)構(gòu)中存儲的輸出波特率為?speed。取值必須是以下常量之一:
B0B50B75B110B134B150B200B300B600B1200B1800B2400B4800B9600B19200B38400B57600B115200B230400零值?B0?用來中斷連接。如果指定了 B0,不應(yīng)當(dāng)再假定存在連接。通常,這樣將斷開連接。CBAUDEX?是一個掩碼,指示高于 POSIX.1 定義的速度的那一些 (57600 及以上)。因此,B57600?&?CBAUDEX?為非零。
cfgetispeed()?返回?termios?結(jié)構(gòu)中存儲的輸入波特率。
cfsetispeed()?設(shè)置?termios?結(jié)構(gòu)中存儲的輸入波特率為?speed。如果輸入波特率被設(shè)為0,實際輸入波特率將等于輸出波特率。??
RETURN VALUE 返回值
cfgetispeed()?返回?termios?結(jié)構(gòu)中存儲的輸入波特率。
cfgetospeed()?返回?termios?結(jié)構(gòu)中存儲的輸出波特率。
其他函數(shù)返回:
0注意?tcsetattr()?返回成功,如果任何所要求的修改可以實現(xiàn)的話。因此,當(dāng)進行多重修改時,應(yīng)當(dāng)在這個函數(shù)之后再次調(diào)用?tcgetattr()?來檢測是否所有修改都成功實現(xiàn)。
?
NOTES 注意
Unix V7 以及很多后來的系統(tǒng)有一個波特率的列表,在十四個值 B0, ..., B9600 之后可以看到兩個常數(shù) EXTA, EXTB ("External A" and "External B")。很多系統(tǒng)將這個列表擴展為更高的波特率。
tcsendbreak?中非零的?duration?有不同的效果。SunOS 指定中斷?duration*N?秒,其中?N?至少為 0.25,不高于 0.5 。Linux, AIX, DU, Tru64 發(fā)送?duration?微秒的 break 。FreeBSD, NetBSD, HP-UX 以及 MacOS 忽略duration?的值。在 Solaris 和 Unixware 中,?tcsendbreak?搭配非零的?duration?效果類似于?tcdrain。??
所有的范例來源自?miniterm.c. The type ahead 暫存器被限制在 255 個字元, 就跟標(biāo)準(zhǔn)輸入程序的最大字串長度相同 (<linux/limits.h>?或?<posix1_lim.h>).
參考程序碼中的注解它會解釋不同輸入模式的使用. 我希望這些程序碼都能被了解. 標(biāo)準(zhǔn)輸入程序的程序范例的注解寫得最好, 其它的范例都只在不同于其它范例的地方做注解.
敘述不是很完整, 但可以激勵你對這范例做實驗, 以延生出合于你所需應(yīng)用程序的最佳解.
別忘記要把序列埠的權(quán)限設(shè)定正確 (也就是:?chmod a+rw /dev/ttyS1)!
?
?
3.1 標(biāo)準(zhǔn)輸入程序
?
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h>/* 鮑率設(shè)定被定義在 <asm/termbits.h>, 這在 <termios.h> 被引入 */ #define BAUDRATE B38400 /* 定義正確的序列埠 */ #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* POSIX 系統(tǒng)兼容 */#define FALSE 0 #define TRUE 1volatile int STOP=FALSE; main() {int fd,c, res;struct termios oldtio,newtio;char buf[255]; /* 開啟數(shù)據(jù)機裝置以讀取并寫入而不以控制 tty 的模式因為我們不想程序在送出 CTRL-C 后就被殺掉. */fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {perror(MODEMDEVICE); exit(-1); }tcgetattr(fd,&oldtio); /* 儲存目前的序列埠設(shè)定 */bzero(&newtio, sizeof(newtio)); /* 清除結(jié)構(gòu)體以放入新的序列埠設(shè)定值 *//* BAUDRATE: 設(shè)定 bps 的速度. 你也可以用 cfsetispeed 及 cfsetospeed 來設(shè)定.CRTSCTS : 輸出資料的硬件流量控制 (只能在具完整線路的纜線下工作參考 Serial-HOWTO 第七節(jié))CS8 : 8n1 (8 位元, 不做同位元檢查,1 個終止位元)CLOCAL : 本地連線, 不具數(shù)據(jù)機控制功能CREAD : 致能接收字元 */newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;/*IGNPAR : 忽略經(jīng)同位元檢查后, 錯誤的位元組ICRNL : 比 CR 對應(yīng)成 NL (否則當(dāng)輸入信號有 CR 時不會終止輸入)在不然把裝置設(shè)定成 raw 模式(沒有其它的輸入處理) */newtio.c_iflag = IGNPAR | ICRNL;/*Raw 模式輸出. */newtio.c_oflag = 0;/*ICANON : 致能標(biāo)準(zhǔn)輸入, 使所有回應(yīng)機能停用, 并不送出信號以叫用程序 */newtio.c_lflag = ICANON;/* 初始化所有的控制特性預(yù)設(shè)值可以在 /usr/include/termios.h 找到, 在注解中也有,但我們在這不需要看它們 */newtio.c_cc[VINTR] = 0; /* Ctrl-c */ newtio.c_cc[VQUIT] = 0; /* Ctrl-/ */newtio.c_cc[VERASE] = 0; /* del */newtio.c_cc[VKILL] = 0; /* @ */newtio.c_cc[VEOF] = 4; /* Ctrl-d */newtio.c_cc[VTIME] = 0; /* 不使用分割字元組的計時器 */newtio.c_cc[VMIN] = 1; /* 在讀取到 1 個字元前先停止 */newtio.c_cc[VSWTC] = 0; /* '/0' */newtio.c_cc[VSTART] = 0; /* Ctrl-q */ newtio.c_cc[VSTOP] = 0; /* Ctrl-s */newtio.c_cc[VSUSP] = 0; /* Ctrl-z */newtio.c_cc[VEOL] = 0; /* '/0' */newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */newtio.c_cc[VWERASE] = 0; /* Ctrl-w */newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */newtio.c_cc[VEOL2] = 0; /* '/0' *//* 現(xiàn)在清除數(shù)據(jù)機線并啟動序列埠的設(shè)定 */tcflush(fd, TCIFLUSH);tcsetattr(fd,TCSANOW,&newtio);/*終端機設(shè)定完成, 現(xiàn)在處理輸入信號在這個范例, 在一行的開始處輸入 'z' 會退出此程序. */while (STOP==FALSE) { /* 回圈會在我們發(fā)出終止的信號后跳出 *//* 即使輸入超過 255 個字元, 讀取的程序段還是會一直等到行終結(jié)符出現(xiàn)才停止.如果讀到的字元組低于正確存在的字元組, 則所剩的字元會在下一次讀取時取得.res 用來存放真正讀到的字元組個數(shù) */res = read(fd,buf,255); buf[res]=0; /* 設(shè)定字串終止字元, 所以我們能用 printf */printf(":%s:%d/n", buf, res);if (buf[0]=='z') STOP=TRUE;}/* 回存舊的序列埠設(shè)定值 */tcsetattr(fd,TCSANOW,&oldtio); }?
3.2 非標(biāo)準(zhǔn)輸入程序
在非標(biāo)準(zhǔn)的輸入程序模式下, 輸入的資料不會被組合成一行而輸入后的處理功能 (清除, 殺掉, 刪除, 等等.) 都不能使用. 這個模式有兩個功能控制參數(shù):?c_cc[VTIME]?設(shè)定字元輸入時間計時器, 及?c_cc[VMIN]?設(shè)定滿足讀取功能的最低字元接收個數(shù).
如果 MIN > 0 且 TIME = 0, MIN 設(shè)定為滿足讀取功能的最低字元接收個數(shù). 由于 TIME 是 零, 所以計時器將不被使用.
如果 MIN = 0 且 TIME > 0, TIME 將被當(dāng)做逾時設(shè)定值. 滿足讀取功能的情況為讀取到單一字元, 或者超過 TIME 所定義的時間 (t = TIME *0.1 s). 如果超過 TIME 所定義的時間, 則不會傳回任何字元.
如果 MIN > 0 且 TIME > 0, TIME 將被當(dāng)做一個分割字元組的計時器. 滿足讀取功能的條件為接收到 MIN 個數(shù)的字元, 或兩個字元的間隔時間超過 TIME 所定義的值. 計時器會在每讀到一個字元后重新計時, 且只會在第一個字元收到后才會啟動.
如果 MIN = 0 且 TIME = 0, 讀取功能就馬上被滿足. 目前所存在的字元組個數(shù), 或者 將回傳的字元組個數(shù). 根據(jù) Antonino (參考 貢獻) 所說, 你可以用?fcntl(fd, F_SETFL, FNDELAY);?在讀取前得到相同的結(jié)果.
藉由修改?newtio.c_cc[VTIME]?及?newtio.c_cc[VMIN]?上述的模式就可以測試了.
?
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h>#define BAUDRATE B38400 #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* POSIX 系統(tǒng)兼容 */ #define FALSE 0 #define TRUE 1volatile int STOP=FALSE; main() {int fd,c, res;struct termios oldtio,newtio;char buf[255];fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {perror(MODEMDEVICE); exit(-1); }tcgetattr(fd,&oldtio); /* 儲存目前的序列埠設(shè)定 */bzero(&newtio, sizeof(newtio));newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;newtio.c_iflag = IGNPAR;newtio.c_oflag = 0;/* 設(shè)定輸入模式 (非標(biāo)準(zhǔn)型, 不回應(yīng),...) */newtio.c_lflag = 0;newtio.c_cc[VTIME] = 0; /* 不使用分割字元組計時器 */newtio.c_cc[VMIN] = 5; /* 在讀取到 5 個字元前先停止 */tcflush(fd, TCIFLUSH);tcsetattr(fd,TCSANOW,&newtio);while (STOP==FALSE) { /* 輸入回圈 */res = read(fd,buf,255); /* 在輸入 5 個字元后即返回 */buf[res]=0; /* 所以我們能用 printf... */printf(":%s:%d/n", buf, res);if (buf[0]=='z') STOP=TRUE;}tcsetattr(fd,TCSANOW,&oldtio); }?
3.3 非同步式輸入
?
#include <termios.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/signal.h> #include <sys/types.h>#define BAUDRATE B38400 #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* POSIX 系統(tǒng)兼容 */ #define FALSE 0 #define TRUE 1volatile int STOP=FALSE; void signal_handler_IO (int status); /* 定義信號處理程序 */ int wait_flag=TRUE; /* 沒收到信號的話就會是 TRUE */main() {int fd,c, res;struct termios oldtio,newtio;struct sigaction saio; /* definition of signal action */char buf[255];/* 開啟裝置為 non-blocking (讀取功能會馬上結(jié)束返回) */fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);if (fd <0) {perror(MODEMDEVICE); exit(-1); }/* 在使裝置非同步化前, 安裝信號處理程序 */saio.sa_handler = signal_handler_IO;saio.sa_mask = 0;saio.sa_flags = 0;saio.sa_restorer = NULL;sigaction(SIGIO,&saio,NULL);/* 允許行程去接收 SIGIO 信號*/fcntl(fd, F_SETOWN, getpid());/* 使文檔ake the file descriptor 非同步 (使用手冊上說只有 O_APPEND 及O_NONBLOCK, 而 F_SETFL 也可以用...) */fcntl(fd, F_SETFL, FASYNC);tcgetattr(fd,&oldtio); /* 儲存目前的序列埠設(shè)定值 *//* 設(shè)定新的序列埠為標(biāo)準(zhǔn)輸入程序 */newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;newtio.c_iflag = IGNPAR | ICRNL;newtio.c_oflag = 0;newtio.c_lflag = ICANON;newtio.c_cc[VMIN]=1;newtio.c_cc[VTIME]=0;tcflush(fd, TCIFLUSH);tcsetattr(fd,TCSANOW,&newtio);/* 等待輸入信號的回圈. 很多有用的事我們將在這做 */ while (STOP==FALSE) {printf("./n");usleep(100000);/* 在收到 SIGIO 后, wait_flag = FALSE, 輸入信號存在則可以被讀取 */if (wait_flag==FALSE) { res = read(fd,buf,255);buf[res]=0;printf(":%s:%d/n", buf, res);if (res==1) STOP=TRUE; /* 如果只輸入 CR 則停止回圈 */wait_flag = TRUE; /* 等待新的輸入信號 */}}/* 回存舊的序列埠設(shè)定值 */tcsetattr(fd,TCSANOW,&oldtio); }/*************************************************************************** * 信號處理程序. 設(shè)定 wait_flag 為 FALSE, 以使上述的回圈能接收字元 * ***************************************************************************/void signal_handler_IO (int status) {printf("received SIGIO signal./n");wait_flag = FALSE; }?
3.4 等待來自多個信號來源的輸入
這一段很短. 它只能被拿來當(dāng)成寫程序時的提示, 故范例程序也很簡短. 但這個范例不只能用在序列埠上, 還可以用在被當(dāng)成文檔來使用的裝置上.
select 呼叫及伴隨它所引發(fā)的巨集共用?fd_set.?fd_set?則是一個位元陣列, 而其中每一個位元代表一個有效的文檔敘述結(jié)構(gòu).?select?呼叫接受一個有效的文檔敘述結(jié)構(gòu)并傳回?fd_set?位元陣列, 而該位元陣列中若有某一個位元為 1, 就表示相對映的文檔敘述結(jié)構(gòu)的文檔發(fā)生了輸入, 輸出或有例外事件. 而這些巨集提供了所有處理?fd_set的功能. 亦可參考手冊 select(2).
?
#include <sys/time.h> #include <sys/types.h> #include <unistd.h>main() {int fd1, fd2; /* 輸入源 1 及 2 */fd_set readfs; /* 文檔敘述結(jié)構(gòu)設(shè)定 */int maxfd; /* 最大可用的文檔敘述結(jié)構(gòu) */int loop=1; /* 回圈在 TRUE 時成立 */ /* open_input_source 開啟一個裝置, 正確的設(shè)定好序列埠,并回傳回此文檔敘述結(jié)構(gòu)體 */fd1 = open_input_source("/dev/ttyS1"); /* COM2 */if (fd1<0) exit(0);fd2 = open_input_source("/dev/ttyS2"); /* COM3 */if (fd2<0) exit(0);maxfd = MAX (fd1, fd2)+1; /* 測試最大位元輸入 (fd) *//* 輸入回圈 */while (loop) {FD_SET(fd1, &readfs); /* 測試輸入源 1 */FD_SET(fd2, &readfs); /* 測試輸入源 2 *//* block until input becomes available */select(maxfd, &readfs, NULL, NULL, NULL);if (FD_ISSET(fd1)) /* 如果輸入源 1 有信號 */handle_input_from_source1();if (FD_ISSET(fd2)) /* 如果輸入源 2 有信號 */handle_input_from_source2();}}這個范例程序在等待輸入信號出現(xiàn)前, 不能確定它會停頓下來. 如果你需要在輸入時加入逾時功能, 只需把 select 呼叫換成:
int res; struct timeval Timeout;/* 設(shè)定輸入回圈的逾時值 */ Timeout.tv_usec = 0; /* 毫秒 */ Timeout.tv_sec = 1; /* 秒 */ res = select(maxfd, &readfs, NULL, NULL, &Timeout); if (res==0) /* 文檔敘述結(jié)構(gòu)數(shù)在 input = 0 時, 會發(fā)生輸入逾時. */這個程序會在 1 秒鐘后逾時. 如果超過時間, select 會傳回 0, 但是應(yīng)該留意?Timeout?的時間遞減是由?select?所等待輸入信號的時間為基準(zhǔn). 如果逾時的值是 0, select 會馬上結(jié)束返回.
?
?
Linux 環(huán)境下使用RS-232接口
RS是英文 "推薦標(biāo)準(zhǔn)"的縮寫
232為標(biāo)識號
RS-485?
串口通信表示計算機一次傳送一個位的數(shù)據(jù),
當(dāng)使用串行通信時,每個字的數(shù)據(jù)是一個位一個位的傳輸或接收的,
每個位不是高電平,就是低電平.
串行通信的速率通常是使用"位/每秒"的方式來表示的,即波特率。
全雙工--計算機可以同時收發(fā)數(shù)據(jù),
它有兩個獨立的數(shù)據(jù)通道,一個輸入,一個輸出,
半雙工意味著計算機不能同時收發(fā)信息,
只能有一人通道進行通信.
流控:
??? 通常,當(dāng)數(shù)據(jù)在兩個串行接口之間進行傳輸時需要對其進行控制.
??? 這通常依賴于串行通信連接的各種規(guī)定,
????
??? 對異步數(shù)據(jù)傳輸?shù)目刂朴袃煞N方法.
????
??? 一種叫:“軟件”流控 。
??? 一種叫: “硬件"流控 。
串口設(shè)備:
?打開一個串行口
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>? // 文件控制定義
#include <errno.h>
#include <termios.h>? //POSIX終端控制定義
/*
?* open_port() --打開串行口
?*?
?* 成功的話,返回文件描述符,錯誤則返回 -1.
*/
int? open_port(void)
{
??? int fd;
??? fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
??? if (fd == -1)
??? {
??? /*無法打開串口*/
??? perror("open_port : Unable to open /dev/ttyS0");
??? }
??? else
??? ??? fcntl(fd,F_SETFL,0);
??? return (fd);
}
//O_NOCTTY 標(biāo)志 ,該程序不想成為此端口的“控制終端"。
????????????????? 如果沒有強調(diào)這一點,
//O_NDELAY標(biāo)志 , 標(biāo)志告訴Linux ,該程序并不關(guān)注DCD信叼線所處的狀態(tài),
即不管另外一端的設(shè)備是在運行還是被掛起。如果沒有指定該標(biāo)志,那么程序就會被設(shè)置睡
眠狀態(tài),
(2)向端口寫數(shù)據(jù)
??? 向端口寫數(shù)據(jù)是很容易的,只要使用write()系統(tǒng)調(diào)用就可以了。
??? 例如:
??? ???? n=write(fd,"ATZ/r",4);
??? if (n<0)
??? ??? fputs("write() of 4 bytes failed!/n",stderr);
????
??? write函數(shù)返回發(fā)送數(shù)據(jù)的個數(shù),如果出現(xiàn)錯誤,則返回 -1。
(3) 讀端口數(shù)據(jù)
??? 從端口讀數(shù)據(jù)則需要些技巧。如果在原始數(shù)據(jù)的模式下對端口進行操作,
??? read()系統(tǒng)調(diào)用將返回串行口輸入緩沖區(qū)中所有的字符數(shù)據(jù),不管有多少,
????
??? 如果沒有數(shù)據(jù),那么該調(diào)用將被阻塞.處于等待狀態(tài),直到有字符輸入,
??? 或者到了規(guī)定的時限和出現(xiàn)錯誤為止,
??? 通過以下方法,能使read函數(shù)立即返回。
??? fcntl(fd,F_SETFL,FNDELAY);
??? FNDELAY 函數(shù)使read函數(shù)在端口沒月字符存在的情況下,立刻返回0,
??? 如果要恢復(fù)正常(阻塞)狀態(tài),可以調(diào)用fcntl()函數(shù),不要FNDELAY參數(shù),
??? 如下所示:
??? ??? fcntl(Fd,F_SETFL,0);
??? 在使用O_NDELAY參數(shù)打開串行口后,同樣與使用了該函數(shù)調(diào)用。
????
??? fcntl(fd,F_SETFL,0);
POSIX終端接口
??? 串口,波特率,字符大小等, <termios.h>
??? POSIX函數(shù)是 tcgetattr()和tcsetattr()?
????
??? 獲取和設(shè)置終端的屬性,
??? ??? 可以提供 structrure termios的指針,
??? ????
??? termios結(jié)構(gòu)的成員 :
????
??? ?參數(shù)?????? 功能
??? C_cflag???? 控制參數(shù)
??? C_
?
本文轉(zhuǎn)自:http://blog.csdn.net/chenchao03/article/details/3219791
總結(jié)
以上是生活随笔為你收集整理的linux tty pty pts tts概念 区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10 年经验也找不到工作了
- 下一篇: oracle学生-教师-班级表