Linux 用户态与内核态的交互
  在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络管理工具,它与内核的交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通 读,也在最新版本中改变为netlink,无疑,它将是Linux用户态与内核态交流的主要方法之一。它的通信依据是一个对应于进程的标识,一般定为该进 程的 ID。当通信的一端处于中断过程时,该标识为 0。当使用 netlink 套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。但通信双方有一端是中断过程,使用方法则不同。netlink 套接字的最大特点是对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函 数。
《UNIX Network Programming Volume 1 - 3rd Edition》第18章
讲到BSD UNIX系统中routing socket的应用,这种套接字是按下面方式生成的:
rt_socket = socket(AF_ROUTE, SOCK_RAW, 0);
然 后就可以用它跟内核交互,进行网络环境管理的操作,如读取/设置/删除路由表信息,更改网关等等,但书中所列代码只在4.3BSD及以后版本的原始 UNIX系统下可用,Linux虽然实现了AF_ROUTE族套接字,但用法却完全不同。由于网上这方面知识的资料想对匮乏,现对Linux下 routing socket的使用做一介绍。
由于我现在在Magic Linux1.0下工作,所以以下的讲解全部基于2.4.10内核。Linux从v2.2开始引入这一机制,因此可以肯定从v2.2到v2.4的内核都是适用的,更新的v2.6我没有试过。

Linux下虽然也有AF_ROUTE族套接字,但是这个定义只是个别名,请看
/usr/include/linux/socket.h, line 145:
#define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */
可 见在Linux内核当中真正实现routing socket的是AF_NETLINK族套接字。AF_NETLINK族套接字像一个连接用户空间和内核的双工管道,通过它,用户进程可以修改内核运行参 数、读取和设置路由信息、控制特定网卡的up/down状态等等,可以说是一个管理网络资源的绝佳途径。

1 生成所需套接字,并绑定一个sockaddr结构

先来看如何生成一个AF_NETLINK族套接字:
sockfd = socket(AF_NETLINK, socket_type, netlink_faimly);
这里socket_type可选SOCK_DGRAM或SOCK_RAW;因为AF_NETLINK族是面向数据报的套接字,所以不能使用SOCK_STREAM。
netlink_family指定要和内核中的哪个子系统进行交互,目前支持:
NETLINK_ROUTE 与路由信息相关,包括查询、设置和删除路由表中的条目等。待会儿我们将以这类family举个实际的例子;
NETLINK_FIREWALL 接收由IPv4防火墙代码发送的包;
NETLINK_ARPD 可以在用户空间进行arp缓存的管理;
NETLINK_ROUTE6 在用户空间发送和接收路由表信息更新;
还有几种虽然没有实现,但已经有了定义,为以后扩展做好了准备。

接下来要给该套接字绑定一个sockaddr结构,实际上是一个sockaddr_nl结构:
struct sockaddr_nl {
sa_family_t nl_family; /*AF_NETLINK*/
unsigned short nl_pad; /* 0 */
pid_t nl_pid; /* 进程pid */
u_32 nl_groups; /* 多播组掩码*/
}nl;
这个结构一般按照注释填好就可以了,nl_groups我也不知道怎么用,一般填零了,表示没有多播。绑定:
bind(sockfd, (struct sockaddr*) &nl, sizeof(nl));

2 填充所需数据结构,并通过sendmsg()/send()等函数写到套接字里去

到 此为止,与内核通信的准备工作就完成了,下面要做的工作是,选取适当的数据结构进行填充,并作为sendmsg()的参数发送出去,并recv()收到的 消息。这个数据结构就是nlmsghdr,它只是一个信息头,后面可以接任意长的数据,这些数据实际上又是针对某一需求所采用的特定数据结构。先来看 nlmsghdr:
struct nlmsghdr {
_u32 nlmsg_len; /* Length of msg including header */
_u32 nlmsg_type; /* 操作命令 */
_u16 nlmsg_flags; /* various flags */
_u32 nlmsg_seq; /* Sequence number */
_u32 nlmsg_pid; /* 进程PID */
};
/* 紧跟着是实际要发送的数据,长度可以任意 */

