zoukankan      html  css  js  c++  java
  • [13]APUE:KQUEUE / FreeBSD

    [a] 概述

    • kqueue API 由两个函数(kqueue、kevent)、一个辅助宏(EV_SET)、一个结构体(struct kevent)构成,可以应用于 socket、FIFO、pipe、aio、signal、process、regular file、path 等对象
    • 与 Linux 下的 epoll 功能类似,性能相当

    [b] kqueue / kevent / EV_SET

    #include <sys/types.h>
    #include <sys/event.h>
    #include <sys/time.h>
    int kqueue(void) 
    //成功返回 kqueue 标识符,供 kevent 函数首个参数使用,出错返回 -1 int kevent(int kq, const struct kevent *changelist, int nchange, struct kevent *eventlist, int nevent, const struct timespec *timeout)
    //成功返回 eventlist 数组的元素个数,出错信息写入 struct kevent 中的 flag 字段,不能写入的出错信息返回 -1,若 timeout 超时,则返回 0 EV_SET(kev, ident, filter, flags, fflags, data, udata)
    //kev 是指向 struct kevent 的指针,其余字段依次对应于 struct kevent 中的字段 
    struct kevent {
        uintptr_t    ident; //event 标识符,根据 filter 的不同可以为 fd、pid 等
        short    filter; //监听类别(目标),如 socket、process 等
        u_short    flags; //通用标志,可用于保存返回信息
        u_int    fflags; //特定 filter 的专有标志,可用于保存专有返回信息
        intptr_t    data; //特定 filter 存储专有信息
        void    *udata; //进程预定义内容,常作识别用途,kqueue 不更改此项,原样返回其内容
    • kqueue 函数用于初始化一个 kqueue 队列,单个进程可以建立多个 kqueue,kqueue 描述符与文件描述符类似,也可以复用,不能被 fork 出的子进程继承
    • kevent 用于设置策略及如何获取结果,其中:
      • changelist 用于添加或修改 event
      • eventlist 用于向调用进程返回 event 结果,若实际产生的事件数量超过了 nevent,可通过多次调用获取全部结果;若 nevent 为 0,则即使指定了 timeout 也会立即返回,不阻塞
      • changelist 与 eventlist 可以指向同一个 kevent 结构体数组;nchange 与 nevent 分别代表两个结构体数组的元素数量上限
      • 如果 timeout 参数为 NULL,则 kevent 函数将阻塞,直到有事件发生,若不为 NULL,阻塞到超时为止(<24h),若 timespec 结构体各字段均设置为 0,则立即返回当前尚未处理的所有事件
    • EV_SET 仅是预定义的一个用于设置 struct kevent 的辅助宏,与手动指定结构体的各字段效果相同

    [c] filter 及专有规则

    • /usr/include/sys/event.h 中定义的 10 种 filter
    • #define EVFILT_READ             (-1)
      #define EVFILT_WRITE            (-2)
      #define EVFILT_AIO              (-3)    /* attached to aio requests */
      #define EVFILT_VNODE            (-4)    /* attached to vnodes */
      #define EVFILT_PROC             (-5)    /* attached to struct proc */
      #define EVFILT_SIGNAL           (-6)    /* attached to struct proc */
      #define EVFILT_TIMER            (-7)    /* timers */
      #define EVFILT_PROCDESC         (-8)    /* attached to process descriptors */
      #define EVFILT_FS               (-9)    /* filesystem events */
      #define EVFILT_LIO              (-10)   /* attached to lio requests */
    • EVFILT_READ:用于检测数据何时可读,不同的描述符将返回不同的信息
      • 若 ident 字段指定为一个文件描述符,则该事件表示文件偏移量尚未到达文件末尾,当前基于 SEEK_END 的 offset 值保存在 data 成员字段中
      • 若为 pipe 或 FIFO 则表示已有实际数据可读,data 字段存储可读的字节数量
    • EVFILT_WRITE:用于检测是否可以对描述符执行写操作;对 pipe、FIFO 或 socket,data 字段代表缓冲区中可用的空间(bytes),此 filter 对文件或目录没有意义(vnodes)
    • EVFILT_AIO:用于异步 I/O 操作,用于检测和 aio_error 系统调用类似的条件
    • EVFILT_VNODE:用于检测文件系统上某个文件的各种变动事件,ident 须指定一个有效的文件描述符,fflags 字段设定检测的事件类别(如下)
      • #define NOTE_DELETE     0x0001                  /* vnode was removed */
        #define NOTE_WRITE      0x0002                  /* data contents changed, a write occurred on the file */
        #define NOTE_EXTEND     0x0004                  /* size increased */
        #define NOTE_ATTRIB     0x0008                  /* attributes changed */
        #define NOTE_LINK       0x0010                  /* link count changed */
        #define NOTE_RENAME     0x0020                  /* vnode was renamed */
        #define NOTE_REVOKE     0x0040                  /* vnode access was revoked */
        #define NOTE_OPEN       0x0080                  /* vnode was opened */
        #define NOTE_CLOSE      0x0100                  /* file closed, fd did not allowed write */
        #define NOTE_READ       0x0400                  /* file was read */
    • EVFILT_PROC:用于检测发生在另一个进程里的事件,此时 ident 字段须指定进程 ID,fflags 字段设定检测的事件类别(如下)
      • #define NOTE_EXIT       0x80000000              /* process exited, exit status will be stored in 'data' */
        #define NOTE_FORK       0x40000000              /* process forked */
        #define NOTE_EXEC       0x20000000              /* process exec'd */
        #define NOTE_TRACK      0x00000001              /* follow across forks */
        #define NOTE_TRACKERR   0x00000002              /* could not track child */
        #define NOTE_CHILD      0x00000004              /* am a child process */
    • EVFILT_SIGNAL:检测是否有信号发送至 ident 所指定的进程 ID
      • 事件将在进行完常规的信号处理之后,放到 kqueue 中,data 字段存储收到的信号数量,包括被设置了 SIG_IGN 的信号(SIG_CHLD 除外,此信号若被忽略则不计数)
      • 此 filter 默认带有 EV_CLEAR 标志

    [d] 通用标志:flags

    • /usr/include/sys/event.h 定义的通用 flags,按位 OR 进行组合
      • /* actions */
        #define EV_ADD          0x0001          /* add event to kq (implies enable) */
        #define EV_DELETE       0x0002          /* delete event from kq */
        #define EV_ENABLE       0x0004          /* enable event */
        #define EV_DISABLE      0x0008          /* disable event (not reported) */
        #define EV_FORCEONESHOT 0x0100          /* enable _ONESHOT and force trigger */
        
        /* flags */
        #define EV_ONESHOT      0x0010          /* only report one occurrence */
        #define EV_CLEAR        0x0020          /* clear event state after reporting */
        #define EV_RECEIPT      0x0040          /* force EV_ERROR on success, data=0 */
        #define EV_DISPATCH     0x0080          /* disable event after once reporting */
        
        #define EV_SYSFLAGS     0xF000          /* reserved by system */
        #define EV_DROP         0x1000          /* note should be dropped */
        #define EV_FLAG1        0x2000          /* filter-specific flag */
        #define EV_FLAG2        0x4000          /* filter-specific flag */
        
        /* returned values */
        #define EV_EOF          0x8000          /* EOF detected */
        #define EV_ERROR        0x4000          /* error, data contains errno */
    • EV_ADD:添加事件,默认附带 EV_ENABLE 行为,相同事件重复添加会导致旧的事件被覆盖
    • EV_DELETE:从 kqueue 中清除指定项(filter)
    • EV_DISABLE:禁止 kqueue 返回某个事件的信息,但并不删除
    • EV_ONESHOT:针对同一对象的同类事件仅返回一次
    • EV_CLEAR:返回事件后清空现有状态,用途是监测每次状态的改变,而非当前的状态;对可写、可读这种有持续存在的事件状态往往需要设置,对一次性状态,如信号计数,则不需要

    [e] 样例 

    #include <sys/types.h>
    #include "sys/event.h"
    #include <sys/time.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    char *t0 = "/tmp/test_0";
    char *t1 = "/tmp/test_1";
    
    void unlinktmp(int signo)
    {
            unlink(t0);
            unlink(t1);
            printf("%s, %s deleted! 
    ", t0, t1);
            exit(0);
    }
    
    int main(void)
    {
            struct kevent event[2];
            struct kevent tevent[2];
            int kq, ret;
            unsigned long fd_0, fd_1;
            
            struct sigaction act;
            act.sa_handler=unlinktmp;
            sigemptyset(&act.sa_mask);
            act.sa_flags=0;
    
            sigaction(SIGINT, &act, NULL);
    
            fd_0 = open(t0, O_WRONLY|O_CREAT|O_TRUNC, 0600);
            fd_1 = open(t1, O_WRONLY|O_CREAT|O_TRUNC, 0600);
    
            kq = kqueue();
            if (kq == -1)
            {
                    perror("kqueue()");
            }
    
            EV_SET(&event[0], fd_0, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_LINK|NOTE_EXTEND, 0, NULL);
            EV_SET(&event[1], fd_1, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_LINK|NOTE_EXTEND, 0, NULL);
    
            kevent(kq, event, 2, NULL, 0, NULL);
    
            while(1) 
            {
                    ret = kevent(kq, NULL, 0, tevent, 2, NULL);
                    if (ret < 0) 
                    {
                            perror("kevent");
                    }
                    else
                    {
                            for(int i = 0; i < ret; ++i)
                            {
                                    if (tevent[i].fflags & NOTE_LINK)
                                    {
                                            printf("%s: link num changed! 
    ", tevent[i].ident == fd_0 ? t0 : t1);
                                    }
                                    else if (tevent[i].fflags & NOTE_EXTEND)
                                    {
                                            printf("%s: extanded! 
    ", tevent[i].ident == fd_0 ? t0 : t1);
                                    }
                                    else
                                    {
                                            printf("%s: nothing happened!
    ", tevent[i].ident == fd_0 ? t0 : t1);
                                    }
                            }
                    }
            }
    }
  • 相关阅读:
    ansible-playbook启动的多种方式
    git版本控制
    特征工程
    特征工程:图像特征提取和深度学习
    tensorflow数据读取机制tf.train.slice_input_producer 和 tf.train.batch 函数
    浙大版《C语言程序设计(第3版)》题目集 练习2-9 整数四则运算 (10 分)
    浙大版《C语言程序设计(第3版)》题目集 练习2-8 计算摄氏温度 (10 分)
    浙大版《C语言程序设计(第3版)》题目集 练习2-6 计算物体自由下落的距离 (5 分)
    浙大版《C语言程序设计(第3版)》题目集 练习2-4 温度转换 (5 分)
    浙大版《C语言程序设计(第3版)》题目集 练习2-3 输出倒三角图案 (5 分)
  • 原文地址:https://www.cnblogs.com/hadex/p/6201279.html
Copyright © 2011-2022 走看看