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. php 购物车的例子

    网上搜到的,简单容易理解.cookie存购物车ID,db存购物车数据. //购物车session的产生代码   1 if(! $session && ! $scid) {    2 / ...

  2. 2)Java中的==和equals

    Java中的==和equals   1.如果比较对象是值变量:只用==   2.如果比较对象是引用型变量:      ==:比较两个引用是不是指向同一个对象实例.      equals:       ...

  3. android AES 部分机器javax.crypto.BadPaddingException: pad block corrupted

    package com.bbguoxue.poetry.util; import java.security.SecureRandom; import javax.crypto.Cipher; imp ...

  4. 十款基础级WordPress插件

    1.Akismet插件 Akismet是全球最受欢迎的反垃圾插件,专为对抗"博客spam"."评论spam"而生.Akismet是WordPress官方插件之一 ...

  5. 编写高质量代码改善C#程序的157个建议

    1.使用StringBuilder或者使用string.Format("{0}{1}{2}{3}", a, b, c, d)拼接字符串. 2.使用默认转型方法,比如使用类型内置的P ...

  6. 【转载!】关于C#的RawSocket编程的问题

    Q:你好! 看过了你在csdn上发表的<用C#下的Raw Socket编程实现网络封包监视>,觉得很感兴趣,而且对我的帮助很大.不过在调试的过程中遇到一些问题,特此向你请教一下.谢谢! 首 ...

  7. Mongodb的索引--学习笔记(未完)

    全文索引 建立方法: --在articles集合的key字段上创建全文索引 db.articles.ensureIndex({key:"text"}) --在articles集合的 ...

  8. 关于Cygwin——包管理、替换默认终端、同MSYS的比较

    (搬运自我在SegmentFault的博客) Cygwin 是一个用于 Windows 的类 UNIX shell 环境. 它由两个组件组成:一个 UNIX API 库,它模拟 UNIX 操作系统提供 ...

  9. "Programming"和"Programming"是同一个"Programming"吗?

    什么意思? C语言没有专门的字符串类型,但是,它同样可以处理字符串.本文不是讨论字符串的使用,而是讨论C字符串之间的关系.如题,在C语言代码中,如果定义#define STR = "Prog ...

  10. 观察者模式学习--使用jdk的工具类简单实现

    观察者模式学习之二:使用jdk的自带的工具类实现,与自己实现相比,两者有以下的区别: 1,自己实现,需要定义观察者的接口类和目标对象的接口类.使用java util的工具类,则不需要自己定义观察者和目 ...