同步IO分为阻塞IO、非阻塞IO、信号驱动的IO和多路转接IO。
异步IO :能够在较短时间内从多个接收通道收集大量的数据,异步IO操作在IO期间不阻塞发出IO请求的进程,其IO操作由操作系统派生新的线程并行的执行,当IO操作结束是发送信号给发送请求IO操作的进程通知其IO操作完成,进程也可以调用aio_suspend阻塞进程等待IO操作完成。
阻塞IO:
一直阻塞进程直到完成IO操作。
非阻塞IO:
有数据时进行IO操作,没有数据时立即返回不阻塞进程。
信号驱动IO:
当有数据到来是发送信号给进程执行IO操作,提高CPU的利用率。当设置打开文件描述字O_ASYNC标志是可以用于信号驱动的IO操作,也可以用过fcntl()改变文件标签,当文件进行IO操作时会产生SIGIO信号或套接字有带外数据到来时会产生SIGURG信号。通过fcntl函数执行F_SETOWN命令可以设置/获得,接收信号的进程/进程组ID。
具体实例:
/*
* main.c
*
* Created on: 2016年10月22日
* Author: chy
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <termios.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <stdlib.h>
FILE *file;
void sigfunc(int sig)
{
char c,input[128];
int n;
static int i = 0;
if(read(STDIN_FILENO,&c,1) > 0){
if(c != '\n')
input[i++] = c;
else {
input[i++] = '\0';
fprintf(file,"NO sig=%d, input line is %s\n",i,input);
i = 0;
if(c == 'q'){
fclose(file);
_exit(0);
}
}
}
}
int main(int argc,char *argv)
{
int falg;
char buff[256];
struct termios newseting,old_termios;
file = fopen("test.txt","w");
signal(SIGIO,sigfunc);
tcgetattr(STDIN_FILENO,&old_termios);
newseting = old_termios;
newseting.c_iflag &= (~ICANON);
newseting.c_cc[VTIME] = 0;
newseting.c_cc[VMIN] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &newseting);
fcntl(STDIN_FILENO, F_SETOWN,getpid());
falg = fcntl(STDIN_FILENO, F_GETFL,0);
falg |= O_ASYNC;
fcntl(STDIN_FILENO,F_SETFL,falg);
while(1) sleep(1);
return 0;
}
多路转接IO:
处理来自多个通道的输入,通过select函数告诉调用它的进程需要等待IO事件的数量。
#include <sys/select.h>
#include <sys/time.h>
int select(int nfds,fd_set *rfds,fd_set *wfds,fd_set *edfs,struct timeval *timeout);
void FD_ZERO(fd_set *fdset); //初始化描述字为空
void FD_CLR(int filedes,fd_set *fdset); //将filedes描述字从fdset描述字集合中清除
int FD_ISSET(int fileds,fd_set *fdset); //判断filedes是否属于fdset所指的描述
void FD_SET(int filedes,fd_set *fdset); //将filedes添加到描述字fset所指的集合中
select中的三个测试描述字集合都为空可以做为定时器切精度高于sleep。
异步IO :能够在较短时间内从多个接收通道收集大量的数据,异步IO操作在IO期间不阻塞发出IO请求的进程,其IO操作由操作系统派生新的线程并行的执行,当IO操作结束是发送信号给发送请求IO操作的进程通知其IO操作完成,进程也可以调用aio_suspend阻塞进程等待IO操作完成。
#include <aio.h>
int aio_write(struct aiocb *aiocbp);//返回实际写入的字节数
int aio_read(struct aiocb *aiocbp); //返回实际读出的字节数
int lio_listio(int mode,struct aiocb *restrict const list[restrict],int nent,struct sigevent *restrict notification);
int aio_error(struct aiocb *aiocbp); //正在进行IO操作返回EINPROGRESS
ssize_t aio_return(struct aiocb *aiocbp); //返回读写字节个数
int aio_suspend(const struct aiocb *const list[],int nent,const struct timespec *timeout); //挂起调用进程直到IO完成或时间到期
int aio_sysnc(int op,struct aiocb *aiocbp); //把数据同步到物理磁盘
struct aiocb{
int aio_filds;
off_t aio_offset;
volatile void *aio_buf;
size_t aio_nbytes;
int aio_reqprio;
struct sigevent aio_sigevent;
int aio_lio_opcode;
};
struct sigevent{
union sigval sigev_value;
int sigv_signo;
int sigev_notify;//异步事件的通知类型 SIGEV_NONE(不通知)、SIGEV_SIGNAL(生成信号)、SIGEV_THREAD(执行sigev_notify_function指定的函数)
void (*sigev_notify_function) (union sigval);
pthread_attr_t *sigev_notify_attrbutes;
};
具体实例:
/*
* main.c
*
* Created on: 2016年10月20日
* Author: chy
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <aio.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define buffer_num 2
#define buffer_size 2048
#define ERR(msg,f_num) { \
if(f_num < 0) { \
fprintf(stderr,"%s",msg); \
exit(-1); \
} \
}
typedef enum{
buffer_ferr = 1,
buffer_full,
buffer_write
}BUFFER;
typedef struct { //缓冲区结构
BUFFER state;
int fillpt;
char buffer[buffer_size];
struct aiocb aio;
}buffer_t;
static buffer_t buffer[buffer_num];
static sigset_t procmask;
static int write_num = 0;
static int sig_num;
static volatile off_t seek_ptr;
void sig_func(int signo,siginfo_t *info,void *sig)
{
int i;
buffer_t *temp;
if(info->si_signo != SIGRTMIN || info->si_code != SI_ASYNCIO)
return;
else
printf("write over\n");
temp = (buffer_t*)info->si_value.sival_ptr;
int write_temp_num = 0;
if(aio_error(&temp->aio) != EINPROGRESS)
write_temp_num = aio_return(&temp->aio);
write_num += write_temp_num;
sig_num++;
temp->fillpt = 0;
temp->state = buffer_ferr;
return;
}
buffer_t *find_empty_buffer()
{
int i;
sigset_t newsig;
sigprocmask(SIG_BLOCK,&procmask,&newsig); //屏蔽异步写信号量
while(1){
for(i = 0; i < buffer_num; i++)
if(buffer[i].state == buffer_ferr)
break;
if(i == buffer_num)
sigsuspend(&procmask); //挂起SIGRTMIN
else break;
}
buffer[i].state = buffer_full;
buffer[i].fillpt = 0;
sigprocmask(SIG_SETMASK,&procmask,NULL); //恢复屏蔽异步写信号量
return (&buffer[i]);
}
void buffer_flush(buffer_t *temp)
{
temp->aio.aio_offset = seek_ptr; //文件指针的位置
seek_ptr += temp->fillpt;
temp->aio.aio_buf = temp->buffer; //缓冲地地址
temp->aio.aio_nbytes = temp->fillpt; //要传输的字节数
temp->aio.aio_reqprio = 0; //优先移位0
temp->aio.aio_sigevent.sigev_notify = SIGEV_SIGNAL; //实时信号类型
temp->aio.aio_sigevent.sigev_signo = SIGRTMIN; //信号数
temp->aio.aio_sigevent.sigev_value.sival_ptr = temp; //携带的信息
temp->state = buffer_write; //标记为正在写的状态
ERR("write fail\n",aio_write(&temp->aio));
return;
}
int main(int argc,char *argv[])
{
int i,file_in,file_out;
struct sigaction sig;
buffer_t *buffer_opt;
if(argc < 3){
printf("please input three func\n");
exit(0);
}
file_in = open(argv[1],O_RDONLY);
ERR("open file_in faile\n",file_in);
file_out = open(argv[2],O_WRONLY | O_CREAT,0777);
ERR("open file_out faile\n",file_out);
for(i = 0; i < buffer_num; i++){
buffer[i].state = buffer_ferr;
buffer[i].fillpt = 0;
buffer[i].aio.aio_fildes = file_out; //绑定文件描述字
}
buffer_opt = find_empty_buffer();
sigemptyset(&procmask);
sigaddset(&procmask,SIGRTMIN);
sigemptyset(&sig.sa_mask);
sig.sa_flags = SA_SIGINFO;
sig.sa_sigaction = sig_func;
ERR("sigaction fail\n",sigaction(SIGRTMIN,&sig,NULL));
int read_num;
while(1){
while((read_num = read(file_in,buffer_opt->buffer + buffer_opt->fillpt,buffer_size)) != 0){
if(read_num > 0){
buffer_opt->fillpt += read_num;
if(buffer_opt->fillpt == buffer_size){
buffer_flush(buffer_opt);
buffer_opt = find_empty_buffer();
break;
}
}
if(errno == EINTR)
break;
else if(read_num < 0 && errno != EINTR)
ERR("read faile\n",-1);
}
if(read_num == 0){
printf("chen\n");
buffer_flush(buffer_opt);
break;
}
}
for(i = 0; i < buffer_num; i++)
if(buffer[i].state == buffer_write){
struct aiocb *temp[1];
temp[0] = &buffer[i].aio;
aio_suspend(temp, 1, NULL);
}
close(file_out);
close(file_in);
printf("this is over\n");
return 1;
}
存储映射IO:传统IO进行读写文件要进行多次系统调用,并且把文件加载到自己的地址空间,其效率低,浪费存储空间。存储映射IO,系统把文件的一页读取到内存,每一个进程把改页映射到自己的内存空间。映射后不再需要read等系统调用,可以通过指令直接访问。存储映射分为共享映射和私有映射,共享映射每个进程都能够改变映射页的内容,当一页被刷新时保回会物理磁盘。私有映射,写文件将导致复制该页的一个副本,物理磁盘物件本身不改变。
#include <sys/mman.h>
void *mmap(void *addr,size_t len,int port,int flags,int filedes,off_t off);
int munmap(void *paddr,size_t len); //删除映射
int msync(void *addr,size_t len,int flags); //写入到物理磁盘
addr:映射区在内存的起始地址。
len:映射的字节数。
prot:映射区的保护权限。
flags:映射区的属性。
filedes: 已打开文件的描述字。
off:文件要映射的其实字节位置。