一、VIO数据类型

VIO是一个数据结构,在include/violite.h中定义的说明中有一句话“This structure is for every connection on both sides.Note that it has a non-default move assignment operator, so if adding more
members, you’ll need to update operator=.”,它的意思是说这个数据结构可以用来网络连接的服务端和客户端。后面就是说没有默认的操作符如果需要就自己搞一个。这个不重要。VIO主要是用在了网络通信Socket中的数据结构的封装。一般来说,封装的目的是为抽象,抽象的目的是为更有适应性,让程序在不同的平台或者说环境下的不修改或者改动最小。对具体动作的依赖降低,这也是设计原则里,依赖于抽象而不依赖于具体的行为动作。比如在处理不同的写入操作时就只考虑接口的实现,而不用考虑具体的细节的实现。
一般来说,对网络通信的封装有好多种情形,网上也有不少的知名的网络库,这个需要大家认真分析一下。不过MYSQL应该对网络通信的要求没有库的那么高,但一定会有独特一些的要求,下面会分析一下。

二、MySql中的定义

先看一下相关的定义:

typedef Vio Vio;
#define MYSQL_VIO Vio *
#endifenum enum_vio_type : int {/**Type of the connection is unknown.*/NO_VIO_TYPE = 0,/**Used in case of TCP/IP connections.*/VIO_TYPE_TCPIP = 1,/**Used for Unix Domain socket connections. Unix only.*/VIO_TYPE_SOCKET = 2,/**Used for named pipe connections. Windows only.*/VIO_TYPE_NAMEDPIPE = 3,/**Used in case of SSL connections.*/VIO_TYPE_SSL = 4,/**Used for shared memory connections. Windows only.*/VIO_TYPE_SHARED_MEMORY = 5,/**Used internally by the prepared statements*/VIO_TYPE_LOCAL = 6,/**Implicitly used by plugins that doesn't support any other VIO_TYPE.*/VIO_TYPE_PLUGIN = 7,FIRST_VIO_TYPE = VIO_TYPE_TCPIP,/*If a new type is added, please update LAST_VIO_TYPE. In addition, pleasechange get_vio_type_name() in vio/vio.c to return correct name for it.*/LAST_VIO_TYPE = VIO_TYPE_PLUGIN
};

这个定义如果学过SOCKET通信的,应该看得很明白。不同的平台的套接字,不同的SSL验证方式。管道和共享内存的定义等等,这没啥。再看一下具体的定义:

