127-POSIX 异步IO(异步通知)

到这里,终于可以采用异步的方式进行通知我们的程序异步操作完成了,在前面我们一直使用很笨很笨的方式,使用 while 循环和 aio_error 函数不断的去测试异步操作有没有完成。

1. aiocb 的成员 aio_sigevent

struct aiocb {
  // ...
  struct sigevent aio_sigevent;   /* 通知方法 */
  // ...
};

aiocb 的很多字段我们都已经学习过了,现在我们再来学习一个新的成员,aio_sigevent,它是我们的主角,这个成员的类型也是一个结构体,如下。

union sigval {          /* Data passed with notification */
  int     sival_int;         /* Integer value */
  void   *sival_ptr;         /* Pointer value */
};

struct sigevent {
  int          sigev_notify; /* 通知方式 */
  int          sigev_signo;  /* 通知所用的信号,可以自己指定,比如 SIGUSR1 */
  union sigval sigev_value;  /* 通知附带的数据 */

  // 下面两个成员仅仅用于 SIGEV_THREAD 通知方式
  void         (*sigev_notify_function) (union sigval); /* 线程通知函数 */
  void        *sigev_notify_attributes; /* 通知线程的属性,一般指定为 pthread_attr_t 结构的地址 */


  // 通知线程 id. 这个成员仅仅用于 SIGEV_THREAD_ID 通知方式,这种通知方式我们不学它。
  pid_t        sigev_notify_thread_id; 
};

2. sigevent 的成员

(1) sigev_notify

这个成员,表示异步 IO 完成后,用什么样的方式通知用户程序。它有四个可选值:

  • SIGEV_NONE:表示不通知。我们前面实验中用的所有异步请求用的都是这种。
  • SIGEV_SIGNAL:当异步 IO 操作完成后,程序会收到你指定的信号(成员 sigev_signo)。
  • SIGEV_THREAD:当异步 IO 操作完成后,内核会创建一个新线程执行一个函数,这个函数由成员 sigev_notify_function 指定。该函数的参数是成员 sigev_value 的值。
  • SIGEV_THREAD_ID:Linux 操作系统独有的。仅用于 POSIX 定时器中。这个目前我们不用管它。

(2) sigev_signo

如果使用 SIGEV_SIGNAL 方式通知,当异步 IO 操作完成后,进程会收到这个成员指定的信号。

(3) sigev_value

产生通知时,所附加的数据。这个值由你自己指定,当通知产生时,会通过带参数的信号处理函数或线程函数的参数传递过来。(具体看后面的示例)

(4) sigev_notify_function

线程函数,仅仅用于 SIGEV_THREAD 通知方式。

(5) sigev_notify_attributes

创建线程的属性,需要传入 pthread_attr_t 类型的指针。仅仅用于 SIGEV_THREAD 通知方式。

(6) sigev_notify_thread_id

仅仅用于 SIGEV_THREAD_ID 通知方式,这种通知方式我们不学它。

3. 实验

这部分有两个实验,一个用信号的方式通知,另一个是用线程的方式。

3.1 信号通知

  • 代码
// my_aio_sig.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <aio.h>
#include <strings.h>
#include <errno.h>
#include <signal.h>

#define ERR_EXIT(msg) do { perror(msg); exit(1); } while(0)
#define IO_SIGNAL SIGUSR1

// 信号处理函数
void handler(int sig, siginfo_t *info, void *ucontext) {
  int ret;
  printf("receive signal: %d\n", sig);
  struct aiocb *my_aiocb = info->si_value.sival_ptr;
  while(aio_error(my_aiocb) == EINPROGRESS) {
    write(STDOUT_FILENO, ".", 1); 
  }

  ret = aio_return(my_aiocb);
  if (ret < 0) ERR_EXIT("aio_return");

  printf("content: %s\n", (char*)(my_aiocb->aio_buf));
  exit(0);
}

int main() {
  int fd, ret;
  struct aiocb my_aiocb;
  char buf[64];

  bzero((char*)&my_aiocb, sizeof(struct aiocb));
  // 注册信号处理函数
  struct sigaction sa; 
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_SIGINFO;
  sa.sa_sigaction = handler;
  sigaction(IO_SIGNAL, &sa, NULL);

  my_aiocb.aio_buf = buf;
  my_aiocb.aio_fildes = STDIN_FILENO;
  my_aiocb.aio_nbytes = 64; 
  my_aiocb.aio_offset = 0;
  // 设置通知方式为信号通知
  my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  // 设置通知信号
  my_aiocb.aio_sigevent.sigev_signo = IO_SIGNAL;
  // 通知附加数据。这个成员将来会传递到信号处理函数中。
  my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;

  ret = aio_read(&my_aiocb);
  if (ret < 0) ERR_EXIT("aio_read");

  while(1) {
    pause();
  }

  return 0;
}
  • 编译和运行
$ gcc my_aio_sig.c -o my_aio_sig -lrt
$ ./my_aio_sig


这里写图片描述

3.2 线程通知

  • 代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <aio.h>
#include <strings.h>
#include <errno.h>
#include <signal.h>

#define ERR_EXIT(msg) do { perror(msg); exit(1); } while(0)

// 线程函数
void handler(union sigval val) {
  int ret;
  struct aiocb *my_aiocb = val.sival_ptr;
  while(aio_error(my_aiocb) == EINPROGRESS) {
    write(STDOUT_FILENO, ".", 1); 
  }

  ret = aio_return(my_aiocb);
  if (ret < 0) ERR_EXIT("aio_return");

  printf("content: %s\n", (char*)(my_aiocb->aio_buf));
  exit(0);
}

int main() {
  int fd, ret;
  struct aiocb my_aiocb;
  const struct aiocb* aio_list[1] = {&my_aiocb};
  char buf[64];

  bzero((char*)&my_aiocb, sizeof(struct aiocb));

  my_aiocb.aio_buf = buf;
  my_aiocb.aio_fildes = STDIN_FILENO;
  my_aiocb.aio_nbytes = 64; 
  my_aiocb.aio_offset = 0;
  // 通知方式设置为线程通知
  my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
  // 通知附加数据
  my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;
  // 线程函数
  my_aiocb.aio_sigevent.sigev_notify_function = handler;


  ret = aio_read(&my_aiocb);
  if (ret < 0) ERR_EXIT("aio_read");

  ret = aio_suspend(aio_list, 1, NULL);
  if (ret < 0) ERR_EXIT("aio_suspend");

  while(1) pause();

  puts("main exited");

  return 0;
}
  • 编译和运行
$ gcc my_aio_thread.c -o my_aio_thread -lrt
$ ./my_aio_thread


这里写图片描述

4. 总结

  • 掌握 aiocb 的成员 aio_sigevent.
  • 掌握信号通知和线程通知
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值