Vswitchd是ovs中运行在用户空间的守护进程, 实现ovs主要的功能逻辑, 本文将着重分析其启动过程.
数据结构
bridge/port/iface/ofproto/ofport
在数通领域, 交换机和桥很多时候可以是在说一个东西, 它工作在二层, 可以添加多个端口, 从一个端口上收到的报文会根据MAC表从其他某个端口转发出去. 在ovs中, 它也还是一个东西, 不过ovs用两个数据结构描述它们: 交换机(ofproto)与桥(bridge). 它们在ovs中是一一对应的, 也就是说一个bridge就有一个对应的ofproto. 相比而言, bridge更贴近用户, ofproto跟底层联系更多. 想想添加一个桥的命令是 ovs-vsctl add-br 就可见一斑了.
交换机需要添加端口才能具有实际作用, ovs用port和ofprot描述端口, 前者对应bridge, 更贴近用户配置, 而后者对应ofproto,更贴近底层. ovs还有一个结构叫iface, 一般而言, 一个port包含一个iface, 但存在一种聚合(bond)的情况, 我们可以把多个iface捆在一起, 将它们一并归纳到一个port.此时port和iface就是一对多的关系了.
以下为前面提到的几个数据结构之间的联系
Vswitchd启动
入口
Vswitchd 进程的入口在ovs-vswitchd.c ,下面我们将主要关注主干流程的分析,而忽略其中的一些旁枝末节
int main(int argc, char *argv[])
{
remote = parse_options(argc, argv, &unixctl_path)
bridge_init(remote);
while (!existing){
bridge_run();
bridge_wait();
poll_block();
}
.....exit();
return 0;
}
初始化 bridge 模块
bridge 模块的初始化在 bridge_init 中完成, bridge_init主要的动作是连接数据库ovsdb, 并从中读取配置信息.
void bridge_init(const char *remote)
{
idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true, true);
ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg);
......
}
注意, 像ovsrec_open_vswitch_col_cur_cfg这样的全局变量的定义是编译时自动生成的, 从官方下载的代码中最初是没有它的定义的.
运行 bridge 模块
bridge 模块的初始化在 bridge_run 中进行
void bridge_run(void)
{
cfg = ovsrec_open_vswitch_first(idl);
bridge_init_ofproto(cfg);
bridge_run__();
if (ovsdb_idl_get_seqno(idl) != idl_seqno || if_notifier_changed(ifnotifier))
{
bridge_reconfigure(cfg ? cfg : &null_cfg)
}
...
}
首先读取数据库ovsdb中的配置到cfg, 此时, cfg中就有了诸如用户创建了多少个bridge, 每个bridge中有多少个port, iface等信息.
在bridge_init_ofproto中, 会进行ofproto library初始化, 之前提到了, bridge更靠近用户配置, ofproto更靠近底层, 这里就是进行底层库的初始化
void ofproto_init(const struct shash *iface_hints)
{
ofproto_class_register(&ofproto_dpif_class);
.....
for (i = 0; i < n_ofproto_classes; i++) {
ofproto_classes[i]->init(&init_ofp_ports);
}
}
ofproto library首先进行ofproto class类的注册, ovs当前仅支持一种类, 即ofproto_dpif_class, 所以后面调用init, 实际上也只会调用ofproto_dpif_class->init().
观察ofproto的数据结构中 的前几项
struct ofproto{
const struct ofproto_class *ofproto_class;
char *type; /* Datapath type */
char *name; /* Datapath name */
...
}
其中, ofproto_class即为生产者的class, 在当前ovs中, 只会指向ofproto_dpif_class, type为ofproto的类型, 也可称为provider, 当前的ofproto_dpif_class有两种provider , 记录在dpif_classes中, 它的来源是base_dpif_classes[]
static const struct dpif_class *base_dpif_classes[] = {
&dpif_netlink_class,
&dpif_netdev_class,
}
可以把dpif_classes看成一张注册表, 里面有两个条目ofproto_dpif_class实现的enumerate_types方法可以列举当前支持的datapath的类型, 在当前的ovs实现中,就是"system"和"netdev"
再来看 bridge_run__(void)
static void bridge_run__(void)
{
ofproto_enumerate_types(&types);
SSET_FOR_EACH(type, &types){
ofproto_type_run(type);
}
HMAP_FOR_EACH(br, node, &all_bridges){
ofproto_run(br->ofproto);
}
}
首先bridge_run__将当前支持的type列举出来, 再逐个调用ofproto_type_run ,最终调用到ofproto_dpif_class->type_run(),这里在第一次运行时, 由于all_dpif_backers为空,所以就直接返回了.
然后, bridge_run__对all_bridges上记录的每个bridge调用ofproto_run, 同样在第一次运行在这里时all_bridges为空, 所以也不会有实际的动作.