UNIX環境高級編程,終端IO--unix環境高級編程讀書筆記

 2023-11-19 阅读 28 评论 0

摘要:???? 終端IO部分整體上讀了兩遍,感覺這一部分的內容又亂又碎,不太好理解。讀完了之后,仍然感覺什么也沒有學到。先做一個膚淺的記錄,等到以后要用到的時候,再回來補充。 ??? 1.終端IO的工作方式 ???? 終端IO有兩種不同的工作方式,規

???? 終端IO部分整體上讀了兩遍,感覺這一部分的內容又亂又碎,不太好理解。讀完了之后,仍然感覺什么也沒有學到。先做一個膚淺的記錄,等到以后要用到的時候,再回來補充。

??? 1.終端IO的工作方式

???? 終端IO有兩種不同的工作方式,規范方式輸入處理和非規范方式輸入處理:

(1)規范方式輸入處理:終端以行為單位進行處理,對于每個讀要求,終端驅動程序最多返回一行。
(2)非規范方式輸入處理:輸入字符不以行為單位進行處理。

終端設備是由位于內核中的終端驅動程序所控制的,每個終端設備有一個輸入隊列和一個輸出隊列。

???? 2.對終端設備進行操作

???? 關于終端IO的屬性存放在一個? termios? 的結構體中,這個結構體中的成員如下:

           tcflag_t c_iflag;      /* input modes */tcflag_t c_oflag;      /* output modes */tcflag_t c_cflag;      /* control modes */tcflag_t c_lflag;      /* local modes */cc_t     c_cc[NCCS];   /* special characters */

通過對這些數據成員的設置,可以來控制終端的屬性。c_iflag? 用來控制終端的輸入屬性,c_oflag? 用來控制輸出的屬性,c_cflag? 用來控制一些其他屬性,c_lflag? 用來控制驅動程序和用戶之間的界面。c_cc? 數組包含了所有可以更改的特殊字符,這在稍后會介紹到。
???? 對終端設備的操作函數基本如下:
       int tcgetattr(int fd, struct termios *termios_p);int tcsetattr(int fd, int optional_actions,const 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);speed_t cfgetispeed(const struct termios *termios_p);speed_t cfgetospeed(const struct termios *termios_p);int cfsetispeed(struct termios *termios_p, speed_t speed);int cfsetospeed(struct termios *termios_p, speed_t speed);

我們可以通過這幾個函數來操作終端的屬性。tcgetattr? 函數用來獲取終端的屬性,終端的屬性通過第二個參數返回。tcsetattr? 函數用來設置終端的屬性。這里需要注意的是最后四個用來設置和獲得波特率的函數,它們是建立在前面兩個函數的基礎之上的。也就是說,如果想要獲得波特率,需要先調用tcgetattr函數來獲得終端的屬性,然后在將獲得的終端的屬性作為參數傳遞給? cfgetispeed? 函數或? cfgetospeed? 函數。同樣,當需要設置波特率時,需要先將波特率在? termios? 結構體中設置好,然后調用? tcsetattr? 函數。

UNIX環境高級編程,

???? 下面的一個例子用來禁止終端中的中斷字符(即CTRL+C),并且將文件結束的字符更改為CTRL+B:

#include <termios.h>
#include <stdio.h>
#include <unistd.h>int main(void)
{struct termios term;long vdisable;if(isatty(STDIN_FILENO)==0){printf("standard input is not terminal device\n");return -1;}if( (vdisable=fpathconf(STDIN_FILENO,_PC_VDISABLE))<0 ){printf("fpathconf error\n");return -1;}if(tcgetattr(STDIN_FILENO,&term)<0){printf("tcgetattr error\n");return -1;}term.c_cc[VINTR] = vdisable;term .c_cc[VEOF] = 2;if(tcsetattr(STDIN_FILENO,TCSAFLUSH,&term)<0){printf("tcsetattr error\n");return -1;}return 0;
}

???? 該程序先用? isatty? 函數來測試? STDIN_FILENO? 描述符所指向的文件是否是終端設備,如果測試的文件描述符是指向一個終端設備的話則返回1,否則返回0。

???? 然后使用函數? fpathconf? 獲取系統中的? _PC_VDISABLE? 的值,將這個值保存在? c_cc? 數組中的相應位置就可以禁止使用這個位置所代表的特殊字符。

???? 接著我們調用? tcgetattr? 函數用來獲取終端IO的屬性,然后設置? c_cc? 數組的? VINTR? 的位置為? _PC_VDISABLE,表示禁止使用中斷符號CTRL+C。而將VEOF的位置的值更改為2,即表示將文件的結束符號修改為CTRL+B,同理如果要修改為CTRL+A,則這個地方的值為1。

