Linux IO模型:阻塞IO、非阻塞IO、IO多路复用、信号驱动IO、异步IO
·
1. 阻塞IO
最简单直接的IO模型。
调用过程:
进程调用recvfrom系统调用,进程阻塞;等待数据,数据准备完成,
将数据从内核拷贝到用户空间,系统调用返回;进程继续处理。
优点是简单直接,缺点是进程阻塞,浪费资源。
2. 非阻塞IO
非阻塞IO采用轮询的方式隔一段时间发起系统调用看数据是否就绪,
与阻塞IO不同的是它会马上返回结果。
如果数据准备就绪,就将数据拷贝到用户空间,返回成功。
否则kernal立即返回EWOUDBLOCK这个错误。
因此在准备数据阶段,不会阻塞进程。
优点:进程发起I/O操作时,不会因为数据未就绪而阻塞。
缺点:会多次调用系统调用,消耗CPU。

3. I/O多路复用
I/O多路复用可以理解为,单线程监听多个文件描述符。
I/O多路利用向kernel发起系统调用(select、poll、epoll),
传入文件描述符和相应的动作read、write让kernel去监测,当
其中有文件描述有相应的动作发生,就返回结果。进程再
发起真正的I/O操作。

4. 信号驱动IO
通过信号来通知数据是否到来,
因此在等待数据到来这个阶段进程不会阻塞。
在信号触发后,调用相应的信号处理函数,开始真正的I/O操作:
将数据从内核拷贝到用户空间,这时候进程会阻塞。

下面是找得一段例子,sigaction对信号绑定相应的处理函数,相应的处理
函数就是sa.sa_handler。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <linux/if_tun.h>
extern int errno;
int bridge_term = 0;
int f1, f2;
void sig_io(int sig)
{
static char buf[1600];
register int r;
while( (r=read(f1, buf, sizeof(buf))) > 0 )
write(f2, buf, r);
if( r < 0 && (errno != EAGAIN && errno != EINTR) ) {
bridge_term = 1;
return;
}
while( (r=read(f2, buf, sizeof(buf))) > 0 )
write(f1, buf, r);
if( r < 0 && (errno != EAGAIN && errno != EINTR) ) {
bridge_term = 1;
return;
}
}
int main(int argc, char *argv[])
{
struct sigaction sa;
char buf[20];
if(argc < 2) {
printf("Usage: bridge tap|tun\n");
exit(1);
}
sprintf(buf,"/dev/%s%d",argv[1],0);
f1 = open(buf, O_RDWR);
sprintf(buf,"/dev/%s%d",argv[1],1);
f2 = open(buf, O_RDWR);
ioctl(f1, TUNSETNOCSUM, 1);
ioctl(f2, TUNSETNOCSUM, 1);
fcntl(f1, F_SETFL, O_NONBLOCK | O_ASYNC);
fcntl(f2, F_SETFL, O_NONBLOCK | O_ASYNC);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_io;
sigaction(SIGIO, &sa, NULL);
while( !bridge_term )
sleep(1000);
}
5. 异步I/O
在异步I/O中,I/O操作不会引起进程的阻塞。等待数据和拷贝数据都发生在了内核之中。数据就绪后,发送一个signal到进程中去,进程再进行处理。

