quagga源码分析--内核通信netlink
Linux操作系统中当CPU处于内核状态时,可以分为有用户上下文的状态和执行硬件、软件中断两种。其中当处于有用户上下文时,由于内核态和用户态的内 存映射机制不同,不可直接将本地变量传给用户态的内存区;处于硬件、软件中断时,无法直接向用户内存区传递数据,代码执行不可中断。针对传统的进程间通信 机制,他们均无法直接在内核态和用户态之间使用,原因如下表:
| 通信方法 |
无法介于内核态与用户态的原因 |
|
管道(不包括命名管道) |
局限于父子进程间的通信。 |
|
消息队列 |
在硬、软中断中无法无阻塞地接收数据。 |
|
信号量 |
无法介于内核态和用户态使用。 |
|
内存共享 |
需要信号量辅助,而信号量又无法使用。 |
|
套接字 |
在硬、软中断中无法无阻塞地接收数据。 |
一、解决内核态和用户态通信机制可分为两类:
- 处于有用户上下文时,可以使用Linux提供的copy_from_user()和copy_to_user()函数完成,但由于这两个函数可能阻塞,因此不能在硬件、软件的中断过程中使用。
- 处于硬、软件中断时。
2.1 可以通过Linux内核提供的spinlock自旋锁实现内核线程与中断过程的同步,由于内核线程运行在有上下文的进程中,因此可以在内核线程中使用套接字或消息队列来取得用户空间的数据,然后再将数据通过临界区传递给中断过程.
2.2 通过Netlink机制实现。Netlink 套接字的通信依据是一个对应于进程的标识,一般定为该进程的 ID。Netlink通信最大的特点是对对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用 用户事先指定的接收函数。通过软中断而不是自行启动内核线程保证了数据传输的及时性。
二、Netlink优点
Netlink相对于其他的通信机制具有以下优点:
使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。
Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。
Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。
Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。
三、quagga代码中的netlink
rt_link.c:
static const struct message nlmsg_str[] = {
{ RTM_NEWROUTE, "RTM_NEWROUTE" },
{ RTM_DELROUTE, "RTM_DELROUTE" },
{ RTM_GETROUTE, "RTM_GETROUTE" },
{ RTM_NEWLINK, "RTM_NEWLINK" },
{ RTM_DELLINK, "RTM_DELLINK" },
{ RTM_GETLINK, "RTM_GETLINK" },
{ RTM_NEWADDR, "RTM_NEWADDR" },
{ RTM_DELADDR, "RTM_DELADDR" },
{ RTM_GETADDR, "RTM_GETADDR" },
{ 0, NULL }
};
struct message nimsg_str[] 定义了需要使用的netlink消息类型集合的一个描述。以及对于描述字符串,在后续调试打印,比如_netlink_route_debug 等函数使用。
内核在rtnetlink初始化的时候注册了这些消息对于的处理函数:
void __init rtnetlink_init(void)
{
if (register_pernet_subsys(&rtnetlink_net_ops))
panic("rtnetlink_init: cannot initialize rtnetlink\n"); register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink,
rtnl_dump_ifinfo, rtnl_calcit);
rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL);
rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL); rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL);
rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL); rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL);
rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL);
rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
}
void __init ip_fib_init(void)
{
rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL); register_pernet_subsys(&fib_net_ops);
register_netdevice_notifier(&fib_netdev_notifier);
register_inetaddr_notifier(&fib_inetaddr_notifier); fib_trie_init();
}
ok,打开了内核的通路,下面就看看怎么在用户态的使用吧:
1、quagga的sock定义
struct nlsock
{
int sock;
int seq;
struct sockaddr_nl snl;
const char *name;
}; struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
2、创建socket
/* Make socket for Linux netlink interface. */
static int
netlink_socket(struct nlsock *nl, unsigned long groups, vrf_id_t vrf_id) {
int ret;
struct sockaddr_nl snl;
int sock;
int namelen;
int save_errno; if (zserv_privs.change(ZPRIVS_RAISE)) {
zlog(NULL, LOG_ERR, "Can't raise privileges");
return -;
} sock = vrf_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, vrf_id);
if (sock < ) {
zlog(NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
safe_strerror(errno));
return -;
} memset(&snl, , sizeof snl);
snl.nl_family = AF_NETLINK;
snl.nl_groups = groups; /* Bind the socket to the netlink structure for anything. */
ret = bind(sock, (struct sockaddr *)&snl, sizeof snl);
save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER)) zlog(NULL, LOG_ERR, "Can't lower privileges"); if (ret < ) {
zlog(NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s",
nl->name, snl.nl_groups, safe_strerror(save_errno));
close(sock);
return -;
} /* multiple netlink sockets will have different nl_pid */
namelen = sizeof snl;
ret = getsockname(sock, (struct sockaddr *)&snl, (socklen_t *)&namelen);
if (ret < || namelen != sizeof snl) {
zlog(NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name,
safe_strerror(errno));
close(sock);
return -;
} nl->snl = snl;
nl->sock = sock;
return ret;
}
groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
#ifdef HAVE_IPV6
groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
#endif /* HAVE_IPV6 */
netlink_socket(&zvrf->netlink, groups, zvrf->vrf_id);
netlink_socket(&zvrf->netlink_cmd, , zvrf->vrf_id);
/* Callback upon enabling a VRF. */
static int
zebra_vrf_enable(vrf_id_t vrf_id, void **info) {
struct zebra_vrf *zvrf = (struct zebra_vrf *)(*info); assert(zvrf); #if defined (HAVE_RTADV)
rtadv_init(zvrf);
#endif
kernel_init(zvrf);
interface_list(zvrf);
route_read(zvrf); return 0;
}
/* Zebra VRF initialization. */
static void
zebra_vrf_init(void) {
vrf_add_hook(VRF_NEW_HOOK, zebra_vrf_new);
vrf_add_hook(VRF_ENABLE_HOOK, zebra_vrf_enable);
vrf_add_hook(VRF_DISABLE_HOOK, zebra_vrf_disable);
vrf_init();
} 1 /* Initialize VRF module, and make kernel routing socket. */
zebra_vrf_init();
由zebra_vrf_init在main函数完成了netlink socket的初始化。
上面创建了2个socket。
1.netlink.sock用于接收内核主动发送的消息,比如动态路由:
/* Kernel route reflection. */
static int
kernel_read(struct thread *thread) {
struct zebra_vrf *zvrf = (struct zebra_vrf *)THREAD_ARG(thread);
netlink_parse_info(netlink_information_fetch, &zvrf->netlink, zvrf);
zvrf->t_netlink = thread_add_read(zebrad.master, kernel_read, zvrf,
zvrf->netlink.sock); return ;
}
注意:netlink_information_fetch函数接收内核发送过来的信息并做相应的处理。
内核的netlink消息发送函数:
/**
* nlmsg_notify - send a notification netlink message
* @sk: netlink socket to use
* @skb: notification message
* @portid: destination netlink portid for reports or 0
* @group: destination multicast group or 0
* @report: 1 to report back, 0 to disable
* @flags: allocation flags
*/
int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
unsigned int group, int report, gfp_t flags)
{
int err = ; if (group) {
int exclude_portid = ; if (report) {
atomic_inc(&skb->users);
exclude_portid = portid;
} /* errors reported via destination sk->sk_err, but propagate
* delivery errors if NETLINK_BROADCAST_ERROR flag is set */
err = nlmsg_multicast(sk, skb, exclude_portid, group, flags);
} if (report) {
int err2; err2 = nlmsg_unicast(sk, skb, portid);
if (!err || err == -ESRCH)
err = err2;
} return err;
}
2.netlink_cmd.sock用于向内核发送上面定义的消息请求:
/* Routing table read function using netlink interface. Only called
bootstrap time. */
int
netlink_route_read(struct zebra_vrf *zvrf) {
int ret; /* Get IPv4 routing table. */
ret = netlink_request(AF_INET, RTM_GETROUTE, &zvrf->netlink_cmd);
if (ret < ) return ret;
ret = netlink_parse_info(netlink_routing_table, &zvrf->netlink_cmd, zvrf);
if (ret < ) return ret; #ifdef HAVE_IPV6
/* Get IPv6 routing table. */
ret = netlink_request(AF_INET6, RTM_GETROUTE, &zvrf->netlink_cmd);
if (ret < ) return ret;
ret = netlink_parse_info(netlink_routing_table, &zvrf->netlink_cmd, zvrf);
if (ret < ) return ret;
#endif /* HAVE_IPV6 */ return ;
}
quagga还支持其他的内核交互接口,如ioctl,sysctl,procfs等等。
quagga源码分析--内核通信netlink的更多相关文章
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...
- Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...
- Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...
- v80.01 鸿蒙内核源码分析(内核态锁篇) | 如何实现快锁Futex(下) | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(内核态锁篇) | 如何实现快锁Futex(下) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...
- v87.01 鸿蒙内核源码分析 (内核启动篇) | 从汇编到 main () | 百篇博客分析 OpenHarmony 源码
本篇关键词:内核重定位.MMU.SVC栈.热启动.内核映射表 内核汇编相关篇为: v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的 v75.03 鸿蒙内核源码分析(汇编基础) | ...
- Linux内核源码分析--内核启动之(1)zImage自解压过程(Linux-3.0 ARMv7) 【转】
转自:http://blog.chinaunix.net/uid-25909619-id-4938388.html 研究内核源码和内核运行原理的时候,很总要的一点是要了解内核的初始情况,也就是要了解内 ...
- Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】
前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就 ...
- quagga源码分析--大内总管zebra
zebra,中文翻译是斑马,于是我打开了宋冬野的<斑马,斑马>作为BGM来完成这个篇章,嘿嘿,小资一把! zebra姑且戏称它是quagga项目的大内总管. 因为它负责管理其他所有协议进程 ...
- quagga源码分析--路由信息处理zebra-rib
对于各个协议生成的路由信息的处理属于quagga中非常重要的一个功能,如何在内核进行路由增加,更新,删除是一个复杂的过程. quagga在thread任务调度中加入了一种工作队列,work_queue ...
随机推荐
- leetcode第19题--Remove Nth Node From End of List
Problem: Given a linked list, remove the nth node from the end of list and return its head. For exam ...
- Office文档在线编辑的实现之一
因为项目的关系,研究了一下Office的在线编辑功能,写出来共享一下. Office xp之后的版本支持通过webdav协议(http的扩展)直接编辑服务器上的文件. IIS(6.0)支持webdav ...
- Jumony快速抓取网页
Jumony快速抓取网页 --- Jumony使用笔记--icode 作者:郝喜路 个人主页:http://www.cnicode.com 博客地址:http://haoxilu.c ...
- 使用POI 导入excel
引言:最近一直在接触excel的问题,网页也有很多关于POI解析excel的资料,我也简单的整理了下,有不对地方的还望及时指正,渴望与大家交流并学习. public int importExcel(F ...
- javaIO流实现读写txt文件
javaIO流实现文件读写 文件写入: InputStreamReader BufferedReader 文件读取: FileOutputStream package javatest.basic22 ...
- html+js+ashx+easyui+ado.net权限管理系统
分享一个html+js+ashx+easyui+ado.net权限管理系统 EasyUI.权限管理 这是个都快被搞烂了的组合,但是easyui的确好用,权限管理在项目中的确实用.一直以来博客园里也 ...
- 浅谈DevExpress<五>:TreeList简单的美化——自定义单元格,加注释以及行序号
今天就以昨天的列表为例,实现以下效果:预算大于110万的单元格突出显示,加上行序号以及注释,如下图:
- linux下sshd_config的StrictModes参数
今天在两台机器A和B上设置免密码登录,A机是Centos6.5,B机是Centos7,我想通过A机免密码登录到B机,在两台机器上设置好公钥和私钥后,在A机上通过ssh连接B机,每次都是要让我手动输入密 ...
- Task.WhileAll扩展方法
TPL实现Task.WhileAll扩展方法 文章翻译整理自 Nikola Malovic 两篇博文: Task.WhileAll Awaitable task progress reporting ...
- 使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 6 - 业务逻辑
翻译:使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 6 - 业务逻辑 Part 3: 设计逻辑层:核心开发 如前所述,我们的解决方案 ...