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 ...
随机推荐
- 安装Windows8.1操作系统 - 初学者系列 - 学习者系列文章
Windows 8这款操作系统是微软最新的操作系统.虽然微软做了推广,但是据消息称市场份额暂时没那么高.下面就对该操作系统的安装进行简要介绍. 1. 将光盘装入光驱,设置BIOS中光驱启动,启动计算 ...
- 实例学习SSIS(五)--理论介绍SSIS
原文:实例学习SSIS(五)--理论介绍SSIS 导读: 实例学习SSIS(一)--制作一个简单的ETL包 实例学习SSIS(二)--使用迭代 实例学习SSIS(三)--使用包配置 实例学习SSIS( ...
- linux服务创建及jq配置服务列表查看
1.应用背景 随着业务需求,后台处理服务不断增多,对于这些服务或后台程序的查看.更新操作越来越凌乱,所以我们首先需要一个服务列表查看工具,方便查看各 服务的端口.运行状态.jar包路径等等. 2.创建 ...
- java 权限 部分截图
下载地址:http://download.csdn.net/detail/heyehuang/5857263
- NET实现仓库看板的一些感想
NET实现仓库看板的一些感想 从一名技术开发人员到实施人员的蜕变,从不同的角度看待同一个问题,或许会有不一样的结果.这里记录一下最近一个项目实施的案例,非常有感触! 一. 项目情况简介 本次项目是给一 ...
- Binder机制,从Java到C (5. IBinder对象传递形式)
1.IBinder的传递 Binder IPC通信中,Binder是通信的媒介,Parcel是通信的內容.远程调用过程中,其参数都被打包成Parcel的形式來传递.IBinder对象当然也不例外,在前 ...
- [Usaco2008 Dec]Patting Heads 轻拍牛头[筛法]
Description 今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏. 贝茜让N(1≤N≤100000)头奶牛坐成一个圈.除了1号与N号奶牛外,i号奶牛与i-l号和i+l号奶 ...
- SublimeText编辑器替代notepad++了
可以考虑使用SublimeText编辑器替代notepad++了 内容目录: 插件安装配置 配置打包下载 大概是去年吧,这款编辑器神一般的出现在我面前,经过我小心翼翼的试用后发现并不是那么太顺手, ...
- [转]Introduction to Core Bluetooth: Building a Heart Rate Monitor
ref:http://www.raywenderlich.com/52080/introduction-core-bluetooth-building-heart-rate-monitor The C ...
- 教你使用shell数组
数组的使用,需要掌握 1.对数组进行赋值 2.通过下标访问数组元素 3.循环遍历所有的元素 代码如下: #!/bin/bash a="39" b="5" c=& ...