下面给出posix aio的一个例子
先给出aiocb的结构体定义
struct aiocb
{
int aio_fildes; /* File descriptor. */
int aio_lio_opcode; /* Operation to be performed. */
int aio_reqprio; /* Request priority offset. */
volatile void *aio_buf; /* Location of buffer. */
size_t aio_nbytes; /* Length of transfer. */
struct sigevent aio_sigevent; /* Signal number and value. */
/* Internal members. */
struct aiocb *__next_prio;
int __abs_prio;
int __policy;
int __error_code;
__ssize_t __return_value;
#ifndef __USE_FILE_OFFSET64
__off_t aio_offset; /* File offset. */
char __pad[sizeof (__off64_t) - sizeof (__off_t)];
#else
__off64_t aio_offset; /* File offset. */
#endif
char __glibc_reserved[32];
};
再给出示例程序,主要是用aio从文件中进行读取。
我们可以运行
./a.out /dev/stdin /dev/stdin
来观察程序运行的结果。
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <aio.h>
#include <signal.h>
#define BUF_SIZE 20 /* Size of buffers for read operations */
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct ioRequest { /* Application-defined structure for tracking
I/O requests */
int reqNum;
int status;
struct aiocb *aiocbp;
};
static volatile sig_atomic_t gotSIGQUIT = 0;
/* On delivery of SIGQUIT, we attempt to
cancel all outstanding I/O requests */
static void /* Handler for SIGQUIT */
quitHandler(int sig)
{
gotSIGQUIT = 1;
}
#define IO_SIGNAL SIGUSR1 /* Signal used to notify I/O completion */
static void /* Handler for I/O completion signal */
aioSigHandler(int sig, siginfo_t *si, void *ucontext)
{
if (si->si_code == SI_ASYNCIO) {
write(STDOUT_FILENO, "I/O completion signal received\n", 31);
/* The corresponding ioRequest structure would be available as
struct ioRequest *ioReq = si->si_value.sival_ptr;
and the file descriptor would then be available via
ioReq->aiocbp->aio_fildes */
}
}
int
main(int argc, char *argv[])
{
struct sigaction sa;
int s;
int numReqs; /* Total number of queued I/O requests */
int openReqs; /* Number of I/O requests still in progress */
if (argc < 2) {
fprintf(stderr, "Usage: %s <pathname> <pathname>...\n",
argv[0]);
exit(EXIT_FAILURE);
}
numReqs = argc - 1;
/* Allocate our arrays. */
struct ioRequest *ioList = calloc(numReqs, sizeof(*ioList));
if (ioList == NULL)
errExit("calloc");
struct aiocb *aiocbList = calloc(numReqs, sizeof(*aiocbList));
if (aiocbList == NULL)
errExit("calloc");
/* Establish handlers for SIGQUIT and the I/O completion signal. */
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sa.sa_handler = quitHandler;
if (sigaction(SIGQUIT, &sa, NULL) == -1)
errExit("sigaction");
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sa.sa_sigaction = aioSigHandler;
if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
errExit("sigaction");
/* Open each file specified on the command line, and queue
a read request on the resulting file descriptor. */
for (size_t j = 0; j < numReqs; j++) {
ioList[j].reqNum = j;
ioList[j].status = EINPROGRESS;
ioList[j].aiocbp = &aiocbList[j];
ioList[j].aiocbp->aio_fildes = open(argv[j + 1], O_RDONLY);
if (ioList[j].aiocbp->aio_fildes == -1)
errExit("open");
printf("opened %s on descriptor %d\n", argv[j + 1],
ioList[j].aiocbp->aio_fildes);
ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
if (ioList[j].aiocbp->aio_buf == NULL)
errExit("malloc");
ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
ioList[j].aiocbp->aio_reqprio = 0;
ioList[j].aiocbp->aio_offset = 0;
ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
&ioList[j];
s = aio_read(ioList[j].aiocbp);
if (s == -1)
errExit("aio_read");
}
openReqs = numReqs;
/* Loop, monitoring status of I/O requests. */
while (openReqs > 0) {
sleep(3); /* Delay between each monitoring step */
if (gotSIGQUIT) {
/* On receipt of SIGQUIT, attempt to cancel each of the
outstanding I/O requests, and display status returned
from the cancelation requests. */
printf("got SIGQUIT; canceling I/O requests: \n");
for (size_t j = 0; j < numReqs; j++) {
if (ioList[j].status == EINPROGRESS) {
printf(" Request %zu on descriptor %d:", j,
ioList[j].aiocbp->aio_fildes);
s = aio_cancel(ioList[j].aiocbp->aio_fildes,
ioList[j].aiocbp);
if (s == AIO_CANCELED)
printf("I/O canceled\n");
else if (s == AIO_NOTCANCELED)
printf("I/O not canceled\n");
else if (s == AIO_ALLDONE)
printf("I/O all done\n");
else
perror("aio_cancel");
}
}
gotSIGQUIT = 0;
}
/* Check the status of each I/O request that is still
in progress. */
printf("aio_error():\n");
for (size_t j = 0; j < numReqs; j++) {
if (ioList[j].status == EINPROGRESS) {
printf(" for request %zu (descriptor %d): ",
j, ioList[j].aiocbp->aio_fildes);
ioList[j].status = aio_error(ioList[j].aiocbp);
switch (ioList[j].status) {
case 0:
printf("I/O succeeded\n");
break;
case EINPROGRESS:
printf("In progress\n");
break;
case ECANCELED:
printf("Canceled\n");
break;
default:
perror("aio_error");
break;
}
if (ioList[j].status != EINPROGRESS)
openReqs--;
}
}
}
printf("All I/O requests completed\n");
/* Check status return of all I/O requests. */
printf("aio_return():\n");
for (size_t j = 0; j < numReqs; j++) {
ssize_t s;
s = aio_return(ioList[j].aiocbp);
printf(" for request %zu (descriptor %d): %zd\n",
j, ioList[j].aiocbp->aio_fildes, s);
}
exit(EXIT_SUCCESS);
}
参考
更多推荐



所有评论(0)