為什么80%的碼農都做不了架構師?>>> ??
在Linux環境下,串口名從ttyS0開始依次是ttyS1、ttyS2等。在本程序中,使用ttyS0作為通信串口。在打開ttyS0的時候選項
O_NOCTTY 表示不能把本串口當成控制終端,否則用戶的鍵盤輸入信息將影響程序的執行;
O_NDELAY表示打開串口的時候,程序并不關心另一端 的串口是否在使用中。
在Linux中,打開串口設備和打開普通文件一樣,使用的是open()系統調用。比如我么打開串口設備1也就是COM1,只需要:
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY );
打開的串口設備有很多設置選項。本文中使用int setup_com(int fd)設置。在系統頭文件中 定義了終端控制結構struct termios,tcgetattr()和tcsetattr()兩個系統函數獲得和設置這些屬性。結構 struct termios中的域描述的主要屬性包括:
c_cflag : 控制選項
c_lflag : 線選項
c_iflag : 輸入選項
c_oflag :輸出選項
c_cc :控制字符
c_ispeed :輸入數據波特率
c_ospeed :輸出數據波特率
如果要設置某個選項,那么就使用"|="運算,如果關閉某個選項就使用"&="和"~"運算。本文使用的各個選項的意義定義如下:
c_cflag:?
CLOCAL 本地模式,不改變端口的所有者
CREAD 表示使能數據接收器
PARENB 表示偶校驗
PARODD 表示奇校驗
CSTOPB 使用兩個停止位
CSIZE 對數據的bit使用掩碼
CS8 數據寬度是8bit
c_lflag:
ICANON 使能規范輸入,否則使用原始數據(本文使用)
ECHO 回送(echo)輸入數據
ECHOE 回送擦除字符
ISIG 使能SIGINTR,SIGSUSP, SIGDSUSP和 SIGQUIT 信號
c_iflag:?
IXON 使能輸出軟件控制
IXOFF 使能輸入軟件控制
IXANY 允許任何字符再次開啟數據流
INLCR 把字符NL(0A)映射到CR(0D)
IGNCR 忽略字符CR(0D)
ICRNL 把CR(0D)映射成字符NR(0A)
c_oflag: OPOST 輸出后處理,如果不設置表示原始數據(本文使用原始數據)
c_cc[VMIN]: 最少可讀數據
c_cc[VTIME]: 等待數據時間(10秒的倍數)
根據以上設置的定義,串口端口設置函數setup_com()定義如下:
int setup_com(int fd){
struct termios options;
tcgetattr(fd, &options);
/* Set the baud rates to 38400...*/
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
/* Enable the receiver and set local mode...*/
options.c_cflag |= (CLOCAL | CREAD);
/* Set c_cflag options.*/
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
/* Set c_iflag input options */
options.c_iflag &=~(IXON | IXOFF | IXANY);
options.c_iflag &=~(INLCR | IGNCR | ICRNL);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* Set c_oflag output options */
options.c_oflag &= ~OPOST;
/* Set the timeout options */
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);
return 1;
}
6.7.2 設置串口通信參數
串口通信參數指的是波特率、數據位、奇偶校驗位和停止位。對串口實現控制的時候同樣要用到termio結構體。下面將結合具體的代碼說明如何設置這些參數。
1.波特率設置
獲得端口波特率信息是通過cfgetispeed函數和cfgetospeed函數來實現的。cfgetispeed函數用于獲得結構體 termios_p中的輸入波特率信息,而cfgetospeed函數用于獲得結構體termios_p 中的輸出波特率信息。這兩個函數的具體信息如表 6.9所示。
表6.9 cfgetispeed函數和cfgetospeed函數
頭文件
函數形式
speed_t cfgetispeed(const struct termios *termios_p);
speed_t cfgetospeed(const struct termios *termios_p);
返回值
是否設置errno
返回termios_p結構中的輸入/輸出端口的波特率
cfsetispeed函數和cfsetospeed函數用于設置端口的輸入/輸出波特率。一般情況下,輸入和輸出波特率是相等的。cfsetispeed函數和cfsetospeed函數的函數聲明信息如表6.10所示。
表6.10 cfsetispeed函數和cfsetospeed函數
頭文件
函數形式
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
返回值
是否設置errno
返回termios_p結構中的輸入/輸出端口的波特率
cfsetispeed函數和cfsetospeed函數會修改結構體termios_p中的波特率信息,其中參數speed可以使用表6.11中所列出的宏。
表6.11 speed參數常用波特率信息
宏 定 義 ? ? ? 波特率(單位:bit/s) ? ? ? ? 宏 定 義 ? ? ? ? ?波特率(單位:bit/s)
B1800 ? ? ? ? ? ?1800 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? B50
B2400 ? ? ? ? ? ?2400 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? B75
B4800 ? ? ? ? ? ?4800 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??B110 ? ? ? ? ? ? ? ? 110
B9600 ? ? ? ? ? ?9600 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??B134 ? ? ? ? ? ? ? ? ?134
B19200 ? ? ? ? ? 19200 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?B150 ? ? ? ? ? ? ? ? 150
B38400 ? ? ? ? ? 38400 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?B200 ? ? ? ? ? ? ? ? ?200
B57600 ? ? ? ? ? ?57600 ? ? ? ? ? ? ? ? ? ? ? ? ? ??B300 ? ? ? ? ? ? ? ? ?300
B115200 ? ? ? ? ?115200 ? ? ? ? ? ? ? ? ? ? ? ? ??B600 ? ? ? ? ? ? ? ? ?600
B230400 ? ? ? ? ? 230400 ? ? ? ? ? ? ? ? ? ? ? ??B1200 ? ? ? ? ? ? ? ? 1200
?
使用cfsetispeed函數和cfsetospeed函數進行串口波特率設置具體代碼如下所示:
#include?//頭文件定義
#include?
#include < termios.h >
……
struct termios opt; /*定義指向termios 結構類型的指針opt*/
……
//獲得串口指向termios結構的指針
tcgetattr(fd, &Opt);
cfsetispeed(&opt,B9600 ); /*指定輸入波特率,9600bps*/
cfsetospeed(&opt,B9600);/*指定輸出波特率,9600bps*/
//將修改后的termios數據設置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
2.數據位
數據位指的是每字節中實際數據所占的比特數。要修改數據位可以通過修改termios結構體中c_cflag成員來實現。CS5、CS6、CS7和CS8分別表示數據位為5、6、7和8。值得注意的是,在設置數據位時,必須先使用CSIZE做位屏蔽。具體設置代碼如下:
#include?//頭文件定義
#include?
#include < termios.h >
……
struct termios opt; /*定義指向termios 結構類型的指針opt*/
.......
//獲得串口指向termios結構的指針
tcgetattr(fd, &Opt);
…
//屏蔽其他標志
Opt.c_cflag&=~CSIZE;
//將數據位修改為8bit
Opt.c_cflag |=CS8;
…
//將修改后的termios數據設置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
3.奇偶校驗位
奇偶校驗可以選擇偶校驗、奇校驗、空格等方式,也可以不使用校驗。如果要設置為偶校驗的話,首先要將termios結構體中c_cflag設置 PARENB標志,并清除PARODD標志。如果要設置奇校驗,要同時設置termios結構體中c_cflag設置PARENB標志和PARODD標 志。如果不想使用任何校驗的話,清除termios結構體中c_cflag的PARENB位。表6.12所示為設置奇偶校驗的具體方法。
表6.12 設置奇偶校驗位
設 置
具 體 代 碼
無校驗
opt.c_cflag &= ~PARENB;
奇校驗
opt.c_cflag |= (PARODD | PARENB);
偶校驗
opt.c_cflag &= ~ PARENB;
opt.c_cflag &= ~PARODD;
opt.c_cflag &= ~PARENB;
opt.c_cflag &= ~CSTOPB;
下面給出將串口通信的奇偶校驗設置為偶校驗的例子,具體代碼如下:
#include?//頭文件定義
#include?
#include < termios.h >
……
struct termios opt; /*定義指向termios 結構類型的指針opt*/
……
//獲得串口指向termios結構的指針
tcgetattr(fd, &Opt);
…
opt.c_cflag &= ~ PARENB;
opt.c_cflag &= ~PARODD;
…
//將修改后的termios數據設置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
4.數據流控制
數據流控制指是使用何種方法來標志數據傳輸的開始和結束。可以選擇不使用數據流控制、使用硬件進行流控制和使用軟件進行流控制。數據流控制設置如表6.13所示。
表6.13 數據流控制設置
設 置
具 體 代 碼
不使用數據流控制
opt.c_cflag &= ~CRTSCTS
opt.c_cflag |= CRTSCTS
opt.c_cflag | = IXON|IXOFF|IXANY
由于使用硬件流控制需要相應連接的電纜,常用的流控制方法還是使用軟件進行流控制。下面給出了設置不使用數據流控制的相關代碼:
#include?//頭文件定義
#include?
#include < termios.h >
……
struct termios opt; /*定義指向termios 結構類型的指針opt*/
……
//獲得串口指向termios結構的指針
tcgetattr(fd, &opt);
…
opt.c_cflag &= ~CRTSCTS…
//將修改后的termios數據設置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
串口操作采用UNIX類似的方式,打開/關閉/發送/接收等基本操作采用類
似文件系統的方式進行,而一些屬性的設置和控制則使用termios來進行。
串口對應的設備文件名為”/dev/ttyS0”。
1. 打開串口
fd = open(“/dev/ttyS0”, O_RDWR);
如果只發送數據,可以使用O_WRONLY, 如果只接收數據,可以
設置成O_RDONLY。
2. 關閉串口
close(fd);
3. 接收數據
ret = read(fd, buf, 100);
串口默認的打開方式是非阻塞的,因此本函數只是接收緩沖中的數
據,而并非直接操作IO。如果要加入一些IO的屬性,請參見”使用
超時”和”設置串口屬性”。
如果緩沖中有接收到的數據,那么本函數將返回實際接收到的數據
長度,當然不會超過指定的100字節。
如果緩沖中沒有數據,那么將返回0。
如果接收失敗,那么將返回-1,錯誤代碼放在errno中。
4. 發送數據
ret = write(fd, buf, 100);
返回值表示實際發送的數據長度。
5. 設置串口屬性
tcgetattr(int fd, struct termios *termios_p);
tcsetattr(int fd, int optional_actions, struct termios *termios_p);
串口打開后,使用的串口屬性實際上是上一次關閉串口前的設置。
這個設置也就是一個結構struct termios,其中主要有以下的屬性:
tcflag_t c_iflag // 輸入屬性
tcflag_t c_oflag // 輸出屬性
tcflag_t c_cflag // 控制屬性
tcflag_t c_lflag // 本地屬性
cc_t c_cc[NCCS] // 控制字
c_iflag
IGNBRK 忽略接收到的break信號
BRKINT 如果IGNBRK被設置,break信號將被忽
略,否則如果BRKINT被設置,接收到
break信號將導致輸入/輸出隊列被清空,并
且當前控制串口的前臺進程將收到一個
SIGINT信號。如果IGNBRK和BRKINT都
沒有被設置 ,收到的break信號將被接收為
NULL,即{post.content}。但是如果PARMARK被設
置,接收到的break信號將被接收為7{post.content}
IGNPAR 忽略幀錯誤或奇偶校驗錯。
PARMARK 如果沒有設置IGNPAR,設置本屬性表示在
接收到的帶有錯誤的幀格式或奇偶校驗的字符將被前綴
377{post.content}。如果兩者都沒有設置,帶有錯誤的幀格式或奇偶
校驗的字符將被接收為{post.content}。
INPCK 打開輸入數據的奇偶校驗。
ISTRIP 濾掉第8位。
INLCR 將接收到的NL(換行)轉換成CR(回車)。
IGNCR 忽略接收到的CR。
ICRNL 將收到的CR轉換成NL(除非設置了
IGNCR)。
IUCLC 將接收到的大寫字符轉換成小寫。
IXON 打開輸出的XON/XOFF控制。
IXOFF 打開輸入的XON/XOFF控制。
c_oflag
OLCUC 將小寫字符轉換成大寫后輸出。
ONLCR 將NL轉換成CR-NL后輸出。
OCRNL 將CR轉換成NL后輸出。
ONLRET 不發送CR。
c_cflag
CBAUD 波特率掩碼,可以設置為
B2400
B4800
B9600
B19200
B38400
B57600
B115200
CSIZE 字符長度掩碼,可以設置為
CS5
CS6
CS7
CS8
CSTOPB 設置為兩個停止位。(默認為1個)
CREAD 打開接收功能。
PARENB 打開發送的奇偶校驗生成功能和接收的奇偶
校驗檢查功能,默認的是偶校驗。
PARODD 使用奇校驗。
CLOCAL 忽略modem信號線。
CRTSCTS 打開RTS/CTS硬件流控。
c_lflag
由于使用該類屬性不多,因此在此不作介紹。
c_cc
常用的是c_cc[VMIN],表示調用read函數時等待接收
的最少字符個數。例如設置為1時,read函數至少要讀
到1個字符才會返回。
optional_actions
可以設置為
TCSANOW 立即更新當前的設置。
TCSADRAIN 在當前發送緩沖的所由數據發送
完畢后再更新當前設置。
TCSAFLUSH 同TCSADRAIN,只是在更新前
所有為被讀取的收到的數據將被丟棄。
6. 使用超時
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
此函數用于控制接收、發送或異常出現之前的超時。
fd_set是句柄的集合,其中的句柄都是被監測的對象。
readfds表示需要監測其中句柄代表的設備是
否可以從中讀取數據。
writefds表示需要監測其中句柄代表的設備
是否可以向其寫入數據。
exceptfds表示需要監測其中句柄代表的設備
是否出現異常。
timeout是這樣一個結構
struct timeval{
long tv_sec; // 秒
long tv_usec; // 微秒
n則是所有監測的句柄中的最大值加一。
系統提供一些定義號的操作來操作fd_set:
FD_CLR(int fd, fd_set *fds);
將fd從fds集合中去掉。
FD_ISSET(int fd, fd_set *fds);
檢查fd是否在fds集合中。
FD_SET(int fd, fd_set *fds);
將fd加入fds集合中。
FD_ZERO(fd_set *fds);
將fds集合清空。
7. 其它串口操作
int tcdrain(int fd);
等待所有發送緩沖的數據全部發送出去后返回。
int tcflush(int fd, int queue_selector);
queue_selector:
TCIFLUSH
丟棄所有未讀取的接收到的數據。
TCOFLUSH
丟棄所有為發送的發送緩沖的數據。
TCIOFLUSH
丟棄上面兩種數據。
#include
#include
#include
#include
int main()
int fd;
struct termios attr;
fd_set fds;
struct timeval tv;
unsigned char buf[1024];
// 打開串口
fd = open(“/dev/ttyS0”, O_RDWR);
if (fd == -1)
return -1;
// 讀取串口當前屬性
tcgetattr(fd, &attr);
// 設置最少接收字符個數為0
attr.c_cc[VMIN] = 0;
// 不處理iflag、oflag和lflag
attr.c_iflag = 0;
attr.c_oflag = 0;
attr.c_lflag = 0;
// 設置波特率為9600,字符長度為8位,偶校驗,允許接收
attr.c_cflag = B9600 | CS8 | PARENB | CLOCAL | CREAD;
// 設置串口屬性
tcsetattr(fd, TCSANOW, &attr);
// 發送字符串
write(fd, “12345\n”, 6);
// 清除監測集合
FD_ZERO(&fds);
// 將串口句柄加入到監測集合中
FD_SET(fd, &fds);
// 設置超時為5秒
tv.tv_sec = 5;
tv.tv_usec = 0;
// 監測串口是否有數據接收到,超時為5秒
if (select(fd+1, &fds, NULL, NULL, &tv) <= 0)
return -1;
// 接收最多100個字符
read(fd, buf, 100);
// 關閉串口
close(fd);
return 0;
}
termios結構體內容:
成員 描述
-------------------------------------------
c_cflag 控制模式標志
c_lflag 本地模式標志
c_iflag 輸入模式標志
c_oflag 輸出模式標志
c_line line discipline
c_cc[NCCS] 控制字符
c_ispeed 輸入波特率
c_ospeed 輸出波特率
在termios結構中的四個標志控制了輸入輸出的四個不同部份。輸入模式標志c_iflag決定如何解釋和處理接收的字符。輸出模式標 志 c_oflag決定如何解釋和處理發送到tty設備的字符。控制模式標志決定設備的一系列協議特征,這一標志只對物理設備有效。本地模式標 志 c_lflag決定字符在輸出前如何收集和處理。
在串口傳輸中,用波特率來表示傳輸的速度,1波特表示在1秒鐘內可以傳輸1個碼元。波特率設置可以使用 cfsetispeed(& new_termios,B19200)和 cfsetospeed(&new_termios,B19200)這兩個函數來完成,默認的波特率為 9600baud。 cfsetispeed()函數用來設置輸入的波特率,cfsetospeed()函數用來設置輸出的波特率。B19200是 termios.h頭文件 里定義的一個宏,表示19200的波特率。
CLOCAL和CREAD是c_cflag成員中與速率相關的標志,在串口編程中,這兩個標志一定要有效,以確保程序在突發的作業控制或掛起時,不會成為端口的占有都,同時串口的接收驅動會自動讀入數據。設置方法如下:
termios_new.c_cflag |= CLOCAL; //保證程序不會成為端的占有者
termios_new.c_cflag |= CREAD; //使端口能讀取輸入的數據
設置串口屬性不能直接賦值,要通過對termios不同成員進行"與"和"或"操作來實現。在termios.h文件,定義了各種常量,如上面介 紹的CLOCAL,CREAD。這些常量的值是掩碼,通過把這些常量與termios結構成員進行邏輯操作就可實現串口屬性的設置。在編程時用"|="來 啟用屬性,用"&=~"來取消屬性。
9.3. c_iflag輸入標志說明
BRKINT和IGNBRK
如果設置了IGNBRK,中斷條件被忽略。如果沒有設置IGNBRK而設置了BRKINT,中斷條件清空輸入輸出隊列中所有的數據并且向tty 的前臺進程組中所有進程發送一個SIGINT信號。如果這兩個都沒有設置,中斷條件會被看作一個0字符。這時,如果設置了PARMRK,當檢測到一個幀誤 差時將會向應用程序發送三個字節'\377''\0''\0',而不是只發送一個'\0'。
*
PARMRK和IGNPAR
如果設定了IGNPAR,則忽略接收到的數據的奇偶檢驗錯誤或幀錯誤(除了前面提到的中斷條件)。如果沒有設置IGNPAR而設置了 PARMRK,當接收到的字節存在奇偶檢驗錯誤或幀錯誤的時候。將向應用程序發送一個三字節的'\377''\0''\n'錯誤報告。其中n表示所接收到 的字節。如果兩者都沒有設置,除了接收到的字節存在奇偶檢驗錯誤或幀誤差之外的中止條件都會向應用程序發送一個單字節('\0')的報告。
*
INPCK
如果設置,則進行奇偶校驗。如果不進行奇偶檢驗,PARMRK和IGNPAR將對存在的奇偶校驗錯誤不產生任何的影響。
*
ISTRIP
如果設置,所接收到的所有字節的高位將會被去除,保證它們是一個7位的字符。
*
INLCR
如果設置,所接收到的換行字符('\n')將會被轉換成回車符('\r')。
*
IGNCR
如果設置,則會忽略所有接收的回車符('\r')。
*
ICRNL
如果設置,但IGNCR沒有設置,接收到的回車符向應用程序發送時會變換成換行符。
*
IUCLC
如果IUCLC和IEXTEN都設置,接收到的所有大寫字母發送給應程序時都被轉換成小寫字母。POSIX中沒有定義該標記。
*
IXOFF
如果設置,為避免tty設備的輸入緩沖區溢出,tty設備可以向終端發送停止符^S和開始符^Q,要求終端停止或重新開始向計算機發送數據。通 過停止符和開始符來控制數據流的方式叫軟件流控制,軟件流控制方式較少用,我們主要還是用硬件流控制方式。硬件流控制在c_cflag標志中設置。
*
IXON
如果設置,接收到^S后會停止向這個tty設備輸出,接收到^Q后會恢復輸出。
*
IXANY
如果設置,則接到任何字符都會重新開始輸出,而不僅僅是^Q字符。
*
IMAXBEL
如果設置,當輸入緩沖區空間滿時,再接收到的任何字符就會發出警報符'\a'。POSIX中沒有定義該標記。
9.4. c_oflag輸出標志說明
OPOST是POSIX定義的唯一一個標志,只有設置了該標志后,其它非POSIX的輸出標記才會生效。
OPOST
開啟該標記,后面的輸出標記才會生效。否則,不會對輸出數據進行處理。
*
OLCUC
如果設置,大寫字母被轉換成小寫字母輸出。
*
ONLCR
如果設置,在發送換行符('\n')前先發送回車符('\r')。
*
ONOCR
如果設置,當current column為0時,回車符不會被發送也不會被處理。
*
OCRNL
如果設置,回車符會被轉換成換行符。另外,如果設置了ONLRET,則current column會被設為0.
*
ONLRET
如果設置,當一個換行符或回車符被發送的時候,current column會被設置為0。
*
OXTABS
如果設置,制表符會被轉換成空格符。
9.5. c_cflag控制模式標志說明
CLOCAL
如果設置,modem的控制線將會被忽略。如果沒有設置,則open()函數會阻塞直到載波檢測線宣告modem處于摘機狀態為止。
*
CREAD
只有設置了才能接收字符,該標記是一定要設置的。
*
CSIZE
設置傳輸字符的位數。CS5表示每個字符5位,CS6表示每個字符6位,CS7表示每個字符7位,CS8表示每個字符8位。
*
CSTOPB
設置停止位的位數,如果設置,則會在每幀后產生兩個停止位,如果沒有設置,則產生一個停止位。一般都是使用一位停止位。需要兩位停止位的設備已過時了。
*
HUPCL
如果設置,當設備最后打開的文件描述符關閉時,串口上的DTR和RTS線會減弱信號,通知Modem掛斷。也就是說,當一個用戶通過Modem拔號登錄系統,然后注銷,這時Modem會自動掛斷。
*
PARENB和PARODD
如果設置PARENB,會產生一個奇偶檢驗位。如果沒有設置PARODD,則產生偶校驗位,如果設置了PARODD,則產生奇校驗位。如果沒有設置PARENB,則PARODD的設置會被忽略。
*
CRTSCTS
使用硬件流控制。在高速(19200bps或更高)傳輸時,使用軟件流控制會使效率降低,這個時候必須使用硬件流控制。
9.6. c_cc[]控制字符說明
只有在本地模式標志c_lflag中設置了IEXITEN時,POSIX沒有定義的控制字符才能在Linux中使用。每個控制字符都對應一個按鍵組合(^C、^H等),但VMIN和VTIME這兩個控制字符除外,它們不對應控制符。這兩個控制字符只在原始模式下才有效。
c_cc[VINTR]
默認對應的控制符是^C,作用是清空輸入和輸出隊列的數據并且向tty設備的前臺進程組中的每一個程序發送一個SIGINT信號,對SIGINT信號沒有定義處理程序的進程會馬上退出。
*
c_cc[VQUIT]
默認對應的控制符是^\,作用是清空輸入和輸出隊列的數據并向tty設備的前臺進程組中的每一個程序發送一個SIGQUIT信號,對SIGQUIT信號沒有定義處理程序的進程會馬上退出。
*
c_cc[verase]
默認對應的控制符是^H或^?,作用是在標準模式下,刪除本行前一個字符,該字符在原始模式下沒有作用。
*
c_cc[VKILL]
默認對應的控制符是^U,在標準模式下,刪除整行字符,該字符在原始模式下沒有作用。
*
c_cc[VEOF]
默認對應的控制符是^D,在標準模式下,使用read()返回0,標志一個文件結束。
*
c_cc[VSTOP]
默認對應的控制字符是^S,作用是使用tty設備暫停輸出直到接收到VSTART控制字符。或者,如果設備了IXANY,則等收到任何字符就開始輸出。
*
c_cc[VSTART]
默認對應的控制字符是^Q,作用是重新開始被暫停的tty設備的輸出。
*
c_cc[VSUSP]
默認對應的控制字符是^Z,使當前的前臺進程接收到一個SIGTSTP信號。
*
c_cc[VEOL]和c_cc[VEOL2]
在標準模式下,這兩個下標在行的末尾加上一個換行符('\n'),標志一個行的結束,從而使用緩沖區中的數據被發送,并開始新的一行。POSIX中沒有定義VEOL2。
*
c_cc[VREPRINT]
默認對應的控制符是^R,在標準模式下,如果設置了本地模式標志ECHO,使用VERPRINT對應的控制符和換行符在本地顯示,并且重新打印當前緩沖區中的字符。POSIX中沒有定義VERPRINT。
*
c_cc[VWERASE]
默認對應的控制字符是^W,在標準模式下,刪除緩沖區末端的所有空格符,然后刪除與之相鄰的非空格符,從而起到在一行中刪除前一個單詞的效果。POSIX中沒有定義VWERASE。
*
c_cc[VLNEXT]
默認對應的控制符是^V,作用是讓下一個字符原封不動地進入緩沖區。如果要讓^V字符進入緩沖區,需要按兩下^V。POSIX中沒有定義VLNEXT。
要禁用某個控制字符,只需把它設置為_POSIX_VDISABLE即可。但該常量只在Linux中有效,所以如果程序要考慮移植性的問題,請不要使用該常量。
9.7. c_lflag本地模式標志說明
ICANON
如果設置,則啟動標準模式,如果沒有設置,則啟動原始模式。
*
ECHO
如果設置,則啟動本地回顯。如果沒有設置,則除了ECHONL之外,其他以ECHO開頭的標記都會失效。
*
ECHOCTL
如果設置,則以^C的形式打印控制字符,如:按Ctrl+C顯示^C,按Ctrl+?顯示^?。
*
ECHOE
如果在標準模式下設定了ECHOE標志,則當收到一個ERASE控制符時將刪除前一個顯示字符。
*
ECHOK和ECHOKE
在標準模式下,當接收到一個KILL控制符,則在緩沖區中刪除當前行。如果ECHOK、ECHOKE和ECHOE都沒有設置,則用ECHOCTL表示的KILL字符(^U)將會在輸出終端上顯示,表示當前行已經被刪除。
如果已經設置了ECHOE和ECHOK,但沒有設置ECHOKE,將會在輸出終端顯示ECHOCTL表示的KILL字符,緊接著是換行,如果設置了OPOST,將會通過OPOST處理程序進行適當的處理。
如果ECHOK、ECHOKE和ECHOE都有設置,則會刪除當前行。
在POSIX中沒有定義ECHOKE標記,在沒有定義ECHOKE標記的系統中,設置ECHOK則表示同時設置了ECHOKE標志。
*
ECHONL
如果在標準模式下設置了該標志,即使沒有設置ECHO標志,換行符還是會被顯示出來。
*
ECHOPRT
如果設置,則字符會被簡單地打印出來,包括各種控制字符。在POSIX中沒有定義該標志。
*
ISIG
如果設置,與INTR、QUIT和SUSP相對應的信號SIGINT、SIGQUIT和SIGTSTP會發送到tty設備的前臺進程組中的所有進程。
*
NOFLSH
一般情況下,當接收到INTR或QUIT控制符的時候會清空輸入輸出隊列,當接收到SUSP控制符時會清空輸入隊列。但是如果設置了NOFLUSH標志,則所有隊列都不會被清空。
*
TOSTOP
如果設置,則當一個非前臺進程組的進程試圖向它的控制終端寫入數據時,信號SIGTTOU會被被發送到這個進程所在的進程組。默認情況下,這個信號會使進程停止,就像收到SUSP控制符一樣。
*
IEXIEN
默認已設置,我們不應修改它。在Linux中IUCLC和幾個與刪除字符相關的標記都要求在設置了IEXIEN才能正常工作。
9.8. 下面介紹一些常用串口屬性的設置方法。
設置流控制
termios_new.c_cflag &= ~CRTSCTS; //不使用流控制
termios_new.c_cflag |= CRTSCTS; //使用硬件流控制
termios_new.c_iflag |= IXON|IXOFF|IXANY; //使用軟件流控制
屏蔽字符大小位
termios_new.c_cflag &= ~CSIZE;
設置數據位大小
termios_new.c_cflag |= CS8; //使用8位數據位
termios_new.c_cflag |= CS7; //使用7位數據位
termios_new.c_cflag |= CS6; //使用6位數據位
termios_new.c_cflag |= CS5; //使用5位數據位
設置奇偶校驗方式
termios_new.c_cflag &= ~PARENB; //無奇偶校驗
termios_new.c_cflag |= PARENB; //奇校驗
termios_new.c_cflag &= ~PARODD;
termios_new.c_cflag |= PARENB; //偶校驗
termios_new.c_cflag &= ~PARODD;
停止位
termios_new.c_cflag |= CSTOPB; //2位停止位
termios_new.c_cflag &= ~CSTOPB; //1位停止位
輸出模式
termios_new.c_cflag &= ~OPOST; //原始數據(RAW)輸出
控制字符
termios_new.c_cc[VMIN] = 1; //讀取字符的最小數量
termios_new.c_cc[VTIME] = 1; //讀取第一個字符的等待時間
關閉終端回顯,鍵盤輸入的字符不會在終端窗口顯示。
#include?
#include?
#include?
#include
int main(void)
{
struct termios ts,ots;
char passbuf[1024];
tcgetattr(STDIN_FILENO,&ts); /* STDIN_FILENO的值是1,表示標準輸入的文件描述符 */
ots = ts;
ts.c_lflag &= ~ECHO; /* 關閉回終端回顯功能*/
ts.c_lflag |= ECHONL;
tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts); /* 應用新終端設置 */
fgets(passbuf,1024,stdin); /* 輸入字符不會在終端顯示 */
printf("you input character = %s\n",passbuf);
tcsetattr(STDIN_FILENO,TCSANOW,&ots); /* 恢復舊的終端設備 */
}
Chapter 10. 安全
Table of Contents
10.1. 內核漏洞介紹
Linux內核以穩定和安全著稱,但隨著Linux使用范圍的不斷擴展,各種漏洞也慢慢被內核開發人員或黑客發現。這里介紹有關Linux內核和基于Linux的開源軟件的安全問題。
10.1. 內核漏洞介紹
權限提升類
*
拒絕服務類
*
溢出類
*
IP地址欺騙類
Chapter 11. 數據結構(Data Structure)
Table of Contents
11.1. 基礎概念
11.2. 線性數據結構
11.1. 基礎概念
在實際解決問題的時候,各種數據都不是孤立的,數據之間總是存在關系,這種數據之間的關系叫做數據結構。我們可以把數據結構的形式歸并為四種:
集合:數據之間沒有對應關系,但同屬于一個集合。如汽車是一個集合,編程語言也是一個集合。
*
線性結構:各數據有一一對應的關系,有前驅也有后續。
*
樹形結構:各數據間存在一對多的關系,有一個前驅但有多個后續。
*
圖:各數據間有多對多的關系,對前驅和后續沒有限制。
數據類型是一個值的集合和定義在這個值集上的一組操作的總稱。
數據類型可分兩類,一類是每個對象僅由單值組成,稱為原子類型,如整型、字符型等。另一類是由某種結構組成的類型,叫結構類型,如數組、字符串等。
抽象數據結構(Abstract Data Type,ADT)是一種數據類型及在這個類型上定義的一組合法的操作。
算法(Algorithm)是一個有窮規則(或語句、指令)的有序集合。通俗地說,就是計算機解決問題的過程。算法應具備以下幾個重要的特性:
輸入:一個算法有零個或多個輸入。
*
輸出:一個算法至少有一個輸出,這種輸出是同輸入有著某些特定關系的量。沒有輸出的算法是沒有意義的。
*
有窮性:一個算法必須總是在執行有窮步之后結束,且每一步都在有窮時間內完成。
*
確定性:算法中每條指令的含義都必須明確,無二義性。對相同的輸入,必須有相同的結果。
*
可行性:算法中的每條指令的執行時間都是有限的。
描述算法的工具:自然語言、流程圖、形式化語言和程序設計語言。
由瑞士科學家Niklaus Wirthrn提出的計算機界公認的公式:算法 + 數據結構 = 程序
算法設計的要求:正確、可讀、健壯、快速、節省存儲空間。
11.2. 線性數據結構
線性結構中的數據元素之間是一種線性關系,數據元素一個接一個地排列。如排除的隊列、表格中一行行的記錄等。數據元素可以包含多個數據項(字段),包含多個數據項的數據元素叫做記錄。由大量記錄組成的線性表又稱為文件。
線性表的數學表示模型:a0,a1,a2,...a(n-1)。
順序連續存放的線性表是最簡單的,稱為順序存儲結構線性表。它在內存開辟一片連續的存儲空間,讓線性表的第一個元素存放在內存空間的第一個位置, 第二個元素存放在第二個位置,其它元素以此類推。數據元素間的前驅和后繼關系表現在存放位置的前后關系上。順序存儲結構線性表算法在插入或刪除操作時的效 率不高。平均起來,每插入或刪除一個元素需要移動一半的元素,最壞的情況更要移動全部的元素。另外,順序表不利于存儲空間的分配。在經常需要進入插入或刪 除操作的線性表中,使用順序存儲結構線性表是不合適的。所以我們有了鏈式存儲結構線性表。
[Note]
數組就是順序存儲結構的程序實現。
鏈式存儲結構線性表由結點組成,每個節點由一個數據元素和一個指向下個結點的指針組成。每個結點中如果只有一個指向后續指針的鏈表,叫單鏈表。由于鏈表通過指針指向下一個結點,所以數據元素可以分散存儲。
單鏈表的建立是一種動態內在管理操作,表中的每個節點占用的存儲空間無需預先指定,而是在運行時動態申請。
單鏈表一旦創建就可對鏈表進行操作。
查找值為x的節點,并返回該節點地址。算法分析:從單鏈表的第一個節點開始,判斷當前節點的數據域的值是否為x,若是,則返回該節點的指針域,否則,依據指針域內的指針查找下一節點,直至表結束。若找不到,則返回空。
*
查找第i個節點,返回期指針。算法分析:從單鏈表的第一個節點開始,依次判斷當前節點是否為第i個節點,若是則返回其指針,否則,依據指針域內的指針查找下一節點,直至表結束。若找不到,則返回空。
Chapter 12. 網絡編程
Table of Contents
12.1. TCP/IP協議分析
12.2. 入門示例程序
12.1. TCP/IP協議分析
EthernetII幀的結構(DMAC+SMAC+Type+Data+CRC),EthernetII幀的大小是有限制的,最小不能小于64字節,最大不能超過1518字節,否則幀會被丟棄。一個EthernetII幀包括的內容有:
DMAC,目的MAC地址,占48個bit,共6個字節。
*
SMAC,源MAC地址,占48個bit,共6個字節。
*
Type,幀類型,如ip,arp等。占16個bit,共2個字節。
*
Data,幀數據,容量是變化的,但最大不能越過1500個字節,最小不能小于46個字節。
*
CRC,校驗碼,占32個bit,共4個字節。
IP包結構:
tcgetattr(fd0,&ts0);//把fd0(ttyS0)的屬性賦值給ts0 //如果要設置某個選項,那么就使用"|="運算:|=
//如果關閉某個選項就使用"&="和"~"運算:&= ~
ts0.c_cflag |= B9600 | CS7 | CLOCAL | CREAD | PARENB ;
//波特率為9600 | 數據位為7位 | 保證程序不會成為端口的占有者 | 使能端口讀取輸入的數據 |
ts0.c_cflag &= ~CRTSCTS;//取消流控制 |= CRTSCTS 為硬件流控制
//|= IXON | IXOFF |IXANY; 為軟件流控制
ts0.c_lflag &= ~ECHO; //取消回顯輸入字符/* | IEXTEN | ISIG); */
ts0.c_lflag &= ~ECHONL;
ts0.c_iflag &= ~IXOFF;//同下一行為取消軟件流控制
ts0.c_iflag &= ~IXON;
ts0.c_cflag &= ~CSIZE;//屏蔽字符大小位
ts0.c_cflag |= CS7;//數據位為7位
ts0.c_cflag |= PARENB;
//ts0.c_cflag &= ~PARENB;//無校驗
/*
無校驗 ts0.c_cflag &= ~PARENB;
奇校驗 ts0.c_cflag |= (PARODD | PARENB);
偶校驗 ts0.c_cflag &= ~ PARENB;opt.c_cflag &= ~PARODD;
空格 ts0.c_cflag &= ~PARENB;opt.c_cflag &= ~CSTOPB; */
ts0.c_lflag &= ~ICANON;
ts0.c_oflag &= ~ONLCR;
ts0.c_iflag &= ~INLCR;
/*
ISIG 當接收到字符INTR,QUIT,SUSP或DSUSP時,產生相應的信號。
XCASE (不屬于POSIX;LINUX下不支持)如果同時設置了ICANON,終端只有大寫。輸入被
轉換為小寫,除了以\前綴的字符。輸出時,大寫字符被前綴\,小寫字符被轉換成大寫。
ECHO 回顯輸入字符。
ECHOE 如果同時設置了ICANON,字符ERASE擦除前一個輸入字符,WERASE擦除前一個詞。
ECHOK 如果同時設置了ICANON,字符KILL刪除當前行。
ECHONL 如果同時設置了ICANON,回顯字符NL,即使沒有設置ECHO。
ECHOCTL (不屬于POSIX)如果同時設置了ECHO,除了TAB,NL,START和STOP之外的ASCII
控制信號被回顯為^x,這里X是比控制信號大0x40的ASCII碼。例如字符0x08(BS)被回顯為
^H。
ECHOPRT (不屬于POSIX)如果同時設置了ICANON和IECHO,字符在刪除的同時被打印。
ECHOKE (不屬于POSIX)如果同時設置了ICANON,回顯KILL時將刪除一行中的每個字符,
如同指定了ECHOE和ECHORPT一樣。
DEFECHO (不屬于POSIX)只在一個進程讀的時候回顯。
FLUSHO (不屬于POSIX;LINUX不支持)輸出被刷新。這個標志可以通過鍵入字符DISCARD
來打開和關閉。
NOFLSH 禁止產生SIGINT,SIGQUIT和SIGSUSP信號時刷新輸入和輸出隊列。
TOSTOP 向試圖寫控制終端的后臺進程組發送SIGTTOU信號。
PENDIN (不屬于POSIX;LINUX不支持)在讀入一個字符時,輸入隊列中的所有字符被重新
輸出。(bash用他來處理typeahead)。
IEXTEN 啟用實現自定義的輸入處理。這個標志必須與ICANON同時使用,才能解釋特殊字符
EOL2,LNEXT,REPRINT和WERASE,IUCLC標志才有效。
ts0.c_cc[VMIN] = 0;
ts0.c_cc[VTIME] = 0;
/*
IGNBRK 忽略接收到的break信號
BRKINT 如果IGNBRK被設置,break信號將被忽
略,否則如果BRKINT被設置,接收到
break信號將導致輸入/輸出隊列被清空,并
且當前控制串口的前臺進程將收到一個
SIGINT信號。如果IGNBRK和BRKINT都
沒有被設置 ,收到的break信號將被接收為
NULL,即{post.content}。但是如果PARMARK被設
置,接收到的break信號將被接收為7{post.content}
IGNPAR 忽略幀錯誤或奇偶校驗錯。
PARMARK 如果沒有設置IGNPAR,設置本屬性表示在
接收到的帶有錯誤的幀格式或奇偶校驗的字符將被前綴
377{post.content}。如果兩者都沒有設置,帶有錯誤的幀格式或奇偶
校驗的字符將被接收為{post.content}。
INPCK 打開輸入數據的奇偶校驗。
ISTRIP 濾掉第8位。
INLCR 將接收到的NL(換行)轉換成CR(回車)。
IGNCR 忽略接收到的CR。
ICRNL 將收到的CR轉換成NL(除非設置了
IGNCR)。
IUCLC 將接收到的大寫字符轉換成小寫。
IXON 打開輸出的XON/XOFF控制。
IXOFF 打開輸入的XON/XOFF控制。
c_oflag
OLCUC 將小寫字符轉換成大寫后輸出。
ONLCR 將NL轉換成CR-NL后輸出。
OCRNL 將CR轉換成NL后輸出。
ONLRET 不發送CR */
ts0.c_cflag &= ~CSTOPB;
ts0.c_iflag |= IGNBRK;
ts0.c_lflag &= ~IEXTEN;
ts0.c_lflag |= NOFLSH;
rc = cfsetospeed(&ts0,B9600);
rc = tcsetattr(fd0,TCSAFLUSH,&ts0);