nlmsg_type 决定这次要执行的操作,如查询当前路由表信息,所使用的就是RTM_GETROUTE。标准nlmsg_type包括:NLMSG_NOOP, NLMSG_DONE, NLMSG_ERROR等。根据采用的nlmsg_type不同,还要选取不同的数据结构来填充到nlmsghdr后面:
操作 数据结构
RTM_NEWLINK ifinfomsg
RTM_DELLINK
RTM_GETLINK
RTM_NEWADDR ifaddrmsg
RTM_DELADDR
RTM_GETADDR
RTM_NEWROUTE rtmsg
RTM_DELROUTE
RTM_GETROUTE
RTM_NEWNEIGH ndmsg/nda_chcheinfo
RTM_DELNEIGH
RTM_GETNEIGH
RTM_NEWRULE rtmsg
RTM_DELRULE
RTM_GETRULE
RTM_NEWQDISC tcmsg
RTM_DELQDISC
RTM_GETQDISC
RTM_NEWTCLASS tcmsg
RTM_DELTCLASS
RTM_GETTCLASS
RTM_NEWTFILTER tcmsg
RTM_DELTFILTER
RTM_GETTFILTER
由于情形众多,我从现在开始将用一个特定的例子来说明问题。我们的目的是从内核读取IPV4路由表信息。从上面表看,nlmsg_type一定使用RTM_xxxROUTE操作,对应的数据结构是rtmsg。既然是读取,那么应该是RTM_GETROUTE了。
struct rtmsg {
unsigned char rtm_family; /* 路由表地址族 */
unsigned char rtm_dst_len; /* 目的长度 */
unsigned char rtm_src_len; /* 源长度 */ (2.4.10头文件的注释标反了?)
unsigned char rtm_tos; /* TOS */

unsigned char rtm_table; /* 路由表选取 */
unsigned char rtm_protocol; /* 路由协议 */
unsigned char rtm_scope;
unsigned char rtm_type;

unsigned int rtm_flags;
};
对于RTM_GETROUTE操作来说,我们只需指定两个成员:rtm_family:AF_INET, rtm_table: RT_TABLE_MAIN。其他成员都初始化为0即可。将这个结构体跟nlmsghdr结合起来,得到我们自己的新结构体:
struct {
struct nlmsghdr nl;
struct rtmsg rt;
}req;
填充好rt结构之后,还要调整nl结构相应成员的值。Linux定义了多个宏来处理nlmsghdr成员的值,我们这里用到的是NLMSG_LENGTH(size_t len);
req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
这将计算nlmsghdr长度与rtmsg长度的和(其中包括了将rtmsg进行4字节边界对齐的调整),并存储到nlmsghdr的nlmsg_len成员中。接下来要做的就是将这个新结构体req放到sendmsg()函数的msghdr.iov处,并调用函数。
sendmsg(sockfd, &msg, 0);

3 接收数据,并进行分析

接下来的操作是recv()操作,从该套接字读取内核返回的数据,并进行分析处理。
recv(sockfd, p, sizeof(buf) - nll, 0);
其中p是指向一个缓冲区buf的指针,nll是已接收到的nlmsghdr数据的长度。
由于内核返回信息是一个字节流,需要调用者检查消息结尾。这是通过检查返回的nlmsghdr的nlmsg_type是否等于NLMSG_DONE来完成的。返回的数据格式如下:
-----------------------------------------------------------
| nlmsghdr+route entry | nlmsghdr+route entry | ......... 
-----------------------------------------------------------
| 解出route entry
V
-----------------------------------------------------------
| dst_addr | gateway | Output interface| ...............
-----------------------------------------------------------
可 以看出,返回消息由多个(nlmsghdr + route entry)组成,当某个nlmsghdr的nlmsg_type == NLMSG_DONE时就表示信息输出已经完毕。而每一个route entry由多个rtattr结构体组成,每个结构体表示该路由项的某个属性,如目的地址,网关等等。根据这个示意图我们就能够轻松解析需要的数据了。

