file_ops 中 最重要的概念莫过于 同步异步,阻塞非阻塞 概念及其实现.
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由*调用者*主动等待这个*调用*的结果。
而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。
---
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
其实如果是循环的只等待消息结果,就跟调度出去(挂起)没什么两样.也被称作阻塞.(私人理解)
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
同步,
只有ops->read直接监听的,都是同步
异步
不只有 ops->read 直接监听的,或者根本没有 ops_>read 参与的,都是异步
阻塞,
进程会换出,进程会循环卡死到某处
非阻塞
进程不会因为api 被换出
同步非阻塞
read NON_BLOCK
同步阻塞
read BLOCK
异步阻塞
select,poll方案(阻塞) (异步通知方案)加 read NON_BLOCK ,并不是直接read监听的,都是异步非阻塞
异步非阻塞
epoll 方案
epoll 方案是将poll 方案针对 socket 进行的优化,一般用于socket fd 的监听
fasync (非阻塞) 加 read NON_BLOCK(在信号中)
aio 方案
aio_read aio_error (循环检测,阻塞,没有换出) , aio_return
aio_read aio_suspend (阻塞,换出) , aio_return
填充 aio_sigevent (注册信号),aio_read , 有信号来时,执行信号函数(在函数中,aio_error,aio_return )
注意: aio_return 为 非阻塞函数.
阻塞
fifo is empty
if(file->f_flags & O_NONBLOCK)
return _EAGAIN;
wait_event_interruptible(read_queue)
wake_up(read_queue);
非阻塞
fifo is empty
if(file->f_flags & O_NONBLOCK)
return _EAGAIN;
多路复用
poll
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
poll
sys_poll
do_sys_poll
do_poll
for(;;)
do_pollfd(pfd, pt)
file->f_op->poll(file, pwait);
schedule_timeout(__timeout);
除了休眠到指定时间被系统唤醒外,还可以被驱动程序唤醒──记住这点,这就是为什么驱动的poll里要调用poll_wait的原因
task 1
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(w_wait, wait);
__set_current_state(TASK_INTERRUPTIBLE);
schedule();
................
task 2
wake_up(w_wait);
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
经过以上驱动程序的poll()函数应该返回设备资源的可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位"或"结果.每个宏的含义都表示设备的一种状态,如:
常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, int, timeout_msecs)
do_sys_poll(ufds, nfds, to);
do_poll(nfds, head, &table, end_time);
poll_table* pt = &wait->pt;
for (;;) {
for (; pfd != pfd_end; pfd++) {
do_pollfd(pfd, pt, &can_busy_loop, busy_flag);
}
poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack);
}
__put_user(fds[j].revents, &ufds->revents)
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait,
pwait->_key = pollfd->events|POLLERR|POLLHUP;
mask = f.file->f_op->poll(f.file, pwait);
mask &= pollfd->events | POLLERR | POLLHUP;
pollfd->revents = mask;
#define FD_ISSET(fd, fdsetp) __FD_ISSET (fd, fdsetp)
#define __FD_ISSET(d, s) \
((__FDS_BITS (s)[__FD_ELT (d)] & __FD_MASK (d)) != 0)
如果当前不可读(先调用驱动.poll确定是否可读,然后继续do_poll),那么在sys_poll->do_poll中当前进程就会睡眠在等待队列上,这个等待队列是由驱动程序提供的(就是poll_wait中传入的那个)。当可读的时候,驱动程序可能有一部分代码运行了(比如驱动的中断服务
程序),那么在这部分代码中,就会唤醒等待队列上的进程,也就是之前睡眠的那个,当那个进程被唤醒后do_poll会再一次的调用驱动程序的poll函数,这个时候应用程序就知道是可读的了。
异步IO fasync
handler
main
act.sa_sigaction = handler;
sigaction(SIGIO,&act,&oldact)
open
fcntl()
fcntk()
fcntl()
fcntl()
while(1)
---
fops
.fasync = demo_fasyc;
demo_fasyc
fasyc_helper(fd,file,on,xxx);
write
kill_fasync(xxx,SIGIO,POLLIN);
aio 方案
libc
线程和阻塞IO模拟
#include
linux
内核fops->aio_read 实现
.aio_read = pipe_read
#include
libeio
类似libc方案
#include "eio.h"
异步IO aio_read
https://blog.csdn.net/summer_zgh/article/details/82416427
http://blog.sina.com.cn/s/blog_6028e2630100y0d1.html
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
io_context_t ctx;
unsigned nr_events = 10;
memset(&ctx, 0, sizeof(ctx));
int errcode = io_setup(nr_events, &ctx);
if (errcode == 0)
printf("io_setup success\n");
else
printf("io_setup error: :%d:%s\n", errcode, strerror(-errcode));
int fd = open("./direct.txt", O_CREAT|O_DIRECT|O_WRONLY, S_IRWXU|S_IRWXG|S_IROTH);
printf("open: %s\n", strerror(errno));
char* buf;
errcode = posix_memalign((void**)&buf, sysconf(_SC_PAGESIZE), sysconf(_SC_PAGESIZE));
printf("posix_memalign: %s\n", strerror(errcode));
strcpy(buf, "hello xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
struct iocb *iocbpp = (struct iocb *)malloc(sizeof(struct iocb));
memset(iocbpp, 0, sizeof(struct iocb));
iocbpp[0].data = buf;
iocbpp[0].aio_lio_opcode = IO_CMD_PWRITE;
iocbpp[0].aio_reqprio = 0;
iocbpp[0].aio_fildes = fd;
iocbpp[0].u.c.buf = buf;
iocbpp[0].u.c.nbytes = page_size;
iocbpp[0].u.c.offset = 0;
int n = io_submit(ctx, 1, &iocbpp);
printf("==io_submit==: %d:%s\n", n, strerror(-n));
struct io_event events[10];
struct timespec timeout = {1, 100};
n = io_getevents(ctx, 1, 10, events, &timeout);
printf("io_getevents: %d:%s\n", n, strerror(-n));
close(fd);
io_destroy(ctx);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 1025
int main(int argc,char **argv)
{
struct aiocb wr;
int ret,fd;
char str[20] = {"hello,world"};
bzero(&wr,sizeof(wr));
fd = open("test.txt",O_WRONLY | O_APPEND);
if(fd < 0)
{
perror("test.txt");
}
wr.aio_buf = (char *)malloc(BUFFER_SIZE);
if(wr.aio_buf == NULL)
{
perror("buf");
}
wr.aio_buf = str;
wr.aio_fildes = fd;
wr.aio_nbytes = 1024;
ret = aio_write(&wr);
if(ret < 0)
{
perror("aio_write");
}
while(aio_error(&wr) == EINPROGRESS)
{
printf("hello,world\n");
}
ret = aio_return(&wr);
printf("\n\n\n返回值为:%d\n",ret);
return 0;
}
https://blog.csdn.net/lanyan822/article/details/7644745
linux下主要有两套异步IO,一套是由glibc实现的(以下称之为glibc版本)、一套是由linux内核实现,并由libaio来封装调用接口(以下称之为linux版本)
libeio 是 后期 开发的 , 类似 glibc 版本,相比 glibc 的aio 来说bug 比较少,而且架构清晰
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "eio.h"
int respipe [2];
void
want_poll (void)
{
char dummy;
printf ("want_poll ()\n");
write (respipe [1], &dummy, 1);
}
void
done_poll (void)
{
char dummy;
printf ("done_poll ()\n");
read (respipe [0], &dummy, 1);
}
void
event_loop (void)
{
struct pollfd pfd;
pfd.fd = respipe [0];
pfd.events = POLLIN;
printf ("\nentering event loop\n");
while (eio_nreqs ())
{
poll (&pfd, 1, -1);
printf ("eio_poll () = %d\n", eio_poll ());
}
printf ("leaving event loop\n");
}
int
res_cb (eio_req *req)
{
printf ("res_cb(%d|%s) = %d\n", req->type, req->data ? req->data : "?", EIO_RESULT (req));
if (req->result < 0)
abort ();
return 0;
}
int
main (void)
{
printf ("pipe ()\n");
if (pipe (respipe))
abort ();
printf ("eio_init ()\n");
if (eio_init (want_poll, done_poll))
abort ();
eio_mkdir ("eio-test-dir", 0777, 0, res_cb, "mkdir");
event_loop ();
return 0;
}