一起玩玩libuv(二)

Handle和Request是libuv最基本的数据结构,或者说对象。libuv的各种功能都是通过它们来实现。本文利用libuv的Timer Handle实现了一个周期性gc的实例程序;利用filesystem Request实现了一个异步读取文件的实例。

1.Handle 与 Request

Handles represent long-lived objects capable of performing certain operations while active. Some examples: a prepare handle gets its callback called once every loop iteration when active, and a TCP server handle get its connection callback called every time there is a new connection.
Requests represent (typically) short-lived operations. These operations can be performed over a handle: write requests are used to write data on a handle; or standalone: getaddrinfo requests don’t need a handle they run directly on the loop.

上面是我摘录了官网上对Handle和Request的解释,目前来说,本人对Handle和Request的理解是:Handle是一种长期的对象,我们可以在Handle上挂一个Timer,周期性的调用某个Callback;Request是一个短期的对象,比如从文件里面读一段数据。
下面就是libuv中Handle和Request的声明。

/* Handle types. */
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;
typedef struct uv_udp_s uv_udp_t;
typedef struct uv_pipe_s uv_pipe_t;
typedef struct uv_tty_s uv_tty_t;
typedef struct uv_poll_s uv_poll_t;
typedef struct uv_timer_s uv_timer_t;
typedef struct uv_prepare_s uv_prepare_t;
typedef struct uv_check_s uv_check_t;
typedef struct uv_idle_s uv_idle_t;
typedef struct uv_async_s uv_async_t;
typedef struct uv_process_s uv_process_t;
typedef struct uv_fs_event_s uv_fs_event_t;
typedef struct uv_fs_poll_s uv_fs_poll_t;
typedef struct uv_signal_s uv_signal_t;

/* Request types. */
typedef struct uv_req_s uv_req_t;
typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
typedef struct uv_getnameinfo_s uv_getnameinfo_t;
typedef struct uv_shutdown_s uv_shutdown_t;
typedef struct uv_write_s uv_write_t;
typedef struct uv_connect_s uv_connect_t;
typedef struct uv_udp_send_s uv_udp_send_t;
typedef struct uv_fs_s uv_fs_t;
typedef struct uv_work_s uv_work_t;

/* None of the above. */
typedef struct uv_cpu_info_s uv_cpu_info_t;
typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;

2.timer

每一个异步IO库都少不了Timer,下面是利用Timer实现一个gc的周期性处理程序,但我们希望当loop中没有其他的handle的时候,loop会结束。因此我们要使用uv_unref接口,将Timer的Handle的引用在loop之前先删除。

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

uv_loop_t* loop;
uv_timer_t gc_timer;
uv_timer_t fake_job_timer;

void gc_cb(uv_timer_t* handle) {
    printf("GC unused objects.\n");
}

void fake_job_cb(uv_timer_t* handle) {
    printf("Fake job done.\n");
}

int main()
{
    loop = uv_default_loop();

    uv_timer_init(loop, &gc_timer);
    uv_unref((uv_handle_t*)&gc_timer);

    uv_timer_start(&gc_timer, gc_cb, 0, 2000);

    uv_timer_init(loop, &fake_job_timer);
    uv_timer_start(&fake_job_timer, fake_job_cb, 9000, 0);

    return uv_run(loop, UV_RUN_DEFAULT);
}

输出如下:

output:
GC unused objects.
GC unused objects.
GC unused objects.
GC unused objects.
GC unused objects.    
Fake job done.
Program ended with exit code: 0

3. filesystem

Linux系统提供的文件数据读是阻塞模式的,libuv为了将所有的IO操作都搞成异步的,在文件操作上加入了线程池。对文件操作是一种Request,向loop请求一种Request(open, read, close),libuv通过callback返回最终结果。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <uv.h>

uv_fs_t open_fs_r;
uv_fs_t read_fs_r;

static char buffer[1024];
static uv_buf_t iov;
static read_off = 0;

void read_cb(uv_fs_t* request) {
    if(request->result < 0) {
        fprintf(stderr, "error read file; %s\n", uv_strerror(request->result));
    } else if(request->result == 0) {
        uv_fs_t close_fs_r;
        uv_fs_close(uv_default_loop(), &close_fs_r, open_fs_r.result, NULL);
    } else {
        iov.base[request->result] = 0;
        fprintf(stdout, "%s", iov.base);
        read_off += request->result;
        uv_fs_read(uv_default_loop(), &read_fs_r, open_fs_r.result, &iov, 1, read_off, read_cb);
    }
}

void open_cb(uv_fs_t* request) {
    if(request->result > 0) {   
        iov = uv_buf_init(buffer, sizeof(buffer) - 1);
        uv_fs_read(uv_default_loop(), &read_fs_r, request->result, &iov, 1, read_off, read_cb);
    } else {
        fprintf(stderr, "error open file: %s\n", uv_strerror(request->result));
    }
}

int main(int argc, char **argv)
{
    uv_fs_open(uv_default_loop(), &open_fs_r, argv[1], O_RDONLY, S_IRWXU, open_cb);
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    uv_fs_req_cleanup(&open_fs_r);
    uv_fs_req_cleanup(&read_fs_r);
}

4.后记

libuv将所有的IO在上层抽象为异步IO,但在内部实现做了区分——网络IO是走kqueue和epoll,文件IO是利用线程池。