Linux 用户态与内核态的交互【转载】的更多相关文章

  1. Linux探秘之用户态与内核态

    一. Unix/Linux的体系架构 如上图所示,从宏观上来看,Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核).内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程 ...

  2. 【Linux 系统】Linux探秘之用户态与内核态

    一. Unix/Linux的体系架构 如上图所示,从宏观上来看,Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核).内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程 ...

  3. linux之用户态和内核态

    一. Unix/Linux的体系架构 如上图所示,从宏观上来看,Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核).内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程 ...

  4. linux用户态和内核态通信之netlink机制【转】

    本文转载自:http://blog.csdn.net/zcabcd123/article/details/8272360 这是一篇学习笔记,主要是对<Linux 系统内核空间与用户空间通信的实现 ...

  5. Linux用户态与内核态通信的几种方式

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Linux 用 ...

  6. Linux用户态和内核态

    究竟什么是用户态,什么是内核态,这两个基本概念以前一直理解得不是很清楚,根本原因个人觉得是在于因为大部分时候我们在写程序时关注的重点和着眼的角度放在了实现的功能和代码的逻辑性上,先看一个例子: 1)例 ...

  7. Linux 用户态和内核态

    1.特权级特权级用来管理和控制程序执行.如Intel x86架构的CPU,有0~3四个特权级,0级最高,3级最低.硬件在执行每条指令时都会检查指令具有的特权级.硬件提供了特权级使用机制,对操作系统来说 ...

  8. 【转载】 Linux用户态和内核态

    [说明]转载自 http://my.oschina.net/liubin/blog/27795 究竟什么是用户态,什么是内核态,这两个基本概念以前一直理解得不是很清楚,根本原因个人觉得是在于因为大部分 ...

  9. Linux操作系统学习_用户态与内核态之切换过程

    因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...

随机推荐

  1. MVC 中创建简单过滤器

    1.新建一个类,继承自 ActionFilterAttribute类,并重写OnActionExecuting()方法 public class LoginFilter:ActionFilterAtt ...

  2. 【bzoj5133】[CodePlus2017年12月]白金元首与独舞 并查集+矩阵树定理

    题目描述 给定一个 $n\times m$ 的方格图,每个格子有 ↑.↓.←.→,表示从该格子能够走到相邻的哪个格子.有一些格子是空着的,需要填上四者之一,需要满足:最终的方格图中,从任意一个位置出发 ...

  3. 多线程---handlerthread

    当我们需要工作线程来操作的时候,很多时候会有同步问题,UI更新问题. Handle机制就是为了解决这个问题而产生的. android允许每个线程都有自己的消息队列,同时也可以是主线程消息队列. 但是很 ...

  4. Qt5 UI信号、槽自动连接的控件重名

    Qt5 UI信号.槽自动连接的控件重名 来源 http://blog.csdn.net/goldenhawking/article/details/51865909 对Qt5稍有熟悉的童鞋都知道信号. ...

  5. llinux 安装oracle

    5.传输oracle的客户端(600几M linux.x64_11gR2_client).oracle的依赖包(20几M packages),linux只支持zip(rar较困难不做解释) 6.打开l ...

  6. 【JQuery】数据

    一.前言        接着前一章的内容,继续本章的学习 二.内容 queue 显示或操作在匹配元素上执行的函数队列 .queue(queueName) 操作在匹配元素上执行的函数队列 .queue( ...

  7. Asp.Net保存session的三种方法 (Dll文件更新导致session丢失的解决办法)

    1. InProc模式(默认值):asp.net将session保存到当前进程中,这种方式最快,但是不能多台服务器共享session,且会话状态数据容易丢失. <sessionState mod ...

  8. 【recording】gdoi2018

    怎么说..虽然感觉其实..不太想写游记.. 但是回来看着桌面上的课本还是忍不住了(想想班里进度就..qwq)还是写一下吧 Day x(x<0) 之前大家溜到首都开心集训了一波然后被虐的很开心.. ...

  9. 图像灰度化方法总结及其VC实现

    http://blog.csdn.net/likezhaobin/article/details/6915754 最近一段时间作者开始进行运动目标识别定位系统设计,本文以及后续的几篇文章都是从一个图像 ...

  10. bzoj 1588 平衡树 splay

    1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 15446  Solved: 6076[Submit][Sta ...