linux/unix编程手册-61_64
title: linux/unix編程手冊-61_64 date: 2018-10-07 11:53:07 categories: programming tags: tips
linux/unix編程手冊-61(SOCKET 高級特性)
流套接字上的部分讀和部分寫
- 如果沒有足夠的緩沖區來傳輸所有字節并且滿足以下任意一個(關于write的fd不是普通文件時的原子性可以再搜搜)
- write()調用之后被信號中斷(一次write,buf小于緩沖區的大小,是原子性的操作?)
- 套接字工作在非阻塞模式下??赡墚斍爸粋鬏斄艘徊糠终埱笞止?#xff08;非阻塞模式下緩沖區不足只寫入部分?)
- 部分字節傳輸完成后出現了異步錯誤,比如TCP鏈接出現問題
- readn和writen的實現,避免部分讀寫
shutdown()系統調用
int shutdown(int sockfd, int how); 復制代碼socket上調用close會關閉雙向兩端,shutdown會調節,可使一個方向上進行套接字傳輸,通過how
- SHUT_RD:關閉讀端,之后的讀操作返回文件EOF,但數據可以寫入;在UNIX域流套接字執行SHUT_RD,對端進程會受到SIGPIPE
- SHUT_WR:關閉寫端,后續本地的寫操作會產生SIGPIPE信號和EPIPE錯誤,對端寫入可以在套接字上讀取,ssh連接會用到
- SHUT_RDWR:shutdown會關閉套接字通道無論套接字是否關聯其它文件描述符,是針對打開文件來的,而close是針對文件描述符,需要所有socket的fd都關閉才會斷開
- 后兩個TCP會主動關閉,避免對TCP使用SHUT_RD
flags的具體參數略,flags給對于socket的I/O,提供了read,write的拓展
sendfile的優化
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); // 返回實際傳輸的字節數,-1 error 復制代碼- out_fd通常指向套接字
- in_fd文件必須是可以mmap的,套接字不行
linux的TCP sendfile()的優化 (例如http請求,首部信息胡通過write,頁面數據可以sendfile,導致TCP傳輸2個報文,網絡帶寬利用率低)
- linux TCP_CORK選項,
其他基于TCP的一些略
一些常用的命令
- netstat:顯示系統中Internet和UNIX域套接字狀態
- tcpdump:
其他高級功能(當手冊查吧)
linux/unix編程手冊-62(終端(串行終端,鍵盤,顯示器之類的))
終端驅動程序
- 規范模式:終端輸入按行處理,且打開了行編輯功能(可以刪除行等等之類)
- 非規范模式:終端輸入不會被裝配成行 ,禁用了行編輯,讀操作的完成時間等也是由c_cc數組中的位或其他參數決定的
獲取和修改終端屬性
struct termios {tcflag_t c_iflag; /* Input flags */tcflag_t c_oflag; /* Output flags */tcflag_t c_cflag; /* Control flags */tcflag_t c_lflag; /* Local modes */cc_t c_line; /* Line discipline (nonstandard)*/cc_t c_cc[NCCS]; /* Terminal special characters */speed_t c_ispeed; /* Input speed (nonstandard; unused) */speed_t c_ospeed; /* Output speed (nonstandard; unused) */ };int tcgetattr(int fd, struct termios *termios_p);int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);// 0 s, -1 e // fd必須是指向終端的描述符 復制代碼具體略,各種flag略
stty
stty -a
略(偏了)
使用命令tty,表示當前終端對應的設備文件,(以下#表示數字)
- 結果顯示:/dev/pts/# 表示偽終端
- 結果顯示:/dev/tty# 表示虛擬終端
- 結果顯示:/dev/console 表示物理終端(控制臺)
- 結果顯示:/dev/ttys# 表示串行終端
linux/unix編程手冊-63(其他被選I/O模型)
傳統的read,write,沒有設置O_NONBLOCK時,會以阻塞模式打開文件,無法處理以下需求
- 以非阻塞檢查文件描述符是否可執行I/O操作
- 同時檢查多個文件描述符看看能不能執行I/O操作
之前不好的處理方案
- 非阻塞I/O盲輪訓
- 多進程或多線程
好的解決方案,這些技術不會實際執行I/O只是告訴我們某個文件描述符就緒了,等準備好了才會進行I/O調用。
- I/O多路復用(select(), poll())
- 可移植性好
- 延展性差,fd多時效率低
- 信號驅動I/O(和異步I/O的區別是,內核通知信號后,會等待數據從內核態傳到用戶態,異步I/O不會等待內核傳輸過程,因為會將數據準備好復制到用戶空間之后才會通知,看看posix的aio,go的話是基于CSP模型 CSP和Actors)
- 完全利用信號I/O的特點需要用到不可移植的linux的專有特性,移植性不好
- 可以高效大量fd
- linux epoll api(也屬于I/O多路復用)
- 專屬linux
- 和信號驅動I/O比較
- 避免了處理信號復雜性
- 指定檢查類型(讀就緒還是寫就緒)
- 可以選擇水平觸發或者邊緣觸發
插一個
libevent 提供了I/O事件的抽象(不包含異步I/O?)水平觸發/邊緣觸發
- 水平觸發通知:如果文件描述符可以非阻塞的執行I/O系統調用,認為它準備就緒
- 水平觸發允許我們重復檢測文件描述符的就緒狀態,因此沒有必要每次觸發后盡可能多的執行I/O會導致其他文件描述符饑餓狀態
- 邊緣觸發通知:如果文件描述符自上次狀態檢測以來有了新的活動(I/O),需要觸發通知
- 只有I/O事件發生時我們才會收到通知,應當在收到通知后盡可能多的執行I/O,不然的話下次輪訓雖可用但不會通知,同時文件描述符通常應當設置成非阻塞。
備選I/O模型應當需要和O_NONBLOCK標志一起使用
I/O多路復用
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); //return ready fd 數量, 0 timeout, -1 error // fdset最大1024,即FD_SETSIZE //select返回后,fdset只包含就緒的fdvoid FD_ZERO(fd_set *fdset); //置空 void FD_SET(int fd, fd_set *fdset); //加 void FD_CLR(int fd, fd_set *fdset); //刪 int FD_ISSET(int fd, fd_set *fdset); // 1在,否則不在 復制代碼- readfds:輸入是否就緒的文件描述符集合
- writefds:輸出。。。
- exceptfds:一些特殊情況的文件描述符集合
- 流式套接字上收到了帶外數據
- 連接到信包模式下的偽終端主設備上的從設備狀態發生了改變
- nfds需要比三個fdset最大文件描述符大1。提高select效率(select返回之后,可以根據0-nfds來遍歷)
- timeout
- 兩個域都為0則不會阻塞(沒有情況吧)
- NULL或timeout不為0會阻塞置
- fdset至少一個就緒
- 被信號終端
- 超時
- 返回值為正數時,如果同一fd在對個set中都存在,都就緒了,會被重復計算
- timeout:
- -1,一直阻塞直到有ready的
- 0,不會阻塞
- 大于0,阻塞毫秒數
不同fd,select和poll的表現(可參考5.9,相關內容是基礎)
- 普通文件(因為read,write不會阻塞)
- 總會被select標記為可讀,可寫,poll后再revents中返回POLLIN,POLLOUT
- 終端和偽終端
- 管道和FIFO
- 套接字
select 和 epoll對比
- 實現都使用了內核poll例程集合(不同于poll系統調用,具體以后查一下)
- poll是為每個fd調用內核poll例程
- select 通過宏將例程信息轉化為select事件
- 性能
- fd范圍小或者密集時效率差不多
- fd稀疏時poll性能會好很多(不需要遍歷0~nfd)
- api(略)
select和poll的問題
- 每次調用內核必須檢查所有被指定的fd
- 每次調用需要傳遞一個表示所有被檢查文件fd的數據結構到內核(select還每次都要初始化這個數組)
- 每次返回。需要檢查數據結構中的每個元素
- select和poll的調用結果內核不會緩存
信號驅動I/O
使用步驟
信號驅動I/O 可應用在套接字,終端/偽終端及其他設備,管道/FIFO, inotify文件描述符
曾今信號驅動I/O也叫異步I/O,現在異步I/O特指, POSIX AIO規范提供的功能,POSIX AIO 進程會請求內核執行一次I/O操作,當I/O完成或出錯之后,進程會得到內核的通知
不同fd觸發I/O就緒的信號的條件(略)
優化信號驅動I/O的使用
- 在同時檢查大量fd時,信號驅動I/O的優勢在于,內核會記住要檢查的文件描述符(O_ASYNC),僅當I/O事件實際發生時,才會發生信號
- 可以通過使用實時信號取代SIGIO來優化性能
- 因為SIGIO不會排隊,處理了第一個,后續的通知會丟失
- 如果信號處理例程是通過sigaction來安裝,可以通過sa.sa_flags指定SA_SIGINFO,傳遞是哪個fd,發生了何種事件
- 具體略
epoll 編程接口
- 性能上和信號驅動I/O相似but
- 避免復雜的信號處理流程(ex:信號隊列溢出)
- 可以指定檢查類型
epoll_create()
- 內核會在內存中創建一個新的i-node,并打開文件描述符
epoll_ctl()
- fd 不能是普通文件,否則會EPERM, fd可以是epoll fd ,建立層次關系
- op
- EPOLL_CTL_ADD:fd已存在會EEXIST
- EPOLL_CTL_MOD:通過ev修改fd上的設定事件,fd需要已經在列表中,否則ENOENT
- EPOLL_CTL_DEL:fd 不存在會ENOENT
- 多線程中一個線程,epoll_ctl修改了列表,另一個線程epoll_wait()的列表會同步,會立刻更新
epoll_wait()
- maxevents應該只是如果超過,只會返回這么多,沒有等待這么多才返回的語義
- EPOLLONESHOT, fd就緒一次之后,會將fd置為非激活狀態,若重新監控需要epoll_ctl重設
- 使用dup()類似函數復制一個epoll_fd時,或fork()時,新的epoll_fd指向同一epoll數據結構(中文版翻譯錯了)
- 監控和的fd映射的打開文件描述(第二層)關聯的所有fd都關閉后,epoll會自動把它從監控列表刪除
epoll和I/O多路復用的對比
- 每次調用poll/select,內核會檢查所有指定的文件描述符,每次調用都會和內核傳遞一次文件描述符的數據結構;
- epoll在epoll_ctl指定監控fd時,內核會在打開文件描述上下文想關聯列表記錄這個fd,每當一個fd就緒時,會在epoll_fd的就序列表添加一個元素(一個文件的I/O會使所有關聯fd就緒),每次調用epoll_wait()不需要從用戶空間傳數據到內核空間,只有內核返回。
- epoll默認是水平觸發,和select/epoll類似
- epoll設置邊緣觸發之后,一次epoll_wait時,一個fd有多個I/O事件,會合并成一次單獨的通知(信號驅動會是多個)
- 優化,記錄下每個就緒fd,不要一次讀完一個fd,設定一個限度。(水平的話與需要做限度,但是不用記錄)
linux/unix編程手冊-64(SOCKET 偽終端)
偽終端程序通過IPC,連接終端程序
偽終端程序流程(server 端,例如ssh)
- 驅動程序打開偽終端主設備(sshd)
- fork()一個子進程
- 調用setsid()改變會話id,創建一個新會話,子進程會成為新會話的首進程
- 打開從設備,子進程成為從設備的控制進程
- 用dup()類系統調用復制標準的fd,0,1,2
- 調用exec()啟動連接到偽終端從設備的面向終端程序(shell)
偽終端的系統調用(略)
偽終端I/O(類似雙向管道,不同fd關閉的異常,信包模式)(略)
END
總結
以上是生活随笔為你收集整理的linux/unix编程手册-61_64的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java获得时间 String与Time
- 下一篇: linux 高性能服务排查方式