6.1 pipe函数

pipe函数创建一个管道,用于实现进程间通信

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

参数包含两个文件描述符fd[0]和fd[1],往fd[1]写入的数据可以从fd[0]读出

默认情况下这对文件描述符都是阻塞的。如果用read调用读取一个空管道,read将会阻塞直到有数据可读为止;write类似。

如果将fd[0]\fd[1]都设置为非阻塞,则read和write会有不同行为。如果fd[1]引用计数减少到0,即没有任何进程需要往管道写入数据,则对fd[0]的read操作返回0,即读到了EOF;反之,如果fd[0]引用计数减少至0,则对fd[1]的write操作将失败,并引发SIGPIPE信号。

管道内部传输的数据是字节流,这和TCP字节流的概念相同。从Linux 2.6.11开始,管道容量的大小是默认65536字节。可以使用fcntl修改。

socketpair函数可以方便的创建双向管道:

 #include<sys/types.h>
#include<sys/socket.h>
int socketpair(int domain, int type, int protocol, int fd[]);

6.2 dup函数和dup2函数 

复制文件描述符,它们经常用来重定向进程的stdin、stdout和stderr。文件描述符是与打开文件或者数据流 相关联的整数, 0、1、2 是系统保留的三个文件描述符,分别对应标准输入、标准输出、标准错误

 #include<unistd.h>
int dup(int file_descriptor);
int dup2(int file_descriptor_one, int file_descriptor_two);

利用函数dup,我们可以复制一个描述符。传给该函数一个既有的描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝。这意味着,这两个描述符共享同一个数据结构。

dup2函数跟dup函数相似,但dup2函数允许调用者规定一个有效描述符和目标描述符的id。dup2函数成功返回时,目标描述符(dup2函数的第二个参数)将变成源描述符(dup2函数的第一个参数)的复制品,换句话说,两个文件描述符现在都指向同一个文件,并且是函数第一个参数指向的文件。

通过dup和dup2创建的文件描述符并不继承原来的文件描述符的属性,比如close-on-exec和non-blocking等。

例子:CGI服务器的基本原理

    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
if ( connfd < )
{
printf( "errno is: %d\n", errno );
}
else
{
close( STDOUT_FILENO );
dup( connfd );
printf( "abcd\n" );
close( connfd );
}

关闭标准输出文件描述符,dup总返回系统最小的可用文件描述符,所以实际返回值是1,这样就将所连接的socket文件描述符connfd定位到标书输出,printf调用的输出将被客户端获得,这就是CGI服务器的基本原理

6.3 readv函数和writev函数 

读指从文件描述符读到内存,写指从内存写入文件描述符

readv函数将数据从文件描述符读到分散的内存块中,即分散读;writev函数则将多块分散的内存数据一并写入文件描述符中,即集中写。

 #include<sysy/uio.h>
ssize_t readv(int fd, const struct iovec* vector, int count);
ssize_t writev(int fd, const struct iovec* vector, int count);
struct iovec {
void *iov_base; //starting address
size_t iov_len; //number of bytes to transfer
};

例子:web服务器对客户端的HTTP请求响应,HTTP应答包含1个状态行,多个头部字段,1个空行和文档内容,这些可能不在一起,可以用writev写入一个文件描述符

 struct iovec iv[];
iv[ ].iov_base = header_buf;
iv[ ].iov_len = strlen( header_buf );
iv[ ].iov_base = file_buf;
iv[ ].iov_len = file_stat.st_size;
ret = writev( connfd, iv, );

6.4 sendfile函数

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

 #include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);

in_fd必须是一个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道;

而out_fd则必须是一个socket,由此可见,sendfile几乎是专门为在网络上传输文件设计的。

offset:读入位置

count:传输字节数

6.5 mmap函数和munmap函数

mmap函数用于申请一段内存空间。我们可以将这段内存作为进程间通信的共享内存,也可以将文件直接映射到其中。munmap释放这段内存空间。

 #include <sys/mman.h>
void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *start, size_t length);

start可以指定这段内存的起始地址,当为NULL时,系统自动分配一个地址。

prot参数用来设置访问权限。按位或:

 PROT_READ        //内存可读
PROT_WRITE //内存可写
PROT_EXEC //内存段可执行
PROT_NONE //内存段不能被访问

flags参数控制内存段修改后的行为

6.6 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);

使用splice函数,fd_in和fd_out必须至少有一个是管道文件描述符,splice函数调用成功时返回移动字节的数量

如果fd_in是管道描述符,那么off_in必须为NULL。若不是管道,则标示从什么位置读取,fd_out类似

flags参数:

 SPLICE_F_MOVE    //按整页内存移动数据,只是给内核一个提示,没有实际效果。
SPLICE_F_NONBLOCK //实际效果还会受到文件描述符本身的阻塞状态影响。
SPLICE_F_MORE //给内核一个提示:后续的splice调用将读取更多数据。
SPLICE_F_GIFT //没有效果

例子:使用splice函数实现的echo服务器

     int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