struct Vio {MYSQL_SOCKET mysql_socket;          /* Instrumented socket */bool localhost = {false};           /* Are we from localhost? */enum_vio_type type = {NO_VIO_TYPE}; /* Type of connection */int read_timeout = {-1};  /* Timeout value (ms) for read ops. */int write_timeout = {-1}; /* Timeout value (ms) for write ops. */int retry_count = {1};    /* Retry count */bool inactive = {false};  /* Connection has been shutdown */struct sockaddr_storage local;  /* Local internet address */struct sockaddr_storage remote; /* Remote internet address */size_t addrLen = {0};           /* Length of remote address */char *read_buffer = {nullptr};  /* buffer for vio_read_buff */char *read_pos = {nullptr};     /* start of unfetched data in theread buffer */char *read_end = {nullptr};     /* end of unfetched data */#ifdef USE_PPOLL_IN_VIOmy_thread_t thread_id = {0};  // Thread PIDsigset_t signal_mask;         // Signal mask/*Flag to indicate whether we are in poll or shutdown.A true value of flag indicates either the sockethas called  shutdown or it is sleeping on a poll call.False value of this flag means that the socket isnot sleeping on a poll call.This flag provides synchronization between two threadsone entering vio_io_wait and another entering vio_shutdownfor the same socket. If the other thread is waiting on pollsleep, it wakes up the thread by sending a signal viapthread_kill. Also it ensures that no other thread enters into a poll call if it's socket has undergone shutdown.*/std::atomic_flag poll_shutdown_flag = ATOMIC_FLAG_INIT;
#elif defined HAVE_KQUEUEint kq_fd = {-1};std::atomic_flag kevent_wakeup_flag = ATOMIC_FLAG_INIT;
#endif#ifdef HAVE_SETNS/**Socket network namespace.*/char network_namespace[256];
#endif/*VIO vtable interface to be implemented by VIO's like SSL, Socket,Named Pipe, etc.*//*viodelete is responsible for cleaning up the VIO object by freeinginternal buffers, closing descriptors, handles.*/void (*viodelete)(MYSQL_VIO) = {nullptr};int (*vioerrno)(MYSQL_VIO) = {nullptr};size_t (*read)(MYSQL_VIO, uchar *, size_t) = {nullptr};size_t (*write)(MYSQL_VIO, const uchar *, size_t) = {nullptr};int (*timeout)(MYSQL_VIO, uint, bool) = {nullptr};int (*viokeepalive)(MYSQL_VIO, bool) = {nullptr};int (*fastsend)(MYSQL_VIO) = {nullptr};bool (*peer_addr)(MYSQL_VIO, char *, uint16 *, size_t) = {nullptr};void (*in_addr)(MYSQL_VIO, struct sockaddr_storage *) = {nullptr};bool (*should_retry)(MYSQL_VIO) = {nullptr};bool (*was_timeout)(MYSQL_VIO) = {nullptr};/*vioshutdown is resposnible to shutdown/close the channel, so that nofurther communications can take place, however any related buffers,descriptors, handles can remain valid after a shutdown.*/int (*vioshutdown)(MYSQL_VIO) = {nullptr};bool (*is_connected)(MYSQL_VIO) = {nullptr};bool (*has_data)(MYSQL_VIO) = {nullptr};int (*io_wait)(MYSQL_VIO, enum enum_vio_io_event, int) = {nullptr};bool (*connect)(MYSQL_VIO, struct sockaddr *, socklen_t, int) = {nullptr};
#ifdef _WIN32
#ifdef __clang__OVERLAPPED overlapped = {0, 0, {{0, 0}}, nullptr};
#else// MSVC, at least up to 2015, gives an internal error on the above.OVERLAPPED overlapped = {0};
#endifHANDLE hPipe{nullptr};
#endifvoid *ssl_arg = {nullptr};struct PSI_socket_locker *m_psi_read_locker = {nullptr};PSI_socket_locker_state m_psi_read_state;struct PSI_socket_locker *m_psi_write_locker = {nullptr};PSI_socket_locker_state m_psi_write_state;
#if defined(_WIN32)HANDLE handle_file_map = {nullptr};char *handle_map = {nullptr};HANDLE event_server_wrote = {nullptr};HANDLE event_server_read = {nullptr};HANDLE event_client_wrote = {nullptr};HANDLE event_client_read = {nullptr};HANDLE event_conn_closed = {nullptr};size_t shared_memory_remain = {0};char *shared_memory_pos = {nullptr};#endif /* _WIN32 */bool (*is_blocking)(Vio *vio) = {nullptr};int (*set_blocking)(Vio *vio, bool val) = {nullptr};int (*set_blocking_flag)(Vio *vio, bool val) = {nullptr};/* Indicates whether socket or SSL based communication is blocking or not. */bool is_blocking_flag = {true};private:friend Vio *internal_vio_create(uint flags);friend void internal_vio_delete(Vio *vio);friend bool vio_reset(Vio *vio, enum_vio_type type, my_socket sd, void *ssl,uint flags);explicit Vio(uint flags);~Vio();public:Vio(const Vio &) = delete;Vio &operator=(const Vio &) = delete;Vio &operator=(Vio &&vio);
};

这个结构体并不大,加上所有的注释才一百多行,从这方面可以明白,它的功能肯定有限,根本没法和网络库相比。看一下官方的说明:
https://dev.mysql.com/doc/dev/mysql-server/latest/structVio.html#acaedc338e42997991e9444e4173d6df2

