相关API笔记(二)
Linux网络编程高级I/O函数
1. pipe
pipe函数用于创建一个管道,实现进程间通信
代码语言:javascript复制#include <unistd.h>
//成功返回0,失败返回-1并设置errno
int pipe(int fd[2]);
参数:
fd: pipe函数创建的两个文件描述符对应管道两端,分别为“读”端和“写端”(可记忆为读写),即fd[0]为管道的读端,fd[1]为管道的写端。
默认情况下这对文件描述符是阻塞的,对空的fd[0]执行读操作或对满的fd[1]执行写操作会阻塞。
创建双向管道
代码语言:javascript复制#include <sys/types.h>
#include <sys/socket.h>
//成功返回0,失败返回-1并设置errno
int socketpair(int domain, int type, int protocol, int fd[2]);
参数:
前三个参数与socket系统调用的三个参数完全相同,但是domian只能使用UNIX本地域协议族AF_UNIX,因为仅能在本地使用这个双向管道
fd: 与pipe系统调用的参数一样,不过这里创建的文件描述符都是即可读又可写的
2. dup和dup2
代码语言:javascript复制#include <unistd.h>
//二者都是失败返回-1并设置errno
int dup(int file_descriptor);
//dup2可理解为后者重定向到前者
int dup2(int file_descriptor_one, int file_descriptor_two);
参数:
dup和dup2的参数都是文件描述符,具体作用如下:
dup函数创建一个新的文件描述符,该新的描述符和原有文件描述符file_descriptor指向相同文件,管道或者网络连接, 并且dup返回的文件描述符总是取系统当前可用的最小整数值。
dup2函数与dup类似,不过它返回的是第一个不小于file_descriptor_tow的整数值
代码语言:javascript复制//例子。重定向STDOUT
//使用dup
void dup_out(){
//...
close(STDOUT);
dup(fd);
printf("dup");
close(fd);
}
//使用dup2
void dup2_out(){
dup2(fd, STDOUT);
printf("dup2");
close(fd);
}
3. readv和writev
readv函数将数据从文件描述符读到分散的内存块中,即分散读
writev函数则将多块分散的内存数据一并写入文件描述符中,即集中写
代码语言:javascript复制#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec* vector, int count);
ssize_t writev(int fd, const struct iovec* vector, int count);
//iovec封装了一块内存的起始位置和长度
struct iovec{
void* iov_base; //内存起始地址
size_t iov_len; //这块内存的长度
};
参数:
fd: 被操作的目标文件描述符
vector: iovec结构数组
count: vector数组的长度
代码语言:javascript复制//eg
char buf1[1024], buf2[1024];
//...往buf1填充数据
struct iovec iv[2];
iv[0].iov_base = buf1;
iv[0].iov_len = sizeof(buf1);
iv[1].iov_base = buf2;
iv[1].iov_len = sizeof(buf2);
//writev,集中写,把buf1,buf2中的数据写到connfd
ret = writev(connfd, iv, 2);
//readv,分散读,把键盘的输入读出并放入到buf1,buf2中
ret = readv(STDIN_FILENO, iv, 2);
3. sendfile
sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),避免在内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,称为零拷贝
代码语言:javascript复制#include <sys/sendfile.h>
//成功返回传输的字节数,失败返回-1并设置errno
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
参数:
out_fd: 待写入内容的文件描述符,可理解为数据进入到out_fd输出,流到in_fd,我们需要把数据写给它让它又得输出。
in_fd: 待读出内容的文件描述符,可理解为文件流入到in_fd中,我们需要从它这里读出数据。
offset: 指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。
count: 指定在文件描述符in_fd和out_fd之间传输的字节数
4. mmap和munmap
mmap用于申请一段内存空间,这段内存可以作为进程间通信的共享内存,可以将文件直接映射到其中。
munmap函数则释放mmap创建的这段内存空间。
代码语言:javascript复制#include <sys/mman.h>
//成功返回指向目标区域的指针,失败返回MAP_FAILED((void*)-1)
void* mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
//成功返回0,失败返回-1并设置errno
int munmap(void *start, size_t length);
参数:
start: 用户指定某个特定的地址作为这段内存的起始地址,如果设置为NULL,则系统自动分配一个地址。
length: 指定内存段的长度
prot: 设置内存段的访问权限
值 | 含义 |
---|---|
PROT_READ | 内存段可读 |
PROT_WRITE | 内存段可写 |
PROT_EXEC | 内存段可执行 |
PROT_NONE | 内存段不能访问 |
flags:可控制内存段内容被修改后程序的行为
5. splice
splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。
代码语言:javascript复制#include <fcntl.h>
//成功返回移动字节的数量,失败返回-1并设置errno
ssize_t splice(int fd_in, loof_t* off_in, int fd_out, loff_t* off_out, size_t len, unsigned int flags);
参数:
fd_in: 待输入数据的文件描述符
off_in: 如果fd_in是管道文件描述符,则必须设置为NULL,若不是(如socket),则off_in表示从输入数据流的何处开始读取数据。
fd_out: 待输出数据的文件描述符
off_out: 同off_in
len: 指定移动数据的长度
flags: 可被设置为如下某些值的按位或
使用splice时,fd_in和fd_out至少有一个是管道文件描述符
其常见errno:
6. tee
tee函数用于在两个管道文件描述符之间复制数据,也是零拷贝操作。
代码语言:javascript复制#include <fcntl.h>
//成功返回复制的数据量,失败返回-1并设置errno
ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);
参数:
与splice相同,但是fd_in和fd_out必须都是管道文件描述符
代码语言:javascript复制//eg
//把管道pipe1的输出端数据复制到管道pipe2的输入端
int ret = tee(pipe1[0], pipe2[1], 32768, SPLICE_F_NONBLOCK);
7. fcntl
fctnl(file control), 提供对文件描述符的各种控制操作
代码语言:javascript复制#include <fctnl.h>
//成功返回值见后面表格,失败返回-1并设置errno
int fcntl(int fd, int cmd, ...);
参数:
fd: 被操作的文件描述符
cmd: 操作类型,根据操作类型可能还需要第三个可选参数arg
对于F_SETFL,可更改的几个标志如下面的描述:
- O_NONBLOCK 非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
- O_APPEND 强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
- O_DIRECT 最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
- O_ASYNC 当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候
//eg
int setnonblocking(int fd){
int old_option = fctnl(fd, F_GETFL); /* 获取文件描述符旧的状态标志 */
int new_option = old_option | O_NONBLOCK; /* 设置非阻塞标志 */
fcntl(fd, F_SETFL, new_option);
return old_option; /* 返回文件描述符旧的状态标志,以便日后恢复该状态标志 */
}