if ( connfd < )
{
printf( "errno is: %d\n", errno );
}
else
{
int pipefd[];
assert( ret != - );
ret = pipe( pipefd );
ret = splice( connfd, NULL, pipefd[], NULL, , SPLICE_F_MORE | SPLICE_F_MOVE ); //从connfd流入的客户端数据定向到管道
assert( ret != - );
ret = splice( pipefd[], NULL, connfd, NULL, , SPLICE_F_MORE | SPLICE_F_MOVE ); //将管道输出定向到connfd客户端连接的文件描述符
assert( ret != - );
close( connfd );
}

6.7 tee函数

在两个管道文件描述符之间复制数据,也是零拷贝。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作

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

6.8 fcntl函数

提供了对文件描述符的各种控制操作

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

在网络编程中,fcntl函数通常用来将一个文件描述符设置为非阻塞的 重要常用

 #include <fcntl.h>
int fcntl(int fd, int cmd, ...);
int setnonblocking(int fd)
{
int old_option = fcntl( fd ,F_GETFD ); //获取文件描述符旧状态标志
int new_option = odl_option | O_NONBLOCK; //设置非阻塞标志
funcl( fd, F_SETFL, new_option);
return old_option; //返回旧状态以便日后恢复
}

6 高级IO函数的更多相关文章

  1. 第14章——高级IO函数

    1.套接字超时 套接字IO函数设置超时的方法有三种: (1)调用alarm. (2)select (3)使用SO_RECTIMEO和 SO_SNDTIMEO 选项 上面三种方法适用于输入输出操作(re ...

  2. UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流

    1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. ...

  3. UNIX高级环境编程(6)标准IO函数库 - 流的概念和操作

    标准IO函数库隐藏了buffer大小和分配的细节,使得我们可以不用关心预分配的内存大小是否正确的问题. 虽然这使得这个函数库很容易用,但是如果我们对函数的原理不熟悉的话,也容易遇到很多问题.   1 ...

  4. (十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  5. 标准C IO函数和 内核IO函数 效率(时间)比较

    前言 标准C提供的文件相关的IO函数,除标准错误输出是不带缓冲的(可以尽快的将错误消息显示出来)之外,所有与终端相关的都是行缓冲,其余都是全缓冲的. 我们可以使用setbuf,setvbuf改变指定流 ...

  6. 高级IO

    # 高级IO 特殊的IO操作,包括文件锁.系统V的流.信号驱动的I/O.多路转I/O(select和pull函数).readv和writev函数以及存贮映射I/O等概念和函数. ## 文件锁 文件锁是 ...

  7. linux网络编程之IO函数

    Linux操作系统中的IO函数主要有read(),write(),recv(),send(),recvmsg(),sendmsg(),readv(),writev(). 接收数据的recv()函数 # ...

  8. linux_api之高级IO

    本篇索引: 1.引言 2.非阻塞IO 3.记录锁(文件锁) 4.io多路复用(I/O multiplexing ) 5.异步IO 6.存储映射IO 1.引言 我们第三篇学习了对IO的open.read ...

  9. python高级之函数

    python高级之函数 本节内容 函数的介绍 函数的创建 函数参数及返回值 LEGB作用域 特殊函数 函数式编程 1.函数的介绍 为什么要有函数?因为在平时写代码时,如果没有函数的话,那么将会出现很多 ...

随机推荐

  1. 分享O'Reilly最新C语言指针数据

    1.推荐书名 Understanding.and.Using.C.Pointers.pdf 2. 本书目录 Table of Content Chapter 1. Introduction Chapt ...

  2. angularjs2 学习笔记(三) 服务

    在anglar2中服务是什么? 如果在项目中有一段代码是很多组件都要使用的,那么最好的方式就是把它做成服务. 服务是一种在组件中共享功能的机制,当我们使用标签将多个组件组合在一起时我们需要操作一些数据 ...

  3. ListView与GridView异步加载图片

    原理很简单,主要是用到了回调方法,下面是异步加载图片的类 <span style="font-size:16px;">package com.xxx.xxx; impo ...

  4. php捕获网络页面

    <?php $url = 'http://jwzx.cqupt.edu.cn/pubYxKebiao.php?type=zy&yx=06'; $html = file_get_conte ...

  5. python并行迭代

    并行迭代:同时并行遍历两个列表 for line1,line2 in zip(line1_list, line2_list): ... 无聊,贴一段刚才的代码: import sys import s ...

  6. Oracle Insert 多行(转)

    1.一般的insert 操作. 使用语法insert into table_name[(column[,column...])] values (value[,value…])的insert语句,每条 ...

  7. opencv颜色识别代码分享

    android 平台 opencv 实现颜色识别代码:http://www.eyesourcecode.com/thread-40682-1-1.htmlopencv的颜色识别简单实现的代码:http ...

  8. js原型继承与多态 How to apply virtual function in javascript

    function BaseClass() { this.hello = function() { this.talk(); } this.talk = function() { document.wr ...

  9. C++中不可重载的5个运算符

    大多数运算符都是可以重载的,但是有5个运算符C++语言规定是不可以重载的. 1. .(点运算符),通常用于去对象的成员,但是->(箭头运算符),是可以重载的 2.::(域运算符),即类名+域运算 ...

  10. ThinkPHP运算符与PHP运算符对照表

    ThinkPHP运算符与PHP运算符对照表 ThinkPHP标签 说明及对应PHP标签 备注 eq 等于(=)(==:用于模板判断时) 可用于查询条件与模板判断 neq 不等于(!=) 可用于查询条件 ...