polardbxengine/extra/IS/dependency/easy/doc/README.txt

1049 lines
44 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

目录
1. 数据包的处理过程
见图 http://baike.corp.taobao.com/index.php/File:Easy%E6%95%B0%E6%8D%AE%E6%B5%81%E7%A8%8B%E5%9B%BE.png
2. 主要数据类型src/io/easy_io_struct.h中的数据结构
3. 服务器端和客户端程序使用api的步骤
4. 主要io接口src/io/文件夹)
5. include包src/include/文件夹)
6. util包src/util/文件夹)
1. 数据包的处理过程
1.1 服务器端调用过程
/**
* accept 事件处理
* 1. 为新连接创建一个easy_connection_t对象
* 初始化c->read_watcher = easy_connection_on_readable
* 初始化c->write_watcher = easy_connection_on_writable
* 初始化c->timeout_watcher = easy_connection_on_timeout_conn
* 2. accept请求
* 3. 调用easy_connection_on_readable
**/
void easy_connection_on_accept(struct ev_loop *loop, ev_io *w, int revents)
/**
* read事件处理
* 1. 第一次读或者上次读完整了, 新建一个easy_message_t
* 2. 从conn里读入数据
* 3. 调用easy_connection_do_request处理请求
*/
easy_connection_on_readable(struct ev_loop *loop, ev_io *w, int revents)
/**
* 处理请求
* 1. 对请求decode返回packet
* 2. 创建easy_request_t加入到m->request_list列表里
* 3. batch_process操作
* 4. 对request_list列表里的请求调用process进行处理
* 5. 1对处理返回EASY_OK的调用encode返回相应。
* 2处理返回EASY_AGAIN等待process中的回调函数在回调函数中调用easy_request_do_reply
*/
int easy_connection_do_request(easy_message_t *m)
/**
* 在process中异步处理通过回调函数返回
* 1. 通过encode把 easy_request_t->packet里面的回复信息添加到easy_connection_t->output链里面
* 2. 调用easy_connection_write_socket把easy_connection_t->output链里面的数据写到socket里面
*/
easy_request_do_reply(easy_request_t *r)
图1 easy数据流程图
1.2 客户端调用过程
1.2.1 客户端发送请求步骤
/**
* 连接。
* 1. connect到服务器
* 2. 创建一个easy_connection_t对象初始化easy_connection_t对象。
* 初始化c->read_watcher = easy_connection_on_readable
* 初始化c->write_watcher = easy_connection_on_writable
* 初始化c->timeout_watcher = easy_connection_on_timeout_conn
*/
easy_connection_t *easy_connection_connect_addr(easy_io_t *eio, easy_addr_t addrv, easy_io_handler_pt *handler)
/**
* write事件处理
* 1. 调用new_packet创建packet
* 2. 创建easy_session_t和packet
* 3. 调用easy_connection_send_session发送数据
*/
static void easy_connection_on_writable(struct ev_loop *loop, ev_io *w, int revents)
/**
* 1. 对easy_session_t进行easy_connection_sesson_build
* 在easy_connection_sesson_build里面对请求进行encode
* 2. 调用easy_connection_write_socket把encode好的数据写到socket里面
*/
int easy_connection_send_session(easy_session_t *s)
/**
* 1. 在easy_connection_sesson_build里面对请求进行encode
* 2. 以packet_id为key把easy_session_t加入到c->send_queue哈希表里面
*/
void easy_connection_sesson_build(easy_session_t *s)
1.2.2 客户端接收响应步骤
/**
* read事件处理
* 1. 第一次读或者上次读完整了, 新建一个easy_message_t
* 2. 从conn里读入数据
* 3. 调用easy_connection_do_response处理响应
*/
easy_connection_on_readable(struct ev_loop *loop, ev_io *w, int revents)
/**
* 处理响应
* 1. 对服务器返回的响应进行decode操作
* 2. 通过获取的packet_id从c->send_queue哈希表中得到easy_session_t
* 2. 调用easy_session_process处理返回的响应
* 3. 如果客户端没有发送完请求创建新的packet继续请求服务器
*/
static int easy_connection_do_response(easy_message_t *m)
/**
* 1. 对响应进行处理
*/
int easy_session_process(easy_session_t *s)
2 主要操作的类型
2.1
struct easy_io_handler_pt {
// 1.在服务器端,用于解析客户端请求。
// 2.在客户端端,用于解析服务器返回的响应
void* (*decode)(easy_message_t *m);
// 1.服务器端packet是decode的输出把数据封装后添加到output里。
//2.客户端端用于封装new_packet创建的packet然后填加到output里
int (*encode)(easy_buf_chain_t *output, void *packet);
easy_io_process_pt *process;
int (*batch_process)(easy_message_t *m);
int (*cleanup)(void *packet);
// 获取packet_id
uint64_t (*get_packet_id)(easy_connection_t *c, void *packet);//
int (*on_connect) (easy_connection_t *c); //
// 在销毁easy_connection_t的时候释放掉easy_io_handler_pt包含的数据
int (*on_disconnect) (easy_connection_t *c);
// 创建用于请求的packet和easy_session_t
//easy_connection_send_session发送请求
int (*new_packet) (easy_connection_t *c); //
void *user_data, *user_data2; //
int is_uthread;
};
// 对应一个SOCKET连接
struct easy_connection_t {
struct ev_loop *loop;
easy_pool_t *pool; // 用于分配在connect存活周期对象的空间easy_connection_t本身也有pool分配的
easy_pool_t *pbuf; // 用于创建packet easy_buf_t
easy_atomic_t ref; //
easy_io_thread_t *ioth; // 每个connect某一时刻在一个线程执行connect满足一定要求是可以切换到其他线程执行ioth用于保存线程的指针
easy_connection_t *next; // 没次accept会创建一个connetectnext指向下一个connetect
easy_list_t conn_list_node; // 加入到ioth中connect list的节点
easy_hash_list_t client_list_node; // 用于client端
// file description
uint32_t default_message_len;
int fd;
easy_addr_t addr;
ev_io read_watcher; // 在client端是easy_connection_on_readableserver端是easy_connection_on_accept/easy_connection_on_readable
ev_io write_watcher; //
ev_timer timeout_watcher; //
easy_list_t message_list; // 用于保存easy_message_t的链表。接收请求/响应时会用到easy_message_t保存一个或者多easy_request_t
easy_list_t session_list; // 用于保存easy_session_t的链表。easy_session_t是用于发送请求/响应的每个easy_session_t只带一个easy_request_t
easy_buf_chain_t output; // 输出缓存链, output.pool = pbuf。
easy_io_handler_pt *handler; // 操作类型, 包括decode,encode, process, batch_process, on_connect等等
void *user_data; //
easy_hash_t *send_queue; // 作为client端执行easy_connection_do_response时用于查找packet_id对应的easy_session_t
uint32_t status : 3; // connect()链接状态
uint32_t event_status : 3; //
uint32_t type : 2; // EASY_TYPE_CLIENT、EASY_TYPE_SERVER
uint32_t conn_has_error : 1; //
uint32_t sendfile_flag : 1; // 标记connect是否已经添加异步sendfile()操作
uint32_t tcp_cork_flag : 1; // set TCP_CORK
uint32_t tcp_nodelay_flag : 1; // set TCP_NODELAY
uint32_t wait_close : 1; // 标记关闭链接
uint32_t need_redispatch : 1; // 标记connect需要切换到其他线程执行
easy_atomic32_t doing_request_count; // server端正在处理的请求的数量。client端正在等待请求的数量
easy_atomic_t done_request_count; // 处理完请求的数量
ev_tstamp start_time; // 开始时间double类型
easy_uthread_t *uthread; //user thread
};
// easy_message_t和easy_session_t共同的部分类似于基类
struct easy_message_session_t {
easy_connection_t *c; //
easy_pool_t *pool; // 在easy_message_t存活期间的内存池
int8_t type // 转换成easy_message_session_t时用来判断是EASY_TYPE_MESSAGE或EASY_TYPE_SESSION
int8_t async; //
};
// easy_message_t用来接收请求/响应由一个或者多easy_request_t组成。
// easy_message_t是decode的单位每个easy_message_t可以decode一个或多个请求。
struct easy_message_t {
easy_connection_t *c; //
easy_pool_t *pool; // 在easy_message_t存活期间的内存池
int8_t type // 转换成easy_message_session_t时用来判断是EASY_TYPE_MESSAGE区别EASY_TYPE_SESSION
int8_t async; //
int8_t status; // EASY_MESG_READ_AGAIN、EASY_MESG_WRITE_AGAIN、EASY_ERROR
easy_atomic32_t ref; //
easy_list_t message_list_node; // 用于把easy_message_t添加到c->message_list中的节点
easy_buf_t *input; // 接收数据的缓存区input->pos是当前decode的位置input->last接收数据的末尾的下一个位置
easy_list_t request_list; // decode出来的easy_request_t链表在server端接收请求时用
int request_list_count; // request_list中easy_request_t的数量
int next_read_len; // 下一次读取数据的长度,
void *user_data; //
};
// 用于发送, 只带一个easy_request_t
struct easy_session_t {
easy_connection_t *c; //
easy_pool_t *pool; // 在easy_session_t存活期间的内存池
int8_t type // 转换成easy_message_session_t时用来判断是EASY_TYPE_SESSION区别EASY_TYPE_MESSAGE
int8_t async; //
int timeout; //
ev_timer timeout_watcher; //
easy_list_t session_list_node;//
easy_hash_list_t send_queue_node; // c->send_queue发送队列hash表里的节点用于查找packet_id对应的easy_session_t
uint64_t packet_id; // 每个easy_session_t有一个packet_id来标示不同的packet返回的响应中也要获得packet_id来去发送的c->send_queue查找对应的packet
void *thread_ptr; //
easy_request_t r; // easy_session_t对应的请求
char data[0]; //
};
// ipacket放进来的包, opacket出去的包
// easy_request_t在处理请求的时候创建 ms = easy_message_t在new_packet创建ms = easy_session_t
struct easy_request_t {
easy_message_session_t *ms; // 对应的easy_message_t/easy_session_t
easy_list_t request_list_node;//
void *ipacket; // ipacket放进来的包
void *opacket; // opacket出去的包
void *args; //
};
struct easy_thread_pool_t {
int thread_count; // 线程数量
int member_size; // 线程池所包含的线程的size
easy_atomic32_t last_number; // 用于顺序返回线程的计数器
easy_list_t list_node; // 把easy_thread_pool_t加入到eio->thread_pool_list
easy_thread_pool_t *next; // file tp 和 request tp可能有多个存放在eio->thread_pool链表中
char *last; // 线程数组的结尾 last = &data[0] + member_size * thread_count;
char data[0]; // 线程对象数组
};
// 处理IO的线程easy中处理链接的工作线程叫easy_io_thread_t
struct easy_io_thread_t {
easy_baseth_on_start_pt *on_start; // 线程开始函数
pthread_t tid; // 线程id
int idx; // 线程在线程池中的位置索引
struct ev_loop *loop; //
ev_async thread_watcher; //
easy_atomic_t thread_lock; //
easy_io_t *eio; // 线程所在的eio
// file
int task_count; // 线程正在执行异步io任务数量
easy_pool_t *task_pool; // 用于异步操作的内存池
// queue
easy_list_t conn_list, session_list, request_list, filet_list;
// listen watcher
ev_timer listen_watcher;
easy_io_uthread_start_pt *on_utstart;
void *ut_args;
// client list
easy_client_t *client_list;
// connected list
easy_list_t connected_list;
easy_atomic32_t doing_request_count;
easy_atomic_t done_request_count;
};
// 处理FILE读写线程叫easy_file_thread_t
struct easy_file_thread_t {
easy_baseth_on_start_pt *on_start; // 线程开始函数
pthread_t tid; // 线程id
int idx; // 线程在线程池中的位置索引
struct ev_loop *loop; //
ev_async thread_watcher; //
easy_atomic_t thread_lock; //
easy_io_t *eio; // 线程所在的eio
// queue
int task_list_count; // 异步文件线程task_list中任务的数量
easy_list_t task_list; // io线程加进来的任务列表
};
// easy_io对象维护全局变量包括io线程池file线程池处理request的线程池
struct easy_io_t {
easy_pool_t *pool;
easy_list_t eio_list_node; // 再有多个easy_io_t时把easy_io_t加入到easy_io_list_var全局链表里
easy_atomic_t lock; // 用于对easy_io_t的使用加锁
easy_listen_t *listen; // 添加的监听端口一个eio可以有多个监听
easy_client_t *client, *default_client;//
int io_thread_count, file_thread_count;// io线程和file线程数
easy_thread_pool_t *io_thread_pool; // io线程池每个io线程有一个ev_loop
easy_thread_pool_t *file_thread_pool;// 文件线程池,用处理异步文件操作
easy_thread_pool_t *thread_pool; // file线程池和request线程池的链表
void *user_data; //
easy_list_t thread_pool_list; // 用于存放作用于easy_io_t的所有线程池
// flags
uint32_t stoped : 1; // 调用过easy_eio_stop(eio)让eio停止标志
uint32_t started : 1; // 调用过easy_eio_start(eio),线程已经开始执行了
uint32_t tcp_cork : 1; // 默认eio链接的tcp_cork表示设成1
uint32_t tcp_nodelay : 1; // 默认eio链接的tcp_nodelay表示设成0
uint32_t listen_all : 1; //
uint32_t uthread_enable : 1;//
ev_tstamp start_time; // 创建eio的时间
easy_atomic_t send_byte; // writev发送的字节
easy_atomic_t recv_byte; // 接收的字节
};
3 服务器端和客户端程序使用api的步骤
3.1 服务器端步骤
1) 使用easy_io_create初始化线程。对easy_io初始化, 设置io的线程数, file的线程数。
/**
* io_thread_count 用于处理socket请求的线程数
* file_thread_count 文件io的线程数多用于磁盘操作
* 返回值 EASY_ERROR(-1) 创建失败EASY_OK(0)创建成功
*/
easy_io_t *easy_io_create(int io_thread_count, int file_thread_count)
例:
if (easy_io_create(1, 0)) {
fprintf(stderr, "easy_io_create error.\n");
return EASY_ERROR;
}
2) 实例化easy_io_handler_pt对象为监听端口设置处理函数并增加一个监听端口。
easy_io_handler_pt io_handler; // 详见2.1 struct easy_io_handler_pt
/**
* 增加监听端口, 要在easy_io_start开始前调用
*
* @param host 机器名或IP, 或NULL
* @param port 端口号
*
* @return 如果成功返回easy_connection_t对象, 否则返回NULL
*/
easy_listen_t *easy_io_add_listen(const char *host, int port, easy_io_handler_pt *handler)
例:
memset(io_handler, 0, sizeof(io_handler));
io_handler.decode = echo_decode;
io_handler.encode = echo_encode;
io_handler.process = echo_process;
if ((listen = easy_io_add_listen(NULL, 8080, io_handler)) == NULL) {
fprintf(stderr, "easy_connection_add_listen error, port: 8080, %s\n", strerror(errno));
return EASY_ERROR;
} else {
fprintf(stderr, "listen start, port = 8080\n");
}
3) 开始执行线程
/**
* 开始easy_io, 第一个线程用于listen, 后面的线程用于处理
*/
int easy_io_start()
例:
if (easy_io_start()) {
fprintf(stderr, "easy_io_start error.\n");
return EASY_ERROR;
}
4)此步骤可选。起处理速度定时器
/**
* 起处理速度定时器
*/
void easy_io_stat_watcher_start(ev_timer *stat_watcher, double interval,
easy_io_stat_t *iostat, easy_io_stat_process_pt *process)
例:
ev_timer stat_watcher;
easy_io_stat_t iostat;
easy_io_stat_watcher_start(stat_watcher, 5.0, iostat, NULL);
5) 等待线程退出
/**
* 等待easy_io
*/
int easy_io_wait()
例:
if (easy_io_wait() != EASY_OK) {
fprintf(stderr, "easy io is aborted!!!\n");
}
3.2 客户端步骤
1初始化线程。对easy_io初始化, 设置io的线程数, file的线程数。
/**
* io_thread_count 用于处理socket请求的线程数
* file_thread_count 文件io的线程数多用于磁盘操作
* 返回值 EASY_ERROR(-1) 创建失败EASY_OK(0)创建成功
*/
easy_io_t *easy_io_create(int io_thread_count, int file_thread_count)
例:
if (easy_io_create(1, 0)) {
fprintf(stderr, "easy_io_init error.\n");
return EASY_ERROR;
}
2创建连接。
/**
* 创建easy_connection_t与服务器建立连接
* addrv 64地址。
*/
easy_connection_t *easy_io_connect_addr(easy_addr_t addrv, easy_io_handler_pt *handler);
例:
/* 客户程序填充服务端的资料 */
struct sockaddr_in server_addr;
bzero(server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5000);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
easy_addr_t address = *((easy_addr_t*)server_addr);
// 为监听端口设置处理函数,并增加一个监听端口
easy_io_handler_pt io_handler;
memset(io_handler, 0, sizeof(io_handler));
io_handler.decode = echo_decode;
io_handler.encode = echo_encode;
io_handler.process = echo_process;
io_handler.new_packet = echo_new_packet;
io_handler.on_disconnect = echo_disconnect;
io_handler.user_data = (void *)(long)cp.request_size;
// 创建连接
if (easy_io_connect_addr(address, io_handler) == NULL) {
fprintf(stderr, "failure: %s\n", easy_socket_addr_to_str(cp.address, 0, 0));
break;
}
3) 开始执行线程
/**
* 开始easy_io, 第一个线程用于listen, 后面的线程用于处理
*/
int easy_io_start()
例:
if (easy_io_start()) {
fprintf(stderr, "easy_io_start error.\n");
return EASY_ERROR;
}
4)此步骤可选。起处理速度定时器
/**
* 起处理速度定时器
*/
void easy_io_stat_watcher_start(ev_timer *stat_watcher, double interval,
easy_io_stat_t *iostat, easy_io_stat_process_pt *process)
例:
ev_timer stat_watcher;
easy_io_stat_t iostat;
easy_io_stat_watcher_start(stat_watcher, 5.0, iostat, NULL);
5) 等待线程退出
/**
* 等待easy_io
*/
int easy_io_wait()
例:
if (easy_io_wait() != EASY_OK) {
fprintf(stderr, "easy io is aborted!!!\n");
}
4. 主要io接口
4.1 easy_io.h
/**
* 初始化线程。对easy_io初始化。
* io_thread_count 用于处理socket请求的线程数
* file_thread_count 文件io的线程数多用于磁盘操作
* 返回值 EASY_ERROR(-1) 创建失败EASY_OK(0)创建成功
*/
easy_io_t *easy_eio_create(easy_io_t *eio, int io_thread_count, int file_thread_count);
/**
* 开始easy_io, 第一个线程用于listen, 后面的线程用于处理
*/
int easy_eio_start(easy_io_t *eio);
/**
* 等待eio->thread_pool_list中所有线程退出
*/
int easy_eio_wait(easy_io_t *eio);
/**
* 标记eio->stoped = 1唤醒所有线程
*/
int easy_eio_stop(easy_io_t *eio);
/**
* 释放资源,销毁所有线程
*/
void easy_eio_destroy(easy_io_t *eio);
void easy_eio_set_uthread_start(easy_io_t *eio, easy_io_uthread_start_pt *on_utstart, void *args);
struct ev_loop *easy_eio_thread_loop(easy_io_t *eio, int index);
void easy_eio_stat_watcher_start(easy_io_t *eio, ev_timer *stat_watcher,
double interval, easy_io_stat_t *iostat, easy_io_stat_process_pt *process);
/**
* 增加监听端口到eio->listen监听链表中, 要在easy_io_start开始调用
*
* @param host 机器名或IP, 或NULL
* @param port 端口号
*
* @return 如果成功返回easy_connection_t对象, 否则返回NULL
*/
easy_listen_t *easy_connection_add_listen(easy_io_t *eio,
const char *host, int port, easy_io_handler_pt *handler)
/**
* 链接host服务器同easy_connection_connect_addr
*/
easy_connection_t *easy_connection_connect(easy_io_t *eio,
const char *host, int port, easy_io_handler_pt *handler)
/**
* 连接。
* 1. connect到服务器
* 2. 创建一个easy_connection_t对象初始化easy_connection_t对象。
* 初始化c->read_watcher = easy_connection_on_readable
* 初始化c->write_watcher = easy_connection_on_writable
* 初始化c->timeout_watcher = easy_connection_on_timeout_conn
*/
easy_connection_t *easy_connection_connect_addr(easy_io_t *eio,
easy_addr_t addrv, easy_io_handler_pt *handler)
/**
* 创建处理request线程
*/
easy_thread_pool_t *easy_thread_pool_create(easy_io_t *eio, int cnt, easy_io_process_pt *cb)
/**
* 断开连接
*/
void easy_connection_disconnect(easy_connection_t *c)
4.2 异步线程读取文件的使用:
首先在easy_io_create第二个参数大于等于1创建异步文件线程数
例对easy_io初始化, 设置io的线程数, file的线程数
if (!easy_io_create(1, 1)) {
easy_error_log("easy_io_init error.\n");
return EASY_ERROR;
}
在异步文件线程创建好之后就可以执行文件读写请求了:
easy_http_request_on_process中对文件处理步骤
文件处理步骤1打开文件
int fd = open(filename, O_RDONLY);
1如果使用sendfile把数据直接发送出去
文件处理步骤2创建easy_file_buf_t并对easy_buf_t初始化
easy_file_buf_t *fb = easy_file_buf_create(p->output.pool); // 创建
fb->fd = fd; // 初始化发送的文件描述符
fb->offset = 0; // 初始化sendfile的偏移量
fb->count = read_len; // 初始化发送的数据长度
fb->close = easy_file_buf_on_close; // 发送完的对文件的处理操作,这里选择关闭文件描述符
fb->args = NULL; // 设置用于(fb->close)(fd->fd, fb->args)的参数
文件处理步骤3把easy_file_buf_t加到packet输出缓存链表里面
easy_buf_chain_push_fbuf(&p->output, fb);
文件处理步骤4返回EASY_OK
return EASY_OK;
2如果使用异步文件线程读取数据
文件处理步骤2如果使用异步文件线程读取数据经过处理之后再发出去
b = easy_buf_create(p->output.pool, fs.st_size); // 创建easy_buf_t
文件处理步骤3编写异步线程处理完文件任务之后的回调函数回调函数是easy_file_process_pt类型
typedef int (easy_file_process_pt)(easy_file_task_t *ft);
static int read_done(easy_file_task_t *ft)
{
easy_request_t *r = (easy_request_t *) ft->args;
if (ft->ret < 0) {
easy_warn_log("async read done: read len: %d\n", ft->ret);
}
// your code ...
close(ft->fd);
return easy_request_do_reply(r);
}
文件处理步骤4把读取文件请求加入到file线程任务队列里read_done是异步线程读取完数据的回调函数
easy_file_async_read(r->ms->c, fd, b->pos, read_len, 0, read_done, r, b); // 把读取文件请求加入到file线程任务队列里
文件处理步骤5使用异步文件线程读取数据process的返回值要返回EASY_AGAIN暂停对这个请求的处理等候异步线程的处理请求完成调用read_done继续处理没完成的工作。
return EASY_AGAIN;
5. include包
5.1 easy_define.h
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
// 返回p按照a的长度对齐的指针
#define easy_align_ptr(p, a) (uint8_t*)(((uintptr_t)(p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
// 返回d按照a对齐的长度
#define easy_align(d, a) (((d) + (a - 1)) & ~(a - 1))
#define easy_max(a,b) (a > b ? a : b)
#define easy_min(a,b) (a < b ? a : b)
#define EASY_OK 0
#define EASY_ERROR (-1)
#define EASY_ABORT (-2)
#define EASY_ASYNC (-3)
#define EASY_BREAK (-4)
#define EASY_AGAIN (-EAGAIN)
5.2 easy_list.h参考kernel list.h
/**
* 双向循环链表链表第一个不作为链表的node只作为head数据从head->next开始
*/
struct easy_list_t {
easy_list_t *next, *prev;
};
/**
* 用EASY_LIST_HEAD_INIT或者easy_list_init初始化链表headprev和next都指向自己
*/
#define EASY_LIST_HEAD_INIT(name) {(name), (name)}
#define easy_list_init(ptr) do { \
(ptr)->next = (ptr); \
(ptr)->prev = (ptr); \
} while (0)
/**
* 向head链表头中添加list节点list只能是一个easy_list_t节点
* 注在head和head->next中间添加listhead是头节点list是链表中
* 第一个entry
*/
static inline void easy_list_add_head(easy_list_t *list, easy_list_t *head)
/**
* 向head链表尾中添加list节点list只能是一个easy_list_t节点
* 注在head和head->prev中间添加listhead是头节点list是尾节点
*/
static inline void easy_list_add_tail(easy_list_t *list, easy_list_t *head)
/**
* 把entry从所在链表中删除
*/
static inline void easy_list_del(easy_list_t *entry)
/**
* 判断head是否只有一个节点只有一个head节点链表为空
*/
static inline int easy_list_empty(const easy_list_t *head)
/**
* 把list赋值给new_list
*/
static inline void easy_list_movelist(easy_list_t *list, easy_list_t *new_list)
/**
* 当list不为空即链表中大于一个节点把list链表加入到head链表前面
* list可以包含多个元素
*/
static inline void easy_list_join(easy_list_t *list, easy_list_t *head)
/**
* 通过member链表节点的地址获取链表所在对象的地址
* ptr为type对象的member的指针地址即ptr=type.member
* type为想要获取的对象类型member为type的成员
*/
#define easy_list_entry(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**
* 遍历head链表中的对象从head->next开始遍历head节点本身不做遍历
* pos为链表所在对象的临时指针head为要遍历的链表member是pos的
* 成员对象链表节点
*/
#define easy_list_for_each_entry(pos, head, member) \
for (pos = easy_list_entry((head)->next, typeof(*pos), member); \
pos->member != (head); \
pos = easy_list_entry(pos->member.next, typeof(*pos), member))
5.3 easy_atomic.h
#define easy_atomic_set(v,i) ((v) = (i))
// 32bit
typedef volatile int32_t easy_atomic32_t;
/**
* 32bit原子操作 v = v + i
*/
static __inline__ void easy_atomic32_add(easy_atomic32_t *v, int i)
/**
* 32bit原子操作 v = v + i 返回v的值
*/
static __inline__ int32_t easy_atomic32_add_return(easy_atomic32_t *value, int32_t diff)
/**
* 32bit原子操作 v ++
*/
static __inline__ void easy_atomic32_inc(easy_atomic32_t *v)
/**
* 32bit原子操作 v --
*/
static __inline__ void easy_atomic32_dec(easy_atomic32_t *v)
// 64bit
typedef volatile int64_t easy_atomic_t;
/**
* 64bit原子操作 v = v + i
*/
static __inline__ void easy_atomic_add(easy_atomic_t *v, int64_t i)
/**
* 64bit原子操作 v = v + i 返回v的值
*/
static __inline__ int64_t easy_atomic_add_return(easy_atomic_t *value, int64_t diff)
/**
* 64bit原子操作 lock值由原来的old赋值成set值设置成功返回1失败返回0
* lock原来的值不等于old会设置不成功返回0
*/
static __inline__ int64_t easy_atomic_cmp_set(easy_atomic_t *lock, int64_t old, int64_t set)
/**
* 64bit原子操作 v ++
*/
static __inline__ void easy_atomic_inc(easy_atomic_t *v)
/**
* 64bit原子操作 v --
*/
static __inline__ void easy_atomic_dec(easy_atomic_t *v)
/**
* 对lock加锁如果加锁不成功线程继续执行其它任务
*/
#define easy_trylock(lock) (*(lock) == 0 && easy_atomic_cmp_set(lock, 0, 1))
// 对lock解锁
#define easy_unlock(lock) {__asm__ ("" ::: "memory"); *(lock) = 0;}
#define easy_spin_unlock easy_unlock
/**
* 对lock加锁如果lock被加过锁线程会自旋一段时间在这段时间自己还不能拥有锁把cpu交给其他线程
* 把线程放到调度队列的队尾等待调度。
*/
static __inline__ void easy_spin_lock(easy_atomic_t *lock)
// 对地址addr所指向的内存的第nr位设成0
static __inline__ void easy_clear_bit(unsigned long nr, volatile void *addr)
// 对地址addr所指向的内存的第nr位设成1
static __inline__ void easy_set_bit(unsigned long nr, volatile void *addr)
6. util包
6.1 easy_hash.h和easy_hash.c
struct easy_hash_t { // hash table
easy_hash_list_t **buckets; // hash桶
uint32_t size; // hash桶的数量 桶的数量为2 ^ n
uint32_t mask; // 桶数量的掩码size - 1
uint32_t count; // 对象的数量
int offset; // easy_hash_list_t在对象中的偏移量offsetof(type obj_type, easy_hash_list_t hash_list)
uint64_t seqno; // 向hash table中增加数据的序列标示从1开始计数
};
struct easy_hash_list_t {
uint64_t key;
easy_hash_list_t *next, **pprev;
};
/**
* 创建并初始化hash table
* offset为easy_hash_list_t在对象中的偏移量 = offsetof(type obj_type, easy_hash_list_t hash_list)
* size hash桶的数量pool用于分配easy_hash_t的内存池
* 返回创建的easy_hash_t *
*/
easy_hash_t *easy_hash_create(easy_pool_t *pool, uint32_t size, int offset)
/**
* 向hash表table中增加节点返回EASY_OK
*/
int easy_hash_add(easy_hash_t *table, uint64_t key, easy_hash_list_t *list)
/**
* 通过key查找对象返回的是easy_hash_list_t所在的对象的指针
*/
void *easy_hash_find(easy_hash_t *table, uint64_t key)
typedef int (easy_hash_cmp_pt)(const void *a, const void *b);
/**
* 找到相同key值的对象obj返回满足cmp(a, obj) == 0的第一个对象
*/
void *easy_hash_find_ex(easy_hash_t *table, uint64_t key, easy_hash_cmp_pt cmp, const void *a)
/**
* 删除key对应的easy_hash_list_t如果删除成功返回删除easy_hash_list_t对应的对象
* 没有删除成功返回NULL
*/
void *easy_hash_del(easy_hash_t *table, uint64_t key)
/**
* 删除节点node成功返回1失败返回0
*/
int easy_hash_del_node(easy_hash_list_t *node)
/**
* hash 64 bit
* key为64bit时封装 easy_hash_code
*/
uint64_t easy_hash_key(volatile uint64_t key)
/**
* 生成hash值key字符串lenkey的长度seed随机种子一般为一个质数
* 同 MurmurHash64A
*/
uint64_t easy_hash_code(const void *key, int len, unsigned int seed)
6.2 easy_buf.c和easy_buf.h
/**
* 用于存放数据的buffer
*/
struct easy_buf_t {
easy_buf_t *next; // 用于在easy_buf_chain_t中使用的下一个buf
int flags; // 判断是默认的buf、EASY_BUF_FILE三种情况
char *pos; // buf当前指向的位置
char *last; // buf包含有效数据最后一个字节的下一个位置
char *start; // buf开始位置
char *end; // buf缓存区最后一个位置end - last表示剩余可用的空间
};
/**
* 文件buffer用于sendfile等
*/
struct easy_file_buf_t {
easy_buf_t *next; // 用于在easy_buf_chain_t中使用的下一个buf
int flags; // 判断是默认的buf、EASY_BUF_MALLOC、EASY_BUF_FILE三种情况
int fd; // 文件描述符
int64_t offset; // 文件偏移量
int64_t count; // 文件的大小
easy_fbuf_close_pt *close; // 释放buffer时调用的关闭文件的函数
void *args; // close参数
};
/**
* buffer链表
*/
struct easy_buf_chain_t {
easy_buf_t *head;
easy_buf_t *tail;
easy_pool_t *pool; // 用于创建easy_buf_t
};
/**
* easy string
*/
struct easy_buf_string_t {
char *data;
int len;
};
/**
* 创建一个新的easy_buf_t
*/
easy_buf_t *easy_buf_create(easy_pool_t *pool, uint32_t size)
/**
* 把data包成easy_buf_t
*/
easy_buf_t *easy_buf_pack(easy_pool_t *pool, const char *data, uint32_t size)
/**
* 创建一个easy_file_buf_t, 用于sendfile等
*/
easy_file_buf_t *easy_file_buf_create(easy_pool_t *pool)
/**
* 销毁buffer
*/
void easy_buf_destroy(easy_pool_t *pool, easy_buf_t *b)
/**
* 空间不够,分配出一块来,保留之前的空间
*/
int easy_buf_check_read_space(easy_pool_t *pool, easy_buf_t *b, uint32_t size)
/**
* 空间不够,分配出一块来,保留之前的空间
*/
int easy_buf_check_write_space(easy_buf_chain_t *bc, easy_buf_t *b, uint32_t size)
/**
* 弹出easy_buf_chain_t中顶端的easy_buf_t并返回弹出的值
*/
easy_buf_t *easy_buf_chain_pop(easy_buf_chain_t *l)
/**
* 清除easy_buf_chain_t释放easy_buf_t
*/
void easy_buf_chain_clear(easy_buf_chain_t *l)
/**
* 向easy_buf_chain_t结尾处添加easy_buf_t
*/
void easy_buf_chain_push(easy_buf_chain_t *l, easy_buf_t *b)
/**
* 向easy_buf_chain_t结尾处添加easy_file_buf_t
*/
void easy_buf_chain_push_fbuf(easy_buf_chain_t *l, easy_file_buf_t *b)
/**
* 把l合并到d的结尾处
*/
void easy_buf_chain_merge(easy_buf_chain_t *l, easy_buf_chain_t *d)
/**
* 把s复制到d上
*/
int easy_buf_string_copy(easy_pool_t *pool, easy_buf_string_t *d, easy_buf_string_t *s)
/**
* 格式字符串打印到d上
*/
int easy_buf_string_printf(easy_pool_t *pool, easy_buf_string_t *d, const char *fmt, ...)
6.3 easy_pool.c和easy_pool.h
struct easy_pool_cleanup_t {
easy_pool_cleanup_pt handler; // 在释放pool的时候的清理函数
const void *data; // 要清理的数据
easy_pool_cleanup_t *next; // 清理函数可以有多个用next连起来
};
struct easy_pool_large_t {
easy_pool_large_t *next;
uint8_t data[0];
};
struct easy_pool_t {
uint8_t *last; // 指向easy_pool_t未分配最后一个字节的下一个字节
uint8_t *end; // 指向easy_pool_t内存区域的结束字节的下一个字节
easy_pool_t *next; // 此easy_pool_t内存区域不够分了有多个easy_pool_t连起来
uint32_t failed; // 从current指向的easy_pool_t分配失败分配一个新的easy_pool_t的次数
uint32_t max; // 从easy_pool_t内存区域分配的最大值超过max的用easy_pool_large_t分配
// pool header
// 下面只在pool中的第一个easy_pool_t也就是没有加入到easy_pool_t->next的用到
// 加入到easy_pool_t->next的easy_pool_t下面变量占用的空间会被分给用户
easy_pool_t *current; // 当前指向的easy_pool_t开始分配空间给用户直到next中的最后一个easy_pool_t
easy_pool_large_t *large; // 要分配的size大于max用easy_pool_large_t分配并把分配好的内存添加到large链表后面
easy_pool_cleanup_t *cleanup; // 在释放pool的时候的清理操作
easy_atomic_t lock; // 多线程分配的自旋锁使用easy_pool_t分配内存是线程安全的
uint32_t ref; // ref是用到这个easy_pool_t的多线程引用的数量为了判断是否加锁
easy_atomic32_t bufcnt; // 用easy_pool_t分配的easy_buf_t或easy_file_buf_t的数量
};
/**
* 创建内存池当size等于0的时内存池大小为EASY_POOL_ALIGNMENT(512)字节
* p->max = EASY_POOL_ALIGNMENT - sizeof(easy_pool_t)
* 分配的内存从p + sizeof(easy_pool_t)开始
*/
easy_pool_t *easy_pool_create(uint32_t size)
/**
* 把pool清空
*/
void easy_pool_clear(easy_pool_t *pool)
/**
* 释放pool
*/
void easy_pool_destroy(easy_pool_t *pool)
/**
* 从pool中分配size大小的空间
* 分配的空间起始位置按sizeof(unsigned long)对齐
*/
void *easy_pool_alloc(easy_pool_t *pool, uint32_t size)
/**
* 从pool中分配size大小的空间起始位置不一定对齐
*/
void *easy_pool_nalloc(easy_pool_t *pool, uint32_t size)
/**
* 注册 在释放pool的时候对data的清理操作
*/
void easy_pool_cleanup_register(easy_pool_t *pool, const void *data, easy_pool_cleanup_pt handler)
/**
* 从pool中分配size大小的空间 并清零
* 分配的空间起始位置按sizeof(unsigned long)对齐
*/
void *easy_pool_calloc(easy_pool_t *pool, uint32_t size)
/**
* 设置分配函数代替easy_pool_default_realloc
*/
void easy_pool_set_allocator(easy_pool_realloc_pt alloc)
6.4 easy_array_queue.h和easy_array_queue.c
/**
* 固定长度的FIFO队列
*/
struct easy_array_queue_t {
int count; // 队列里对象数量
int object_size; // 先入先出队列每个对象的size
int max_size; // 能存放最大对象的数量
char *rpos; // 队列头读的位置
char *wpos; // 队列尾push进的位置
char *last; // 队列实际内存中队列最后的位置
char data[0]; // 队列内存中开始的位置
};
/**
* 创建队列
*/
easy_array_queue_t *easy_array_queue_create(easy_pool_t *pool, int max_size, int object_size)
void *easy_array_queue_push(easy_array_queue_t *queue, void *item)
void *easy_array_queue_pop(easy_array_queue_t *queue)
6.5 easy_inet.h和easy_inet.c
char *easy_inet_addr_to_str(easy_addr_t *addr, char *buffer, int len);
easy_addr_t easy_inet_str_to_addr(const char *host, int port);
int easy_inet_parse_host(easy_addr_t *address, const char *host, int port);
int easy_inet_is_ipaddr(const char *host);
6.6 easy_string.h和easy_string.c
char *easy_strncpy(char *dst, const char *src, size_t n);
char *easy_string_tohex(const char *str, int n, char *result, int size);
6.7 easy_time.h和easy_time.c
int easy_localtime(const time_t *t, struct tm *tp);