这个地址对此结构体的描述相当清晰,这里就不再翻译一下,狗尾续貂了。
另外在vio/vio_priv.h中还有一些相关的定义:

extern PSI_memory_key key_memory_vio;
extern PSI_memory_key key_memory_vio_read_buffer;
extern PSI_memory_key key_memory_vio_ssl_fd;#ifdef _WIN32
size_t vio_read_pipe(Vio *vio, uchar *buf, size_t size);
size_t vio_write_pipe(Vio *vio, const uchar *buf, size_t size);
bool vio_is_connected_pipe(Vio *vio);
int vio_shutdown_pipe(Vio *vio);size_t vio_read_shared_memory(Vio *vio, uchar *buf, size_t size);
size_t vio_write_shared_memory(Vio *vio, const uchar *buf, size_t size);
bool vio_is_connected_shared_memory(Vio *vio);
int vio_shutdown_shared_memory(Vio *vio);
void vio_delete_shared_memory(Vio *vio);
#endif /* _WIN32 */bool vio_buff_has_data(Vio *vio);
int vio_socket_io_wait(Vio *vio, enum enum_vio_io_event event);
int vio_socket_timeout(Vio *vio, uint which, bool old_mode);size_t vio_ssl_read(Vio *vio, uchar *buf, size_t size);
size_t vio_ssl_write(Vio *vio, const uchar *buf, size_t size);/* When the workday is over... */
int vio_ssl_shutdown(Vio *vio);
void vio_ssl_delete(Vio *vio);
bool vio_ssl_has_data(Vio *vio);#endif /* VIO_PRIV_INCLUDED */

从名字上基本就可以看出这些函数的定义目的,再加上函数内的注释,没有什么区别。

三、应用

先看一下怎么产生个一VIO:

Vio *vio_new(my_socket sd, enum enum_vio_type type, uint flags) {Vio *vio;MYSQL_SOCKET mysql_socket = MYSQL_INVALID_SOCKET;DBUG_TRACE;DBUG_PRINT("enter", ("sd: %d", sd));mysql_socket_setfd(&mysql_socket, sd);vio = mysql_socket_vio_new(mysql_socket, type, flags);return vio;
}Vio *mysql_socket_vio_new(MYSQL_SOCKET mysql_socket, enum_vio_type type,uint flags) {Vio *vio;my_socket sd = mysql_socket_getfd(mysql_socket);DBUG_TRACE;DBUG_PRINT("enter", ("sd: %d", sd));if ((vio = internal_vio_create(flags))) {if (vio_init(vio, type, sd, flags)) {internal_vio_delete(vio);return nullptr;}vio->mysql_socket = mysql_socket;}return vio;
}

至于SetBlock这些具体的属性,懂Socket的一眼就明白,不懂得就好好看看网络编程,不然说也说不清楚。说得直白一点就是设置Socket的属性是阻塞还是非阻塞的。
下来自然就是连接了:

bool vio_socket_connect(Vio *vio, struct sockaddr *addr, socklen_t len,bool nonblocking, int timeout, bool *connect_done) {int ret, wait;int retry_count = 0;DBUG_TRACE;/* Only for socket-based transport types. */assert(vio->type == VIO_TYPE_SOCKET || vio->type == VIO_TYPE_TCPIP);/* If timeout is not infinite, set socket to non-blocking mode. */if (((timeout > -1) || nonblocking) && vio_set_blocking(vio, false))return true;/* Initiate the connection. */do {ret = mysql_socket_connect(vio->mysql_socket, addr, len);} while (ret < 0 && vio_should_retry(vio) &&(retry_count++ < vio->retry_count));if (connect_done) *connect_done = (ret == 0);#ifdef _WIN32wait = (ret == SOCKET_ERROR) && (WSAGetLastError() == WSAEINPROGRESS ||WSAGetLastError() == WSAEWOULDBLOCK);
#elsewait = (ret == -1) && (errno == EINPROGRESS || errno == EALREADY);
#endif/*The connection is in progress. The vio_io_wait() call can be usedto wait up to a specified period of time for the connection tosucceed.If vio_io_wait() returns 0 (after waiting however many seconds),the socket never became writable (host is probably unreachable.)Otherwise, if vio_io_wait() returns 1, then one of two conditionsexist:1. An error occurred. Use getsockopt() to check for this.2. The connection was set up successfully: getsockopt() willreturn 0 as an error.*/if (!nonblocking && wait &&(vio_io_wait(vio, VIO_IO_EVENT_CONNECT, timeout) == 1)) {int error;IF_WIN(int, socklen_t) optlen = sizeof(error);IF_WIN(char, void) *optval = (IF_WIN(char, void) *)&error;/*At this point, we know that something happened on the socket.But this does not means that everything is alright. The connectmight have failed. We need to retrieve the error code from thesocket layer. We must return success only if we are sure thatit was really a success. Otherwise we might prevent the callerfrom trying another address to connect to.*/if (connect_done) *connect_done = true;if (!(ret = mysql_socket_getsockopt(vio->mysql_socket, SOL_SOCKET, SO_ERROR,optval, &optlen))) {
#ifdef _WIN32WSASetLastError(error);
#elseerrno = error;
#endifret = (error != 0);}}/* If necessary, restore the blocking mode, but only if connect succeeded. */if (!nonblocking && (timeout > -1) && (ret == 0)) {if (vio_set_blocking(vio, true)) return true;}if (nonblocking && wait) {if (connect_done) *connect_done = false;return false;} else {return (ret != 0);}
}

这是多么熟悉的味道啊,熟悉到真得不想再解释,不过还是简单说一下,不同的平台对Socket的处理略有不同,但基本上是设置协议族,相关端口转换,阻塞非阻塞的设置,如果把相关的异常去除,就会看得更加清楚。假如是小白在看这段代码,建议是使用Windows下的,然后去除异常,就非常明了整个过程了。

然后是读和写,这里只看读的:

size_t vio_read(Vio *vio, uchar *buf, size_t size) {ssize_t ret;int flags = 0;DBUG_TRACE;/* Ensure nobody uses vio_read_buff and vio_read simultaneously. */assert(vio->read_end == vio->read_pos);/* If timeout is enabled, do not block if data is unavailable. */if (vio->read_timeout >= 0) flags = VIO_DONTWAIT;while ((ret = mysql_socket_recv(vio->mysql_socket, (SOCKBUF_T *)buf, size,flags)) == -1) {int error = socket_errno;/* Error encountered that is unrelated to blocking; percolate it up. */
#if SOCKET_EAGAIN == SOCKET_EWOULDBLOCKif (error != SOCKET_EAGAIN)
#elseif (error != SOCKET_EAGAIN && error != SOCKET_EWOULDBLOCK)
#endifbreak;/*Nonblocking with either EAGAIN or EWOULDBLOCK. Don't callio_wait. 0 bytes are available.*/assert(error == SOCKET_EAGAIN || error == SOCKET_EWOULDBLOCK);if (!vio_is_blocking(vio)) {DBUG_PRINT("info", ("vio_read on nonblocking socket read no bytes"));return -1;}/* Wait for input data to become available. */if ((ret = vio_socket_io_wait(vio, VIO_IO_EVENT_READ))) break;}return ret;
}/*Buffered read: if average read size is small it mayreduce number of syscalls.
*/size_t vio_read_buff(Vio *vio, uchar *buf, size_t size) {size_t rc;
#define VIO_UNBUFFERED_READ_MIN_SIZE 2048DBUG_TRACE;DBUG_PRINT("enter", ("sd: %d  buf: %p  size: %u",mysql_socket_getfd(vio->mysql_socket), buf, (uint)size));if (vio->read_pos < vio->read_end) {rc = std::min<size_t>(vio->read_end - vio->read_pos, size);memcpy(buf, vio->read_pos, rc);vio->read_pos += rc;/*Do not try to read from the socket now even if rc < size:vio_read can return -1 due to an error or non-blocking mode, andthe safest way to handle it is to move to a separate branch.*/} else if (size < VIO_UNBUFFERED_READ_MIN_SIZE) {rc = vio_read(vio, (uchar *)vio->read_buffer, VIO_READ_BUFFER_SIZE);if (rc != 0 && rc != (size_t)-1) {if (rc > size) {vio->read_pos = vio->read_buffer + size;vio->read_end = vio->read_buffer + rc;rc = size;}memcpy(buf, vio->read_buffer, rc);}} elserc = vio_read(vio, buf, size);return rc;
#undef VIO_UNBUFFERED_READ_MIN_SIZE
}

然后就是关闭了:

int vio_shutdown(Vio *vio) {int r = 0;DBUG_TRACE;if (vio->inactive == false) {assert(vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SOCKET ||vio->type == VIO_TYPE_SSL);assert(mysql_socket_getfd(vio->mysql_socket) >= 0);if (mysql_socket_shutdown(vio->mysql_socket, SHUT_RDWR)) r = -1;#ifdef USE_PPOLL_IN_VIOif (vio->thread_id != 0 && vio->poll_shutdown_flag.test_and_set()) {// Send signal to wake up from poll.if (pthread_kill(vio->thread_id, SIGALRM) == 0)vio_wait_until_woken(vio);elseperror("Error in pthread_kill");}
#elif defined HAVE_KQUEUEif (vio->kq_fd != -1 && vio->kevent_wakeup_flag.test_and_set())vio_wait_until_woken(vio);
#endifif (mysql_socket_close(vio->mysql_socket)) r = -1;
#ifdef HAVE_KQUEUEif (vio->kq_fd == -1 || close(vio->kq_fd)) r = -1;vio->kq_fd = -1;
#endif}if (r) {DBUG_PRINT("vio_error", ("close() failed, error: %d", socket_errno));/* FIXME: error handling (not critical for MySQL) */}vio->inactive = true;vio->mysql_socket = MYSQL_INVALID_SOCKET;return r;
}

原始的Shutdown其实就弄个参数是关闭一方(接或者收)或者全关闭。这就是所谓的优雅的关闭Socket,可是这个Shutdown里发现还处理了线程和队列 等的相关内容,最后才Close了Socket句柄。

四、总结

如果没有基本学习一门新东西,其实成本是很高的,毕竟新东西里,全新的只是精髓的一部分,如果基础的前置条件都不过关,那么这事儿自然就无法更好的理解后面的精髓了。底下托人办事有过经验的知道,真正办事儿的正主,其实是好说话的,难在于见到正主前的各种小喽啰。
意思不太准确,只求易于理解一下。
努力啊,归来的少年!

mysql源码分析——VIO数据结构相关推荐

  1. 转 MySQL源码分析

    看到一个不错的介绍,原址如下: http://software.intel.com/zh-cn/blogs/2010/08/20/mysql0/ MySQL源码分析(0):编译安装及调试 作者: Yu ...

  2. MySQL 源码分析 binlog 编号上限

    MySQL 源码分析 binlog 编号上限 更新时间:2022-10-30 文章目录 MySQL 源码分析 binlog 编号上限 内容声明 问题描述 测试想法 问题测试 源码说明 MAX_LOG_ ...

  3. mysql源码分析——索引的数据结构

    引子 说几句题外话,在京被困三个月之久,不能回家,所以这个源码分析就中断了.之所以在家搞这个数据库的源码分析,主要是在家环境齐全,公司的电脑老旧不堪.意外事件往往打断正常的习惯和运行轨迹,但这却是正常 ...

  4. MySQL 源码分析 v2.0

    第一节 mysql编译 (一).参考 https://blog.jcole.us/innodb/ https://www.cnblogs.com/zengkefu/p/5674503.html htt ...

  5. mysql源码分析——InnoDB引擎启动分析

    一.InnoDB启动 在MySql中,InnoDB的启动流程其实是很重要的.一些更细节的问题,就藏在了这其中.在前面分析过整个数据库启动的流程,本篇就具体分析一下InnoDB引擎启动所做的各种动作.在 ...

  6. 鸿蒙轻内核M核源码分析:数据结构之任务就绪队列

    摘要:本文会给读者介绍鸿蒙轻内核M核源码中重要的数据结构,任务基于优先级的就绪队列Priority Queue. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列三 数据结构-任务就绪队列> ...

  7. mysql 执行概况_转mysql源码分析之SQL执行过程简介

    本人打算从SQL语句的执行开始学习和分析MYSQL源码,首先了解MYSQL是如何执行一条SQL语句的,详细了解它的执行过程之后,再深入学习执行一条SQL语句的运行原理. 1)从执行一条SQL语句的堆栈 ...

  8. mysql源码分析书籍_从源码分析 MySQL 死锁问题入门

    链接:https://juejin.im/post/5ce287326fb9a07ea8039d70 这篇文章主要讲的是如何通过调试 MySQL 源码,知道一条 SQL 真正会拿哪些锁,不再抓虾,瞎猜 ...

  9. mysql源码分析书籍

    哪里可以下载mysql的源代码,请把详细地址贴出来? MySQL5.0.18-源码包有点旧了的.貌似官网上有新版的,但我也没找到~ 本回答由网友推荐 如何查看mySQL的源代码 给你个过来人的建议.两 ...

最新文章

  1. 基于Struts2框架的名片管理系统
  2. 密码错误Neo.ClientError.Security.Unauthorized: The client is unauthorized due to authentication failure
  3. [流媒体]实例解析MMS流媒体协议,下载LiveMediaVideo[4]
  4. MATLAB图形的修饰(选择图形窗口、线型点颜色、坐标轴、图形标注、栅格和图形叠加的设置)
  5. Lesson 16.4 卷积遇见深度学习
  6. java 经典语录_JavaSpring过时的经典语录
  7. GSON反序列化时,字符串被转换成科学记数法的问题处理
  8. Uboot 命令是如何被使用的?
  9. 设置cookie,跨域取值
  10. Java经典设计模式-创建型模式-单例模式(Singleton)
  11. C++实现离散余弦变换(参数为二维指针)
  12. 发布一个MsBuild任务组件-可用于同时发布多个网站
  13. Atitit jpql ast总结v2 t025.docx 目录 1.1. 多select字段 1 1.2. 多个and条件 (ok) 2 1.3. Select 字段函数(聚合等) 2 1.4. [
  14. 最新PYTHON批量下载快手个人主页短视频代码
  15. python计算条件概率_统计算法_概率基础
  16. python 3des加密_python3使用3des加密
  17. 使用WSS的Lists.UpdateListItems()方法之被截断的CAML
  18. 内蒙古煤炭经济杂志社内蒙古煤炭经济编辑部2022年第14期目录
  19. 最通俗易懂的讲解工厂模式
  20. python 提取字幕_使用Python从zimuku下载字幕

热门文章

  1. Python 读写硬盘、U盘扇区数据的方法
  2. android游戏开发组件,Android实现疯狂连连看游戏之开发游戏界面(二)
  3. python idle怎么设置中文_python IDLE 背景以及字体大小的修改方法
  4. matlab车头时距,教你如何判断车头与前车的距离?so easy!
  5. GAN能进行股票预测吗?
  6. VIPCARD微信会员开多行业多开SAAS系统
  7. hugginface/diffusers 原理
  8. 四川省2020年9月计算机二级成绩查询,2020年9月四川计算机二级考试成绩查询入口...
  9. 【微服务】Nacos为什么丢弃短连接(http)而选择拥抱长连接(gRPC)
  10. Ubuntu16.04+Theano环境