redis筆記,???? 最后使用? tcsetattr? 函數設置修改過的屬性,使這些屬性生效。tcsetattr? 函數的第二個參數用來表示設置屬性之后,這些屬性生效的時間,這個參數有三種選擇:

TCSANOW:更改立即生效
TCSADRAIN:發送了所有輸出之后,更改才生效。如果更改輸出參數,則應該使用此選項。
TCSAFLUSH:發送了所有輸出之后,更改才生效。這里和上面一個的不同是,當更改發生時,未讀的所有輸入數據都被刪除(丟棄)。

???? 3.一些函數

???? (1)ctermid? 函數用來返回控制終端的名字,一般是? /dev/tty。它的函數原型如下:

       #include <stdio.h>char *ctermid(char *s);

它將控制終端的名字存放在由參數? s? 指示的緩存中,如果? s? 為空,則啟動一個靜態存儲區,并將這個靜態存儲區的首地址返回給調用者。下面是? ctermid? 函數的一個實現:
#include <stdio.h>
#include <string.h>static char ctermid_name[L_ctermid];char* ctermid(char* str)
{if(str==NULL){str = ctermid_name;}return strcpy(str,"/dev/tty"); //strcpy returns str
}

???? (2)isatty? 函數的一種實現如下:

#include <termios.h>
#include <stdio.h>int isatty(int fd)
{struct termios term;return (tcgetattr(fd,&term)!=-1);
}

可以用下面的程序來進行測試? isatty? 函數:
#include <termios.h>
#include <stdio.h>int isatty(int fd)
{struct termios term;return (tcgetattr(fd,&term)!=-1);
}int main(void)
{printf("fd 0:%s\n",isatty(0)?"tty":"not a tty");printf("fd 1:%s\n",isatty(1)?"tty":"not a tty");printf("fd 2:%s\n",isatty(2)?"tty":"not a tty");return 0;
}

將程序編譯為? a.out,運行程序,輸出結果:
fd 0:tty
fd 1:tty
fd 2:tty

如果將標準輸入和標準出錯重定向,按照下面的方式運行程序:
./a.out</etc/passwd 2>/tmp/t

輸出結果如下:
fd 0:not a tty
fd 1:tty
fd 2:not a tty

java筆記總結、???? (3)ttyname? 返回在指定描述符上打開的終端設備的路徑名,例如下面的一個程序可以演示一下它的使用:

#include <unistd.h>
#include <stdio.h>
int main(void)
{printf("fd 0:%s\n",isatty(0)?ttyname(0):"not a tty");printf("fd 1:%s\n",isatty(1)?ttyname(1):"not a tty");printf("fd 2:%s\n",isatty(2)?ttyname(2):"not a tty");return 0;
}

???? 4.終端窗口的大小

???? linux? 系統提供了一個跟蹤終端大小的功能,內核為每個終端或者是偽終端保存了一個? winsize? 結構體,這個結構體中保存了當前終端大小的信息,這個結構體如下:

struct winsize {unsigned short ws_row;unsigned short ws_col;unsigned short ws_xpixel;unsigned short ws_ypixel;
};

通過? ioctl? 函數的? TIOCGWINSZ? 命令可以取此結構體的當前值。當然也可以通過? ioctl? 的? TIOCSWINSZ? 命令可以將此結構體的新值存放到內核中,如果新值與存放在內核中的當前值不同,則會向前臺進程組發送SIGWINCH信號,系統對此信號的默認處理是忽略該信號。當前終端窗口大小發生變化時,也會產生? SIGWINCH? 信號。下面的程序演示了當終端的大小發生變化時,就打印當前終端的大小:
#include <signal.h>
#include <termios.h>
#ifndef TIOCGWINSZ
#include <sys/ioctl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>static void pr_winsize(int fd)
{struct winsize size;if(ioctl(fd,TIOCGWINSZ,(char*)&size)<0){printf("TIOCGWINSZ error\n");exit(-1);}printf("%d rows,%d columns\n",size.ws_row,size.ws_col);
}static void sig_winch(int signo)
{printf("SIGWINCH received\n");pr_winsize(STDIN_FILENO);return;
}int main(void)
{if(isatty(STDIN_FILENO)==0){exit(1);}if(signal(SIGWINCH,sig_winch)==SIG_ERR){printf("signal error\n");return -1;}pr_winsize(STDIN_FILENO);for(;;){pause();}
}

將程序編譯之后,運行,不斷的改變終端的大小,就可以看到輸出結果類似與下面:
55 rows,178 columns
SIGWINCH received
6 rows,63 columns
SIGWINCH received
6 rows,124 columns
SIGWINCH received
24 rows,124 columns
SIGWINCH received
53 rows,178 columns
SIGWINCH received
55 rows,178 columns

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/183708.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息