Linux高性能服务器之高级IO复用(8)

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 Linux高性能服务器之高级IO复用(8),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

前言

1.用于创建文件描述符的函数,包括 pipe、 dupldup2函数。
2.用于读写数据的函数,包括readv/writev、sendfile、mmap/munmap、splice和 tee 函数
3.控制IO行为和属性的函数,包括fcntl函数。

pipe函数(队列思想)

#include<unistd.h>
int pipe(int fd[2])

通过pipe函数创建的这两个文件描述符fd[0]和fd[1]分别构成管道的两端,往fd[1]写人的数据可以从fd[0]读出。并且,fd[0]只能用于从管道读出数据****,fd[1]则只能用于往管道写入数据,而不能反过来使用。如果要实现双向的数据传输,就应该使用两个管道。(都是堵塞的)
管道内部传输的数据是字节流,这和TCP字节流的概念相同。但二者又有细微的区别。应用层程序能往一个TCP连接中写入多少字节的数据,取决于对方的接收通告窗口的大小和本端的拥塞窗口的大小。而管道本身拥有一个容量限制,它规定如果应用程序不将数据从管道读走的话,该管道最多能被写入多少字节的数据。自Linux 2.6.11内核起,管道容量的大小默认是65536字节。我们可以使用fcntl函数来修改管道容量
创建双向管道:

#include<sys/types.h>
#include<sys/socket.h>
int socketpair(int domain,int type,int protocol,int fd[2])
注意:domain只能AF_UNIX

dup and dup2

目的:有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接(比如CGI编程)。这可以通过下面的用于复制文件描述符的dup或dup2函数来实现:

   #include <unistd.h>

       int dup(int oldfd);
       int dup2(int oldfd, int newfd);

dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor 指向相同的文件、管道或者网络连接。并且 dup返回的文件描述符总是取系统当前可用的最小整数值。dup2和dup类似,不过它将返回第一个不小于file_descriptor_two的整数值。dup和dup2系统调用失败时返回-1并设置crrno

READV AND WRITEV

  #include <sys/uio.h>

       ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

       ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec {
               void  *iov_base;    /* Starting address */
               size_t iov_len;     /* Number of bytes to transfer */
           };


fd参数是被操作的目标文件描述符。vector参数的类型是iovec结构数组。该结构体描述一块内存区。iovcnt参数是vector数组的长度,即有多少块内存数据需要从fd读出或写到fd。readv和writev在成功时read or write fd 的字节数

sendfile

sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。sendfile函数的定义如下:


       ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);


in_fd参数是待读出内容的文件描述符out_fd参数是待写入内容的文件描述符。offset参数指定从读入文件流的哪个位置开始读,如果为空,则使用读人文件流默认的起始位置。count参数指定在文件描述符in_fd和 out_fd之间传输的字节数。sendfile成功时返回传输的字节数,失败则返回-1并设置errno。该函数的man手册明确指出,in_fd必须是一个支持mmap函数的 out_fd socket 网络传输专用

mmap and munmap

mmap 函数用于申请一段内存空间。我们可以将这段内存作为进程间通信的共享内存也可以将文件直接映射到其中。munmap函数则释放由mmap创建的这段内存空间。它们的定义如下:

#include <sys/mman.h>

       void *mmap(void *addr, size_t length, int prot, int flags,
             int fd, off_t offset);
       int munmap(void *addr, size_t length);

addr参数允许用户使用某个特定的地址作为这段内存的起始地址。如果它被设置成NULL,则系统自动分配一个地址。length参数指定内存段的长度。
prot参数用来设置内存段的访问权限。它可以取以下几个值的按位或:
PROT_READ, 内存段可读
PROT_WRITE,内存段可写。
PROT_EXEC,内存段可执行。
PROT_NONE,内存段不能被访问。

splice and tee

函数用于在两个文件描述符之间移动数据,也是零拷贝操作。splice函数的定义如下:

#include <fcntl.h>

       ssize_t splice(int fd_in, loff_t *off_in, int fd_out,
       
                      loff_t *off_out, size_t len, unsigned int flags);

fd_in参数是待输人数据的文件描述符。如果fd_in是一个管道文件描述符,那么off_in参数必须被设置为NULL。如果fd_in不是一个管道文件描述符(比如 socket),那么off_in表示从输人数据流的何处开始读取数据。此时,若off_in被设置为NULL,则表示从输入数据流的当前偏移位置读人﹔若off_in不为NULL,则它将指出具体的偏移位置。fd_out/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流。len参数指定移动数据的长度﹔flags参数则控制数据如何移动,它可以被设置为下表中的某些值的按位或
在这里插入图片描述
tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。tee函数的原型如下:

#include<fcntl.h>
ssize_t tee(int fd_in,int fd_out,size_t len,unisgned int flags);

fcntl

fcntl函数,正如其名字(file control)描述的那样,提供了对文件描述符的各种控制操作。另外一个常见的控制文件描述符属性和行为的系统调用是ioctl,而且 ioctl 比 fentl能够执行更多的控制。但是,对于控制文件描述符常用的属性和行为,fentl函数是由POSIX规范指定的首选方法。所以本书仅讨论fcntl函数。fcntl函数的定义如下:

#include<fcntl.h>
int fcntl(int fa,int cmd,...)

在这里插入图片描述
在这里插入图片描述

int setnoblocking(int fd)
{
 int old_option=fcntl(fd,F_GETFL);
 int new_option=old_option|O_NONBLOCK
 fcntl(fd,F_SETFL,new_option);
 return old_option
}

SIGIO和SIGURG这两个信号与其他Linux信号不同,它们必须与某个文件描述符相关联方可使用:当被关联的文件描述符可读或可写时,系统将触发SIGIO信号﹔当被关联的文件描述符(而且必须是一个socket)上有带外数据可读时,系统将触发SIGURG信号。将信号和文件描述符关联的方法,就是使用fcntl函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。使用SIGIO时,还需要利用fcntl 设置其O_ASYNC标志.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/129712.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!