First, let's look at a simple example (official file ev. can be found in the pod) # include <ev. h> # include <stdio. h> ev_io stdin_watcher; ev_timer timeout_watcher; static voidstdin_cb (EV_P _ ev_io * w, int revents) {puts ("stdin ready"); ev_io_stop (EV_A _ w ); ev_break (EV_A _ EVBREAK_ALL);} static aggregate (EV_P _ ev_timer * w, int revents) {puts ("timeout"); ev_break (EV_A _ EVBREAK_ONE);} int main () {struct ev_loop * loop = EV_DEFAULT; ev_io_init (& stdin_watcher, stdin_cb, 0, EV_READ); ev_io_start (loop, & stdin_watcher); struct (& timeout_watcher, timeout_cb, 5.5, 0 .); ev_timer_start (loop, & timeout_watcher); ev_run (loop, 0); return 0;} We only care about the following sentence: struct ev_loop * loop = EV_DEFAULT; ev_io_init (& stdin_watcher, stdin_cb, 0, EV_READ); ev_io_start (loop, & stdin_watcher); ev_run (loop, 0); first, let's see what operations ev_io_init has done: ev. # define ev_io_init (ev, cb, fd, events) do {ev_init (ev), (cb); ev_io_set (ev), (fd ), (events);} while (0) # define ev_init (ev, cb _) do {\ (ev_watcher *) (void *) (ev )) -> active = \ (ev_watcher *) (void *) (ev)-> pending = 0; \ ev_set_priority (ev), 0 ); \ ev_set_cb (ev), cb _); \} while (0) # define ev_io_set (ev, fd _, events _) do {(ev) -> fd = (fd _); (ev)-> events = (events _) | ev1_iofdset;} while (0) # define ev_set_cb (ev, cb _) ev_cb (ev) = (cb _) # define ev_cb (ev)-> cb/* rw */you can see that when ev_io_init is mainly used: & stdin_watcher-> active = stdin_watcher-> pending = 0; & stdin_watcher-> priority = 0; & stdin_watcher-> cb = stdin_cb (function); & stdin_watcher-> fd; & stdin_watcher-> events = EV_READ | ev1_iofdset Similarly, ev_io_start performs some value assignment operations, which are not described here. The following uses a function call diagram to show libev function calls: