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 ...
随机推荐
- C++中的左值和右值
左值和右值的定义 在C++中,能够放到赋值操作符=左边的是左值,能够放到赋值操作符右边的是右值.有些变量既能够当左值又能够当右值.进一步来讲,左值为Lvalue,事实上L代表Location,表示在内 ...
- firefox os 该设备呼叫移动开发
1)话筒 权限:telephony api:navigator.moztelephony 参考链接:https://wiki.mozilla.org/WebAPI/WebTelephony 2) 扬声 ...
- iOS_1_加法器
: BeyondViewController.h // // BeyondViewController.h // 01_calc // // Created by beyond on 14-7-20. ...
- Android--扫描二维码
http://www.cnblogs.com/keyindex/archive/2011/06/08/2074900.html
- HTTP压缩算法SDCH
程序设计中使用的那些共享方法或者技术 前段时间看了个paper是讲述谷歌浏览器使用的压缩方法SDCH,其实原理还是比较简单的. 看了论文后就想总结一下程序中使用的一些共享方法或者技术吧. 1.Goog ...
- FineUI开源版之TreeGrid实现
FineUI开源版是没有树表格的,但是又需要,怎么办呢?在博客园看到一位大大的文章 http://www.cnblogs.com/shiworkyue/p/4211002.html 然后参考,不知道为 ...
- url在线编码和解码
在工作中,经常遇到encode之后的url.想查看里面的某个参数的时候,很不直观.今天在网上搜了一下对url在线编码和解码的网站.对我来说,使用起来很方便.而且这个网站里面,不仅仅有对url的编码和解 ...
- 小菜的SharePoint Tips
07. 设计模式应用案例(下) 前文用C#代码实现了Facade模式.Adapter模式.Strategy模式.Bridge模式和Abstract Factory模式解决实际业务需求.本文将继续以C# ...
- inet_aton()
两次技术面试都被让c语言实现inet_aton()函数 看来这个函数真的很重要. 我先贴上我自己的实现代码 #include <stdio.h> #include <math.h&g ...
- [网络编程]VS2010+OpenSSL安装与初步了解
OpenSSL简介 功能作用:SSL(Secure Socket Layer)是netscape公司提出的主要用于web的安全通信标准,分为2.0版和3.0版.TLS(Transport Layer ...