日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux/unix编程手册-61_64

發布時間:2024/4/13 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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的實現,避免部分讀寫
#include<unistd.h> #include<errno.h>ssize_t readn(int fd, void *buffer, size_t n){ssize_t numRead;size_t totRead;char *buf;buf = buffer;for (totRead = 0; totRead < n;){numRead = read(fd, buf, n-totRead);if (numRead == 0)return totRead;if (numRead == -1){if(errno==EINTR)continue;elsereturn -1;}totRead += numRead;buf += numRead;}return totRead; }ssize_t writen(int fd, const void *buffer, size_t n){ssize_t numWritten;size_t totWritten;const char* buf;buf = buffer;for (totWritten = 0; totWritten < n){numWritten = write(fd, buf, n-totWritten);if (numWritten <= 0){if (numWritten == -1 && errno == EINTR)continue;elsereturn -1;}totWritten += numWritten;buf += numWritten;}return totWritten; }復制代碼

shutdown()系統調用

#include<sys/socket.h>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
#include<sys/socket.h>ssize_t recv(int sockfd, void *buffer, size_t length, int flags);ssize_t send(int sockfd, const void *buffer, size_t length, int flags); 復制代碼

flags的具體參數略,flags給對于socket的I/O,提供了read,write的拓展

sendfile的優化

#include<sys/sendfile.h>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選項,
#include<sys/socket.h>int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 0 success, -1 error 復制代碼

其他基于TCP的一些略

一些常用的命令

  • netstat:顯示系統中Internet和UNIX域套接字狀態
  • tcpdump:

其他高級功能(當手冊查吧)

linux/unix編程手冊-62(終端(串行終端,鍵盤,顯示器之類的))

終端驅動程序

  • 規范模式:終端輸入按行處理,且打開了行編輯功能(可以刪除行等等之類)
  • 非規范模式:終端輸入不會被裝配成行 ,禁用了行編輯,讀操作的完成時間等也是由c_cc數組中的位或其他參數決定的

獲取和修改終端屬性

#include<termios.h>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多路復用

#include<sys/time.h> #include<sys/select.h>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中都存在,都就緒了,會被重復計算
#include<poll.h>int poll(struct pollfd fds[], nfds_t nfds, int timeout); //return ready fd 數量, 0 timeout, -1 error //select 將fd放在三個集合中,poll則在pollfd上標明需要檢查的類型struct pollfd{int fd;short events; //需要檢查的類型掩碼short revents; //實際發生的類型掩碼 }; // 掩碼常亮,略 復制代碼
  • 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

使用步驟

  • 設定信號處理例程,默認情況下內核發送的信號是SIGIO
  • 設定文件描述符的屬主,通常調用進程會是屬主fcntl(fd, F_SETOWN, pid)
  • flags=fcntl(fd, F_GETFL)fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK) 復制代碼
  • 等待內核的信號(邊緣觸發),盡可能多執行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:信號隊列溢出)
      • 可以指定檢查類型
    #include<sys/epoll.h>int epoll_create(int size); // return fd success ,-1 error // size 已經被忽略了,之前是內核為數據結構劃分的初始大小int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev); // 0 s, -1 estruct epoll_event {uint32_t events; /* 感興趣事件集合*/epoll_data_t data; /* fd就緒時,回傳的信息*/ };typedef union epoll_data {void *ptr; /* Pointer to user-defined data */int fd; /* File descriptor */uint32_t u32; /* 32-bit integer */uint64_t u64; /* 64-bit integer */ } epoll_data_t;int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout); // return ready fd 數量, 0 timeout, -1 error // timeout的處理poll 復制代碼

    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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。