高级IO
1 非阻塞IO/有限状态机编程
1.1 基本概念
定义
有限状态机(Finite State Machine) 缩写为 FSM,状态机有 3 个组成部分:状态、事件、动作。
- 状态:所有可能存在的状态。包括当前状态和条件满足后要迁移的状态。
- 事件:也称为转移条件,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
- 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是* 必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
作用
用来解决复杂流程的问题:
- 简单流程:一个程序的自然流程是结构化
- 复杂流程:一个程序的自然流程不是结构化的
自然流程即解决问题的直接思路
1.2 有限状态机实现文件复制
分析
实现的功能就是从文件1中复制文件到文件2或者从文件2中复制文件到文件1,有限状态机共有4个状态,分别是read读态,write写态,exception出错态和terminate结束态。
FSM模型图如下:
实现
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024
enum {
STATE_R = 1, //读态
STATE_W = 2, //写态
STATE_EX = 3, //异常态
STATE_T = 4 //退出态
};
struct fsm_st {
int state;//当前状态机的状态
int sfd;
int dfd;
char buf[BUFSIZE];
int len;
int pos;
char *errstr;
};
//状态机推动函数
static void fsm_driver(struct fsm_st *fsm) {
int ret;
switch (fsm->state) {
case STATE_R:
fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
if (fsm->len == 0) fsm->state = STATE_T; // 读完切换到结束态
else if (fsm->state < 0) {
if (errno == EAGAIN) fsm->state = STATE_R; // 假错接着读
else {
fsm->state = STATE_EX; // 真错切换到异常态
fsm->errstr = "read()";
}
} else {
fsm->pos = 0;
fsm->state = STATE_W; // 读成功切换为写态
}
break;
case STATE_W:
ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
if (ret < 0) {
if (errno == EAGAIN) fsm->state = STATE_W;
else {
fsm->errstr = "write()";
fsm->state = STATE_EX;
}
} else {
fsm->pos += ret;
fsm->len -= ret;
if (ret == 0) fsm->state = STATE_R;
else fsm->state = STATE_W;
}
break;
case STATE_EX:
perror(fsm->errstr);
fsm->state = STATE_T;
break;
case STATE_T:
break;
default:
abort();
}
}
static void relay(int fd1, int fd2) {
int fd1_save, fd2_save;
struct fsm_st fsm12, fsm21;
// 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
fd1_save = fcntl(fd1, F_GETFL);
fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
fd2_save = fcntl(fd2, F_GETFL);
fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
// 状态机初始化
fsm12.state = STATE_R;
fsm12.sfd = fd1;
fsm12.dfd = fd2;
fsm21.state = STATE_R;
fsm21.sfd = fd2;
fsm21.dfd = fd1;
while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
fsm_driver(&fsm12);
fsm_driver(&fsm21);
}
//恢复文件描述符之前的状态
fcntl(fd1, F_SETFL, fd1_save);
fcntl(fd2, F_SETFL, fd2_save);
}
int main(int argc, char **argv) {
// 模拟用户打开两个设备的操作
int fd1, fd2;
fd1 = open(TTY1, O_RDWR);
if (fd1 < 0) {
perror("open()");
exit(1);
}
fd2 = open(TTY2, O_RDWR | O_NONBLOCK);
if (fd2 < 0) {
close(fd1);
perror("open()");
exit(1);
}
// 调用数据中继函数
relay(fd1, fd2);
close(fd1);
close(fd2);
exit(0);
}
1.3 中继引擎
头文件
#ifndef LINUX_RELAYER_H
#define LINUX_RELAYER_H
#include <stdint-gcc.h>
#include <time.h>
#define REL_JOBMAX 10000
enum {
STATE_RUNNING = 1,
STATE_CANCELED,
STATE_OVER
};
/**
* 表示任务状态的结构体,这个结构体是我们希望用户看到的,当我们实际操作的时候可以不是这个结构体
* 即实现结构体部分的隐藏和封装
*/
struct rel_stat_st {
int state;
int fd1;
int fd2;
int64_t count12, count21; //通讯的字节数
time_t start, end; //记录任务的起始时间
};
/**
* 添加任务
* @param fd1
* @param fd2
* @return >=0 表示成功,返回当前任务id;-EINVAL 表示失败参数非法; -ENOSPC 表示失败,任务数组满
* -ENOMEM 表示失败,内存分配有误
*/
int rel_addjob(int fd1, int fd2);
/**
* 取消任务
* @param id
* @return =0 表示指定任务已经成功取;-EINVAL 表示失败,参数非法; -EBUSY 失败,任务被重复取消
*/
int rel_canceljob(int id);
/**
* 给任务收尸
* @param id 任务id
* @param old 被收尸任务的状态
* @return =0 成功,指定任务已终止并返回状态; -EINVAL 失败,参数非法
*/
int rel_waitjob(int id, struct rel_stat_st *old);
/**
* 返回id任务的状态
* @param id 任务id
* @param stat 任务状态
* @return 0 成功,指定任务状态已返回; -EINVAL 失败,参数非法
*/
int rel_statjob(int id, struct rel_stat_st *stat);
#endif //LINUX_RELAYER_H
实现
#include <malloc.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "relayer.h"
#define BUFSIZE 1024
enum {
STATE_R = 1, //读态
STATE_W = 2, //写态
STATE_EX = 3, //异常态
STATE_T = 4 //退出态
};
struct rel_fsm_st {
int state;//当前状态机的状态
int sfd;
int dfd;
char buf[BUFSIZE];
size_t len;
int pos;
char *errstr;
int64_t count;
};
struct rel_job_st {
int fd1, fd2;
int job_state;
struct rel_fsm_st fsm12, fsm21;
time_t start, end;
int fd1_save, fd2_save;
};
static struct rel_job_st * rel_job[REL_JOBMAX];
static pthread_mutex_t mut_rel_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static pthread_t tid_relayer;
static int get_free_pos_unlocked() {
int i;
for (i = 0; i < REL_JOBMAX; i++) {
if (rel_job[i] == NULL) {
return i;
}
}
return -1;
}
//状态机推动函数
static void fsm_driver(struct rel_fsm_st *fsm) {
int ret;
switch (fsm->state) {
case STATE_R:
fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
if (fsm->len == 0) fsm->state = STATE_T; // 读完切换到结束态
else if (fsm->state < 0) {
if (errno == EAGAIN) fsm->state = STATE_R; // 假错接着读
else {
fsm->state = STATE_EX; // 真错切换到异常态
fsm->errstr = "read()";
}
} else {
fsm->pos = 0;
fsm->state = STATE_W; // 读成功切换为写态
}
break;
case STATE_W:
ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
if (ret < 0) {
if (errno == EAGAIN) fsm->state = STATE_W;
else {
fsm->errstr = "write()";
fsm->state = STATE_EX;
}
} else {
fsm->pos += ret;
fsm->len -= ret;
fsm->count += ret;
if (ret == 0) fsm->state = STATE_R;
else fsm->state = STATE_W;
}
break;
case STATE_EX:
perror(fsm->errstr);
fsm->state = STATE_T;
break;
case STATE_T:
break;
default:
abort();
}
}
static void * thr_relayer(void *p) {
int i;
while (1) {
pthread_mutex_lock(&mut_rel_job);
for (i = 0; i < REL_JOBMAX; i++) {
if (rel_job[i] != NULL && rel_job[i]->job_state == STATE_RUNNING) {
fsm_driver(&rel_job[i]->fsm12);
fsm_driver(&rel_job[i]->fsm21);
if (rel_job[i]->fsm12.state == STATE_T && rel_job[i]->fsm21.state == STATE_T) {
rel_job[i]->job_state =STATE_OVER;
rel_job[i]->end = time(NULL);
}
}
}
pthread_mutex_unlock(&mut_rel_job);
}
}
static void module_unload() {
int i;
pthread_cancel(tid_relayer);
pthread_join(tid_relayer, NULL);
pthread_mutex_lock(&mut_rel_job);
for (i = 0; i < REL_JOBMAX; i++) {
if (rel_job[i] != NULL) {
fcntl(rel_job[i]->fd1, F_SETFL, rel_job[i]->fd1_save);
fcntl(rel_job[i]->fd2, F_SETFL, rel_job[i]->fd2_save);
free(rel_job[i]);
}
}
pthread_mutex_unlock(&mut_rel_job);
}
/**
* 该函数用来创建一个线程,持续的推状态机
*/
static void module_load(void) {
int err;
err = pthread_create(&tid_relayer, NULL, thr_relayer, NULL);
if (err < 0) {
fprintf(stderr, "pthread_create():%s", strerror(err));
exit(1);
}
atexit(module_unload);
}
int rel_addjob(int fd1, int fd2) {
struct rel_job_st *me;
int pos;
pthread_once(&init_once, module_load);
me = malloc(sizeof (*me));
if (me == NULL) return -ENOMEM;
//初始化任务
me->fd1 = fd1;
me->fd2 = fd2;
me->job_state = STATE_RUNNING;
me->start = time(NULL);
//保证fd是以非阻塞的方式打开的
me->fd1_save = fcntl(me->fd1, F_GETFL);
fcntl(me->fd1, F_SETFL, me->fd1_save | O_NONBLOCK);
me->fd2_save = fcntl(me->fd2, F_GETFL);
fcntl(me->fd2, F_SETFL, me->fd2_save | O_NONBLOCK);
//初始化状态机
me->fsm12.sfd = fd1;
me->fsm12.dfd = fd2;
me->fsm12.state = STATE_R;
me->fsm21.sfd = fd2;
me->fsm21.dfd = fd1;
me->fsm21.state = STATE_R;
//在任务池中找位置
pthread_mutex_lock(&mut_rel_job);
pos = get_free_pos_unlocked();
if (pos == -1) {
pthread_mutex_unlock(&mut_rel_job);
fcntl(me->fd1, F_SETFL, me->fd1_save);
fcntl(me->fd2, F_SETFL, me->fd2_save);
free(me);
return -ENOSPC;
}
rel_job[pos] = me;
pthread_mutex_unlock(&mut_rel_job);
return pos;
}
/**
* 取消任务
* @param id
* @return =0 表示指定任务已经成功取;-EINVAL 表示失败,参数非法; -EBUSY 失败,任务被重复取消
*/
int rel_canceljob(int id) {
if (id < 0 || id >= REL_JOBMAX) return -EINVAL;
pthread_mutex_lock(&mut_rel_job);
if (rel_job[id] == NULL) {
pthread_mutex_unlock(&mut_rel_job);
return -EINVAL;
}
rel_job[id]->job_state = STATE_CANCELED;
rel_job[id]->end = time(NULL);
pthread_mutex_unlock(&mut_rel_job);
}
/**
* 给任务收尸
* @param id 任务id
* @param old 被收尸任务的状态
* @return =0 成功,指定任务已终止并返回状态; -EINVAL 失败,参数非法
*/
int rel_waitjob(int id, struct rel_stat_st *old) {
pthread_mutex_lock(&mut_rel_job);
old->start = rel_job[id]->start;
old->end = rel_job[id]->end;
old->state = rel_job[id]->job_state;
old->count12 = rel_job[id]->fsm12.count;
old->count21 = rel_job[id]->fsm21.count;
old->fd1 = rel_job[id]->fd1;
old->fd2 = rel_job[id]->fd2;
pthread_mutex_unlock(&mut_rel_job);
}
/**
* 返回id任务的状态
* @param id 任务id
* @param stat 任务状态
* @return 0 成功,指定任务状态已返回; -EINVAL 失败,参数非法
*/
int rel_statjob(int id, struct rel_stat_st *stat) {
pthread_mutex_lock(&mut_rel_job);
stat->start = rel_job[id]->start;
stat->end = rel_job[id]->end;
stat->state = rel_job[id]->job_state;
stat->count12 = rel_job[id]->fsm12.count;
stat->count21 = rel_job[id]->fsm21.count;
stat->fd1 = rel_job[id]->fd1;
stat->fd2 = rel_job[id]->fd2;
pthread_mutex_unlock(&mut_rel_job);
}
2 IO多路复用
2.1 select
基本概念
- select定义——以事件为单位组织文件描述符
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
-
参数含义
nfds为所监视文件描述符中最大的那个+1
readfds为关心读状态的文件描述符集合
writefds为关心写状态的文件描述符集合
exceptfds为关心异常的文件描述符集合
timeout为超时设置,无则死等到感兴趣的时间发生,为阻塞的
-
返回值
成功返回三个文件描述符集合中的总文件描述符数,同时将结果存放到三个集合中,超时返回0,失败返回-1并设置errno同时清空这三个集合
-
timeval结构体
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
- 文件描述符处理函数
void FD_CLR(int fd, fd_set *set);//从set中删除fd
int FD_ISSET(int fd, fd_set *set);//判断fd是否在set中
void FD_SET(int fd, fd_set *set);//将fd放入到set中
void FD_ZERO(fd_set *set);//清空文件描述符
select重构中继函数
select使用步骤:
- 构建监视任务,即fd_set集合
- 调用select进行监视
- 监视结果处理
static void relay(int fd1, int fd2) {
int fd1_save, fd2_save;
struct fsm_st fsm12, fsm21;
fd_set rset, wset;
// 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
fd1_save = fcntl(fd1, F_GETFL);
fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
fd2_save = fcntl(fd2, F_GETFL);
fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
// 状态机初始化
fsm12.state = STATE_R;
fsm12.sfd = fd1;
fsm12.dfd = fd2;
fsm21.state = STATE_R;
fsm21.sfd = fd2;
fsm21.dfd = fd1;
while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
//监视任务
FD_ZERO(&rset);
FD_ZERO(&wset);
if (fsm12.state == STATE_R) {
FD_SET(fsm12.sfd, &rset);
}
if (fsm12.state == STATE_W) {
FD_SET(fsm12.dfd, &wset);
}
if (fsm21.state == STATE_R) {
FD_SET(fsm21.sfd, &rset);
}
if (fsm21.state == STATE_W) {
FD_SET(fsm21.dfd, &wset);
}
//监视
if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
if (select(max(fd1, fd2) + 1, &rset, &wset, NULL, NULL) < 0) {
if (errno == EINTR) continue;
perror("select()");
exit(1);
}
}
//查看监视结果
if (FD_ISSET(fd1, &rset) || FD_ISSET(fd2, &wset) || fsm12.state > STATE_AUTO) {
fsm_driver(&fsm12);
}
if (FD_ISSET(fd2, &rset) || FD_ISSET(fd1, &wset) || fsm21.state > STATE_AUTO) {
fsm_driver(&fsm21);
}
}
//恢复文件描述符之前的状态
fcntl(fd1, F_SETFL, fd1_save);
fcntl(fd2, F_SETFL, fd2_save);
}
select特点
优点:
- 可移植性好
缺点
- 监视现场和监视结果放在同一块空间,即结果会覆盖监视现场
- nfds存在溢出的风险
- 监视的事件过于单一
2.2 poll
基本概念
- 定义——以文件描述符为单位组织事件
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
-
参数定义
fds:监视的pollfd数组
nfds:fds数组的大小
timeout:超时时间,毫秒为单位,0表示非阻塞,-1表示阻塞
-
返回值
成功返回一个正数表示有多少事件发生,失败返回-1并设置errno
-
pollfd结构体
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events 关心的事件*/
short revents; /* returned events 发生的事件*/
}
- events/revents是一个位图
POLLIN There is data to read.
POLLPRI
There is some exceptional condition on the file descriptor. Possibilities include:
The bits that may be set/returned in events and revents are defined in <poll.h>:
POLLIN There is data to read.
POLLPRI
POLLPRI
There is some exceptional condition on the file descriptor. Possibilities include:
* There is out-of-band data on a TCP socket (see tcp(7)).
* A pseudoterminal master in packet mode has seen a state change on the slave (see ioctl_tty(2)).
* A cgroup.events file has been modified (see cgroups(7)).
POLLOUT
Writing is now possible, though a write larger that the available space in a socket or pipe will still block (unless O_NONBLOCK is set).
POLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. The _GNU_SOURCE feature test macro must be defined (before including any header files) in order to
obtain this definition.
POLLERR
Error condition (only returned in revents; ignored in events). This bit is also set for a file descriptor referring to the write end of a pipe when the read end has been closed.
POLLHUP
Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its
end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.
POLLNVAL
Invalid request: fd not open (only returned in revents; ignored in events).
When compiling with _XOPEN_SOURCE defined, one also has the following, which convey no further information beyond the bits listed above:
POLLRDNORM
Equivalent to POLLIN.
POLLRDBAND
Priority band data can be read (generally unused on Linux).
POLLWRNORM
Equivalent to POLLOUT.
POLLWRBAND
Priority data may be written.
poll重构中继函数
static void relay(int fd1, int fd2) {
int fd1_save, fd2_save;
struct fsm_st fsm12, fsm21;
struct pollfd pfd[2];
// 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
fd1_save = fcntl(fd1, F_GETFL);
fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
fd2_save = fcntl(fd2, F_GETFL);
fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
// 状态机初始化
fsm12.state = STATE_R;
fsm12.sfd = fd1;
fsm12.dfd = fd2;
fsm21.state = STATE_R;
fsm21.sfd = fd2;
fsm21.dfd = fd1;
pfd[0].fd = fd1;
pfd[1].fd = fd2;
while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
//布置监视任务
pfd[0].events = 0;
pfd[1].events = 0;
if (fsm12.state == STATE_R) {
pfd[0].events |= POLLIN;
}
if (fsm12.state == STATE_W) {
pfd[1].events |= POLLOUT;
}
if (fsm21.state == STATE_R) {
pfd[1].events |= POLLIN;
}
if (fsm21.state == STATE_W) {
pfd[0].events |= POLLOUT;
}
//监视
if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
while (poll(pfd, 2, -1) < 0) {
if (errno == EINTR) continue;
perror("select()");
exit(1);
}
}
//查看监视结果
if ((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLOUT) || fsm12.state > STATE_AUTO) {
fsm_driver(&fsm12);
}
if ((pfd[1].revents & POLLIN) || (pfd[0].revents & POLLOUT) || fsm21.state > STATE_AUTO) {
fsm_driver(&fsm21);
}
}
//恢复文件描述符之前的状态
fcntl(fd1, F_SETFL, fd1_save);
fcntl(fd2, F_SETFL, fd2_save);
}
特点
- 可移植
2.3 epoll
定义
- epoll_create()创建一个epoll实例,成功返回一个fd(epoll实例可以看做一个文件,所以得注意使用完后调用close关闭),失败返回-1并设置errno
int epoll_create(int size);//size随便取值即可
- epoll_ctl() 控制一个epoll实例,对epfd实例中的fd文件描述符执行op操作某行为event
//epfd即epoll实例对应的fd
//op进行什么样的操作
//fd操作的对象
//event即针对的事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- epoll_wait() 往外取正在发生的事件,从epfd实例中去maxevents个事件到events数组中去,timeout为超时时间
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
- 相关结构体
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events 感兴趣的事件,是一个位图和poll中类似*/
epoll_data_t data; /* User data variable */
};
epoll重构中继函数
static void relay(int fd1, int fd2) {
int fd1_save, fd2_save;
struct fsm_st fsm12, fsm21;
int epfd;
struct epoll_event ev;
// 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
fd1_save = fcntl(fd1, F_GETFL);
fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
fd2_save = fcntl(fd2, F_GETFL);
fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
// 状态机初始化
fsm12.state = STATE_R;
fsm12.sfd = fd1;
fsm12.dfd = fd2;
fsm21.state = STATE_R;
fsm21.sfd = fd2;
fsm21.dfd = fd1;
epfd = epoll_create(2);
if (epfd < 0) {
perror("epoll_create()");
exit(1);
}
ev.events = 0;
ev.data.fd = fd1;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &ev);
ev.events = 0;
ev.data.fd = fd2;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &ev);
while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
//布置监视任务
ev.events = 0;
ev.data.fd = fd1;
if (fsm12.state == STATE_R) {
ev.events |= EPOLLIN;
}
if (fsm21.state == STATE_W) {
ev.events |= EPOLLOUT;
}
epoll_ctl(epfd, EPOLL_CTL_MOD, fd1, &ev);
ev.events = 0;
ev.data.fd = fd2;
if (fsm12.state == STATE_W) {
ev.events |= EPOLLOUT;
}
if (fsm21.state == STATE_R) {
ev.events |= EPOLLIN;
}
epoll_ctl(epfd, EPOLL_CTL_MOD, fd2, &ev);
//监视
if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
while (epoll_wait(epfd, &ev, 1, -1) < 0) {
if (errno == EINTR) continue;
perror("epoll_wait()");
exit(1);
}
}
//查看监视结果
if ((ev.data.fd = fd1 && ev.events & EPOLLIN) || (ev.data.fd == fd2 && ev.events & EPOLLOUT) || fsm12.state > STATE_AUTO) {
fsm_driver(&fsm12);
}
if ((ev.data.fd = fd1 && ev.events & EPOLLOUT) || (ev.data.fd == fd2 && ev.events & EPOLLIN) || fsm21.state > STATE_AUTO) {
fsm_driver(&fsm21);
}
}
//恢复文件描述符之前的状态
fcntl(fd1, F_SETFL, fd1_save);
fcntl(fd2, F_SETFL, fd2_save);
close(epfd);
}
3 其他读写函数
相关函数
向多个内存空间(iovcnt个iovec)中写数据
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
iovec结构体
struct iovec {
void *iov_base; /* Starting address 起始地址*/
size_t iov_len; /* Number of bytes to transfer 空间大小*/
};
4 存储映射IO
mmap函数
- 定义 将内核中的一块内存区域和用户进程空间的内存做一个映射后返回一个指针用于操作这块数据
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);//定义映射
int munmap(void *addr, size_t length); //解除映射
- 参数解析
- addr 内存映射地址的起始地址
- length即映射内存大小
- prot即这块内存的属性,即定义自己的操作权限
- flags特殊要求
- fd映射的文件
- offset映射文件的偏移量
- 返回值:成功返回映射地址的起始地址,失败返回MAP_FAILED,并设置errno
函数示例:计算文件中字符’a’的数目
int main(int argc, char **argv) {
struct stat fst;
int fd, i, cnt;
char *p;
if (argc < 2) {
fprintf(stderr, "Usage...\n");
exit(1);
}
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open()");
exit(1);
}
if (fstat(fd, &fst) < 0) {
perror("fstat()");
exit(1);
}
p = mmap(NULL, fst.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
perror("mmap()");
exit(1);
}
close(fd);
for (i = 0; i < fst.st_size; i++) {
if (p[i] == 'a') cnt++;
}
printf("%d\n", cnt);
munmap(p, fst.st_size);
exit(1);
}
5 文件锁
lockf()
- 定义
int lockf(int fd, int cmd, off_t len);
-
cmd的用法
F_LOCK:加锁
F_TLOCK:尝试加锁立即返回
F_ULOCK:解锁
F_TEST:测试是否有锁
-
len的含义
0表示文件有多长锁多长
flock()
- 定义 给一个文件加锁或解锁
int flock(int fd, int operation);
- operation:
- LOCK_SH共享锁
- LOCK_EX独占锁
- LOCK_UN解除锁定
===