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小于缓冲区的大小,是原子性的操作?)
    • 套接字工作在非阻塞模式下。可能当前只传输了一部分请求字节(非阻塞模式下缓冲区不足只写入部分?)
    • 部分字节传输完成后出现了异步错误,比如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

使用步骤

  1. 设定信号处理例程,默认情况下内核发送的信号是SIGIO
  2. 设定文件描述符的属主,通常调用进程会是属主fcntl(fd, F_SETOWN, pid)
  3.  flags=fcntl(fd, F_GETFL)fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK)
    复制代码
  4. 等待内核的信号(边缘触发),尽可能多执行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相关推荐

  1. linux/unix编程手册-56_60

    title: linux/unix编程手册-55_56 date: 2018-10-05 11:53:07 categories: programming tags: tips linux/unix编 ...

  2. linux/unix编程手册-16_20

    title: linux/unix编程手册-16_20 date: 2018-06-06 11:53:07 categories: programming tags: tips linux/unix编 ...

  3. linux/unix编程手册-6_10

    title: linux/unix编程手册-6_10 date: 2018-05-15 11:53:07 categories: programming tags: tips linux/unix编程 ...

  4. Linux系统编程手册-源码的使用

    Linux系统编程手册-源码的使用 转自:http://www.cnblogs.com/pluse/p/6296992.html 第三章后续部分重点介绍了后面章节所要使用的头文件及其实现,主要如下: ...

  5. 学习linux/unix编程方法的建议[转]

    首先先学学编辑器,vim, emacs什么的都行. 然后学make file文件,只要知道一点就行,这样就可以准备编程序了. 然后看看<C程序设计语言>K&R,这样呢,基本上就可以 ...

  6. 学习linux/unix编程方法的建议-转

    假设你是计算机科班出身,计算机系的基本课程如数据结构.操作系统.体系结构.编译原理.计算机网络你全修过  我想大概可以分为4个阶段,水平从低到高  从安装使用=>linux常用命令=>li ...

  7. 我把某大厂P8大能手写的 Linux+网络编程 手册搞到手了

    时间飞逝,转眼间毕业七年多,从事 Java 开发也六年了.我在想,也是时候将自己的 Java 整理成一套体系. 这一次的知识体系面试题涉及到 Java 知识部分.性能优化.微服务.并发编程.开源框架. ...

  8. linux c 编程手册,Linux C/C++编程手册查阅方法

    Linux Programmer's Manual & User Commands https://www.kernel.org/doc/man-pages/ 搜索框输入epoll调用搜索引擎 ...

  9. linux unix编程思想,Unix网络编程思想

    本次博客主要总结参考<Unix网络编程>卷一前四章的知识,对TCP一对一通信进行重新改造和分析,经典就是经典,无可替代! 一.为什么使用包裹函数 任何现实世界的程序都必须检查每个函数调用是 ...

最新文章

  1. SQL中的撤销索引、表以及数据库
  2. 龙岩学院计算机专业宿舍,龙岩学院的各个学院位置有谁能说下
  3. 软件设计原则(七)合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
  4. Struts中Action三种接收参数的方式?
  5. js修改mysql数据库数据_Node.js操作mysql数据库增删改查
  6. 程序员过关斩将--更加优雅的Token认证方式JWT
  7. leetcode 73 矩阵置零 C++ 两种解法
  8. 第二章 数据的表示和运算 2.1.4 奇偶校验 [计算机组成原理笔记]
  9. [转载] Java中对象数组的使用
  10. 借助datetime和pyttsx3在Python中创建闹钟
  11. oracle vm win10,win10系统oraclevm卸载不了错误2503的解决方法
  12. 21.Shiro在springboot与vue前后端分离项目里的session管理
  13. ROS采坑日记(3)----在ROS下 编译ORB_SLAM2时遇到问题:[rosbuild] rospack found package ORB_SLAM2 at ........
  14. 安卓一键ROOT权限获取软件排行榜
  15. 程序员的自我进化:共享经济新模式——共享员工
  16. mysql一条sql是一个事物么_mysql 事物浅析
  17. android 虚拟键 高度,Android获取虚拟按键的高度(适配全面屏)
  18. win10系统遇到深色背景自动降低亮度解决方法、WIN10自动调节亮度、如何关闭WIN10自动调节亮度
  19. 二叉树的基本性质及证明
  20. mysql脚本修改大量数据问题

热门文章

  1. vsts~CI/CD实现自动化编译
  2. 如何上传本地文件到github又如何删除自己的github仓库
  3. 为什么大部分程序员看不起PHP这门语言?
  4. 2016光伏创新如何突围融资困境?
  5. IntelliJ IDEA license server
  6. C#中的三种委托方式:Func委托,Action委托,Predicate委托
  7. Excel 不能直接打开文件的解决方法
  8. Discuz! $_DCACHE数组变量覆盖漏洞
  9. JAVA工程师必学技能,进阶涨薪的推进器!这份实战教程请收下
  10. Nacos配置的优先级