- 浏览: 1468200 次
- 性别:
- 来自: 郑州
文章分类
最新评论
-
getelephantbyid:
make 无法通过.....
php-5.3,php-5.4的thttpd2.25b补丁,及编译方法 -
getelephantbyid:
patch -p1 ../php-5.4.7_thttpd-2 ...
php-5.3,php-5.4的thttpd2.25b补丁,及编译方法 -
zander:
zander 写道c 语言是静态类型语言还是动态类型语言阅读理 ...
什么是动态语言和静态语言? -
zander:
c 语言是静态类型语言还是动态类型语言
什么是动态语言和静态语言? -
lunajiayou:
很有道理,赞一个
跟着苍蝇会找到厕所,跟着蜜蜂会找到花朵
转至:http://bbs.chinaunix.net/thread-2162796-1-1.html
作者:Kendo
2006-9-3
这是一篇学习笔记,主要是对《Linux 系统
内核空间与用户空间通信的实现与分析》中的源码imp2的分析。其中的源码,可以到以下URL下载:
http://www-128.ibm
.com/developerworks/cn/linux
/l-netlink/imp2.tar.gz
参考文档
《Linux 系统内核空间与用户空间通信的实现与分析》 陈鑫
http://www-128.ibm.com/developerworks/cn/linux/l-netlink/?ca=dwcn-newsletter-linux
《在 Linux 下用户空间与内核空间数据交换的方式》 杨燚
http://www-128.ibm.com/developerworks/cn/linux/l-kerns-usrs/
理论篇
在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络
管
理工具,它与内核的交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通读,也在最新版本中改变为netlink,
无疑,它将是Linux用户态与内核态交流的主要方法之一。它的通信依据是一个对应于进程的标识,一般定为该进程的
ID。当通信的一端处于中断过程时,该标识为 0。当使用 netlink
套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。但通信双方有一端是中断过程,使用方法则不同。netlink
套接字的最大特点是对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函
数。工作原理如图:
如图所示,这里使用了软中断而不是内核线程来接收数据,这样就可以保证数据接收的实时性。
当 netlink 套接字用于内核空间与用户空间的通信时,在用户空间的创建方法和一般套接字使用类似,但内核空间的创建方法则不同,下图是 netlink 套接字实现此类通信时创建的过程:
用户空间
用户态应用
使用标准的socket与内核通讯,标准的socket API 的函数, socket(), bind(), sendmsg(), recvmsg() 和 close()很容易地应用到 netlink socket。
为了创建一个 netlink socket,用户需要使用如下参数调用 socket():
- socket(AF_NETLINK, SOCK_RAW, netlink_type)
netlink对应的协议簇是 AF_NETLINK,第二个参数必须是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型:
- #define NETLINK_ROUTE 0 /* Routing/device hook */
- #define NETLINK_W1 1 /* 1-wire subsystem */
- #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
- #define NETLINK_FIREWALL 3 /* Firewalling hook */
- #define NETLINK_INET_DIAG 4 /* INET socket monitoring */
- #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
- #define NETLINK_XFRM 6 /* ipsec */
- #define NETLINK_SELINUX 7 /* SELinux event notifications */
- #define NETLINK_ISCSI 8 /* Open-iSCSI */
- #define NETLINK_AUDIT 9 /* auditing */
- #define NETLINK_FIB_LOOKUP 10
- #define NETLINK_CONNECTOR 11
- #define NETLINK_NETFILTER 12 /* netfilter subsystem */
- #define NETLINK_IP6_FW 13
- #define NETLINK_DNRTMSG 14 /* DECnet routing messages */
- #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
同样地,socket函数返回的套接字,可以交给bing等函数调用:
- static int skfd;
- skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2);
- if(skfd < 0)
- {
- printf("can not create a netlink socket\n");
- exit(0);
- }
bind函数需要绑定协议地址,netlink的socket地址使用struct sockaddr_nl结构描述:
- struct sockaddr_nl
- {
- sa_family_t nl_family;
- unsigned short nl_pad;
- __u32 nl_pid;
- __u32 nl_groups;
- };
成员 nl_family为协议簇 AF_NETLINK,成员 nl_pad 当前没有使用,因此要总是设置为 0,成员 nl_pid
为接收或发送消息的进程的 ID,如果希望内核处理消息或多播消息,就把该字段设置为 0,否则设置为处理消息的进程 ID。成员 nl_groups
用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组:
- struct sockaddr_nl local;
- memset(&local, 0, sizeof(local));
- local.nl_family = AF_NETLINK;
- local.nl_pid = getpid(); /*设置pid为自己的pid值*/
- local.nl_groups = 0;
- /*绑定套接字*/
- if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0)
- {
- printf("bind() error\n");
- return -1;
- }
用户空间可以调用send函数簇向内核发送消息,如sendto、sendmsg等,同样地,也可以使用struct sockaddr_nl来描述一个对端地址,以待send函数来调用,与本地地址稍不同的是,因为对端为内核,所以nl_pid成员需要设置为0:
- struct sockaddr_nl kpeer;
- memset(&kpeer, 0, sizeof(kpeer));
- kpeer.nl_family = AF_NETLINK;
- kpeer.nl_pid = 0;
- kpeer.nl_groups = 0;
另一个问题就是发内核发送的消息的组成,使用我们发送一个IP网络数据包的话,则数据包结构为“IP包头+IP数据”,同样地,netlink的消息结构是“netlink消息头部+数据”。Netlink消息头部使用struct nlmsghdr结构来描述:
- struct nlmsghdr
- {
- __u32 nlmsg_len; /* Length of message */
- __u16 nlmsg_type; /* Message type*/
- __u16 nlmsg_flags; /* Additional flags */
- __u32 nlmsg_seq; /* Sequence number */
- __u32 nlmsg_pid; /* Sending process PID */
- };
字段 nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小,一般地,我们使用netlink提供的宏NLMSG_LENGTH来计算这个长度,仅需向NLMSG_LENGTH宏提供要发送的数据的长度,它会自动计算对齐后的总长度:
- /*计算包含报头的数据报长度*/
- #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
- /*字节对齐*/
- #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
后面还可以看到很多netlink提供的宏,这些宏可以为我们编写netlink宏提供很大的方便。
字段 nlmsg_type 用于应用内部定义消息的类型,它对 netlink 内核实现是透明的,因此大部分情况下设置为 0,字段
nlmsg_flags 用于设置消息标志,对于一般的使用,用户把它设置为 0 就可以,只是一些高级应用(如 netfilter 和路由
daemon 需要它进行一些复杂的操作),字段 nlmsg_seq 和 nlmsg_pid 用于应用追踪消息,前者表示顺序号,后者为消息来源进程
ID。
- struct msg_to_kernel /*自定义消息首部,它仅包含了netlink的消息首部*/
- {
- struct nlmsghdr hdr;
- };
- struct msg_to_kernel message;
- memset(&message, 0, sizeof(message));
- message.hdr.nlmsg_len = NLMSG_LENGTH(0); /*计算消息,因为这里只是发送一个请求消息,没有多余的数据,所以,数据长度为0*/
- message.hdr.nlmsg_flags = 0;
- message.hdr.nlmsg_type = IMP2_U_PID; /*设置自定义消息类型*/
- message.hdr.nlmsg_pid = local.nl_pid; /*设置发送者的PID*/
- 这样,有了本地地址、对端地址和发送的数据,就可以调用发送函数将消息发送给内核了:
- /*发送一个请求*/
- sendto(skfd, &message, message.hdr.nlmsg_len, 0,
- (struct sockaddr*)&kpeer, sizeof(kpeer));
当发送完请求后,就可以调用recv函数簇从内核接收数据了,接收到的数据包含了netlink消息首部和要传输的数据:
- /*接收的数据包含了netlink消息首部和自定义数据结构*/
- struct u_packet_info
- {
- struct nlmsghdr hdr;
- struct packet_info icmp_info;
- };
- struct u_packet_info info;
- while(1)
- {
- kpeerlen = sizeof(struct sockaddr_nl);
- /*接收内核空间返回的数据*/
- rcvlen = recvfrom(skfd, &info, sizeof(struct u_packet_info),
- 0, (struct sockaddr*)&kpeer, &kpeerlen);
-
- /*处理接收到的数据*/
- ……
- }
同样地,函数close用于关闭打开的netlink socket。程序中,因为程序一直循环接收处理内核的消息,需要收到用户的关闭信号才会退出,所以关闭套接字的工作放在了自定义的信号函数sig_int中处理:
- /*这个信号函数,处理一些程序退出时的动作*/
- static void sig_int(int signo)
- {
- struct sockaddr_nl kpeer;
- struct msg_to_kernel message;
- memset(&kpeer, 0, sizeof(kpeer));
- kpeer.nl_family = AF_NETLINK;
- kpeer.nl_pid = 0;
- kpeer.nl_groups = 0;
- memset(&message, 0, sizeof(message));
- message.hdr.nlmsg_len = NLMSG_LENGTH(0);
- message.hdr.nlmsg_flags = 0;
- message.hdr.nlmsg_type = IMP2_CLOSE;
- message.hdr.nlmsg_pid = getpid();
- /*向内核发送一个消息,由nlmsg_type表明,应用程序
将关闭*/
- sendto(skfd, &message, message.hdr.nlmsg_len, 0, (struct sockaddr *)(&kpeer), sizeof(kpeer));
- close(skfd);
- exit(0);
- }
这个结束函数中,向内核发送一个“我已经退出了”的消息,然后调用close函数关闭netlink套接字,退出程序。
内核空间
与应用程序内核,内核空间也主要完成三件工作:
n 创建netlink套接字
n 接收处理用户空间发送的数据
n 发送数据至用户空间
API函数netlink_kernel_create用于创建一个netlink socket,同时,注册一个回调函数,用于接收处理用户空间的消息:
- struct sock *
- netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
参数unit表示netlink协议类型,如NL_IMP2,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个
netlink
socket时,该input函数指针就会被引用。函数指针input的参数sk实际上就是函数netlink_kernel_create返回的
struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct
sock结构来表示。
- static int __init init(void)
- {
- rwlock_init(&user_proc.lock); /*初始化读写锁*/
- /*创建一个netlink socket,协议类型是自定义的ML_IMP2,kernel_reveive为接受处理函数*/
- nlfd = netlink_kernel_create(NL_IMP2, kernel_receive);
- if(!nlfd) /*创建失败*/
- {
- printk("can not create a netlink socket\n");
- return -1;
- }
- /*注册一个Netfilter 钩子*/
- return nf_register_hook(&imp2_ops);
- }
- module_init(init);
用户空间向内核发送了两种自定义消息类型:IMP2_U_PID和IMP2_CLOSE,分别是请求和关闭。kernel_receive 函数分别处理这两种消息:
- DECLARE_MUTEX(receive_sem); /*初始化信号量*/
- static void kernel_receive(struct sock *sk, int len)
- {
- do
- {
- struct sk_buff *skb;
- if(down_trylock(&receive_sem)) /*获取信号量*/
- return;
- /*从接收队列中取得skb,然后进行一些基本的长度的合法性校验*/
- while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
- {
- {
- struct nlmsghdr *nlh = NULL;
-
- if(skb->len >= sizeof(struct nlmsghdr))
- {
- /*获取数据中的nlmsghdr 结构的报头*/
- nlh = (struct nlmsghdr *)skb->data;
- if((nlh->nlmsg_len >= sizeof(struct nlmsghdr))
- && (skb->len >= nlh->nlmsg_len))
- {
- /*长度的全法性校验完成后,处理应用程序自定义消息类型,主要是对用户PID的保存,即为内核保存“把消息发送给谁”*/
- if(nlh->nlmsg_type == IMP2_U_PID) /*请求*/
- {
- write_lock_bh(&user_proc.pid);
- user_proc.pid = nlh->nlmsg_pid;
- write_unlock_bh(&user_proc.pid);
- }
- else if(nlh->nlmsg_type == IMP2_CLOSE) /*应用程序关闭*/
- {
- write_lock_bh(&user_proc.pid);
- if(nlh->nlmsg_pid == user_proc.pid)
- user_proc.pid = 0;
- write_unlock_bh(&user_proc.pid);
- }
- }
- }
- }
- kfree_skb(skb);
- }
- up(&receive_sem); /*返回信号量*/
- }while(nlfd && nlfd->receive_queue.qlen);
- }
因为内核模块可能同时被多个进程同时调用,所以函数中使用了信号量和锁来进行互斥。skb =
skb_dequeue(&sk->receive_queue)用于取得socket sk的接收队列上的消息,返回为一个struct
sk_buff的结构,skb->data指向实际的netlink消息。
程序中注册了一个Netfilter钩子,钩子函数是get_icmp,它截获ICMP数据包,然后调用send_to_user函数将数据发送给应用空
间进程。发送的数据是info结构变量,它是struct packet_info结构,这个结构包含了来源/目的地址两个成员。Netfilter
Hook不是本文描述的重点,略过。
send_to_user 用于将数据发送给用户空间进程,发送调用的是API函数netlink_unicast 完成的:
- int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);
参数sk为函数netlink_kernel_create()返回的套接字,参数skb存放待发送的消息,它的data字段指向要发送的netlink
消息结构,而skb的控制块保存了消息的地址信息,
参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函
数在没有接收缓存可利用时睡眠。
向用户空间进程发送的消息包含三个部份:netlink
消息头部、数据部份和控制字段,控制字段包含了内核发送netlink消息时,需要设置的目标地址与源地址,内核中消息是通过sk_buff来管理的,
linux/netlink.h中定义了NETLINK_CB宏来方便消息的地址设置:
- #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
例如:
- NETLINK_CB(skb).pid = 0;
- NETLINK_CB(skb).dst_pid = 0;
- NETLINK_CB(skb).dst_group = 1;
字段pid表示消息发送者进程ID,也即源地址,对于内核,它为 0, dst_pid 表示消息接收者进程
ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group
应当设置为 0。
- static int send_to_user(struct packet_info *info)
- {
- int ret;
- int size;
- unsigned char *old_tail;
- struct sk_buff *skb;
- struct nlmsghdr *nlh;
- struct packet_info *packet;
- /*计算消息总长:消息首部加上数据加度*/
- size = NLMSG_SPACE(sizeof(*info));
- /*分配一个新的套接字缓存*/
- skb = alloc_skb(size, GFP_ATOMIC);
- old_tail = skb->tail;
- /*初始化一个netlink消息首部*/
- nlh = NLMSG_PUT(skb, 0, 0, IMP2_K_MSG, size-sizeof(*nlh));
- /*跳过消息首部,指向数据区*/
- packet = NLMSG_DATA(nlh);
- /*初始化数据区*/
- memset(packet, 0, sizeof(struct packet_info));
- /*填充待发送的数据*/
- packet->src = info->src;
- packet->dest = info->dest;
- /*计算skb两次长度之差,即netlink的长度总和*/
- nlh->nlmsg_len = skb->tail - old_tail;
- /*设置控制字段*/
- NETLINK_CB(skb).dst_groups = 0;
- /*发送数据*/
- read_lock_bh(&user_proc.lock);
- ret = netlink_unicast(nlfd, skb, user_proc.pid, MSG_DONTWAIT);
- read_unlock_bh(&user_proc.lock);
- }
函数初始化netlink 消息首部,填充数据区,然后设置控制字段,这三部份都包含在skb_buff中,最后调用netlink_unicast函数把数据发送出去。
函数中调用了netlink的一个重要的宏NLMSG_PUT,它用于初始化netlink 消息首部:
- #define NLMSG_PUT(skb, pid, seq, type, len) \
- ({ if (skb_tailroom(skb) < (int)NLMSG_SPACE(len)) goto nlmsg_failure; \
- __nlmsg_put(skb, pid, seq, type, len); })
- static __inline__ struct nlmsghdr *
- __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len)
- {
- struct nlmsghdr *nlh;
- int size = NLMSG_LENGTH(len);
- nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
- nlh->nlmsg_type = type;
- nlh->nlmsg_len = size;
- nlh->nlmsg_flags = 0;
- nlh->nlmsg_pid = pid;
- nlh->nlmsg_seq = seq;
- return nlh;
- }
这个宏一个需要注意的地方是调用了nlmsg_failure标签,所以在程序中应该定义这个标签。
在内核中使用函数sock_release来释放函数netlink_kernel_create()创建的netlink socket:
- void sock_release(struct socket * sock);
程序在退出模块中释放netlink sockets和netfilter hook:
- static void __exit fini(void)
- {
- if(nlfd)
- {
- sock_release(nlfd->socket); /*释放netlink socket*/
- }
- nf_unregister_hook(&imp2_ops); /*撤锁netfilter 钩子*/
- }
发表评论
-
使用strongswan建立基于ikev2 eap-mschapv2的ipsec服务器
2017-04-17 23:14 3208sudo apt-get install strongsw ... -
使用strongswan/xl2tpd建立ipsec/l2tp服务器
2017-04-17 22:32 6163sudo apt-get install strongsw ... -
SecureFX中文件名乱码的解决
2014-08-28 03:23 3354原始贴子:https://forums.vandyke.c ... -
为Linux编译atheros ar1111(设备ID:168c:0037,AW-NB100H – AR5B225 Atheros half size)网卡驱动
2012-07-15 22:57 4597买了个zotac h61itx-a-e wifi主板,从zot ... -
PHY管理接口(MDIO)
2012-01-17 17:01 4216对吉比特以太网而言,串行通信总线称为管理数据输入输出 (MDI ... -
理解ipsec身份标识和认证选项
2012-01-11 15:42 6870This article is part of the Ide ... -
netfiletr和iptables的状态和连接跟踪机制
2012-01-11 15:38 3343Como se lleva a cabo el rastreo ... -
编译安装iw
2011-11-09 13:31 2375ubuntu安装build-essentials libnl- ... -
从ip addr add和ifconfig的区别看linux网卡ip地址的结构
2011-09-24 13:06 1670转至:http://blog.csdn.net/dog25 ... -
DLNA中的UPnP技术浅析
2011-09-22 18:39 5072说到DLNA,UPn ... -
netlink与rtnetlink(二)
2011-09-19 01:36 15984转至:http://blogold.chinaunix.net ... -
netlink和rtnetlink(一)
2011-09-19 01:35 5228转到:http://blogold.chinaunix.net ... -
Linux——Netlink
2011-09-19 01:24 9656转载:http://blog.csdn.net/firo_ba ... -
linux notification chains
2011-08-13 00:26 1053linux内核由各个不同的子系统构成,比如网络子系统、存储 ... -
内核中的notification chain浅析
2011-08-13 00:25 1375内核中的很多子系统都是联系很紧密的,因此有可能某个子系统的某些 ... -
Linux Notification chains
2011-08-13 00:24 2845Notifier是Linux 中提供一种在内核子系统 中共 ... -
printk 使用方法
2011-08-12 22:28 9884内核通过 printk() 输出的信息具有日志级别,日志级 ... -
WEXT/mac80211/nl80211/cfg80211
2011-07-29 02:32 11255Wireless-Extensions--旧的无 ... -
fedora上wpa_supplicant上网配置
2011-07-29 01:28 35351,vi /etc/sysconfig/wpa_supplic ... -
vi替换命令用法详解
2011-07-19 01:06 2196vi替换命令用法详解 : ranges /pat1/ ...
相关推荐
在不依赖udev这样的动态设备管理系统的情况下,可以通过内核与用户空间之间的通信机制——netlink来实现USB热插拔事件的处理。本文将深入探讨如何利用netlink机制实现这一功能,并获取USB设备的相关信息,如厂商编号...
与之前的Linux内核版本相比,2.6内核在网络协议栈中实现了对IPSec的“无缝”集成。这意味着,无论是发送还是接收数据包,内核都能够自动地识别并处理IPSec相关的安全措施,无需额外的配置或中间件干预。 **安全关联...
- **设备与内核间的交互**:讨论网卡硬件如何与Linux内核相互作用。 - **初始化选项**:介绍可用于定制设备初始化过程的各种选项。 - **模块选项**:解释如何通过传递参数给模块来进一步自定义网络设备的行为。 ...
另一个是通用的部分,遵循统一的网络驱动接口,如Netlink接口,使得内核可以与驱动程序通信。这种设计模式使得驱动程序具有良好的可移植性和可扩展性。 文章“Linux网络协议栈——链路层”详细介绍了链路层的工作...
在Linux环境下,EC20R21的驱动通常是一个内核模块,需要与设备树(DTBO)或配置文件相结合,以便内核能够识别并正确初始化模块。开发者在Linux系统中使用EC20R21时,可能需要对内核进行编译,将驱动源码集成到内核源码...
1. **netlink**:这是Linux内核与用户空间通信的一种机制,可以用于传递网络设备状态的变化。当设备状态改变时,内核可以通过netlink socket将消息发送到用户空间的进程。 2. **udev**:这是一个动态管理设备的系统...
Netlink是Linux内核与用户空间通信的一种机制,广泛用于路由、网络设备配置和其他系统管理任务。 描述中提到"ethtool 需要的库文件 libmnl",ethtool是一个命令行工具,用于在Linux系统上查看和配置网络接口控制器...
2. **与 Android 框架层的交互**:Vold 通过 Netlink socket 与内核进行通信,同时通过 SocketListener 与 Android 的 Java 层进行交互。这样可以更高效地管理 USB 存储设备的状态变更,并及时通知应用程序。 3. **...
- **NetLinkManager**:负责处理与内核之间的Netlink消息,通过监听uevent事件来获取设备插入或拔出的信息。 - **CommandListener**:接收并处理来自客户端的命令请求,如挂载或卸载操作。 - **Volume**:表示单个...
iproute2与net-tools的一个显著区别在于它们与内核交互的方式。net-tools通过`/proc`文件系统和ioctl系统调用来访问和更改内核中的网络配置,而iproute2则是通过netlink套接字接口与内核通信。这种方式不仅提供了更...
Netlink是一种特殊的socket类型,用于进程间通信(IPC),特别是内核与用户空间进程之间的通信。 #### 2. Vold功能分析 ##### 2.1 Vold启动 Vold是在Android的初始化过程中被启动的。这一过程通常发生在`init`进程...
1. **Linux内核及驱动层**:这部分主要包括底层硬件的驱动程序和Linux内核,负责管理硬件资源和提供核心服务。 2. **本地库层**:包含了一系列C/C++库,如图形库、媒体库等,以及Java运行时环境。 3. **Java框架层**...
Netlink是一种在Linux内核和用户空间之间传递信息的机制。在Android的Vold中,NetlinkHandler扮演着重要的角色,它负责处理来自内核的网络事件,比如网络接口状态的变化。NetlinkHandler可能会监听特定的Netlink协议...
1. **NetLinkManager**:负责处理内核层与用户空间之间通过Netlink协议进行的通信。 2. **CommandListener**:监听并处理来自Framework层或其他应用程序的命令请求。 3. **SocketListener**:负责监听来自Framework...
然后,通过Linux的用户空间网络接口(如Netlink)与内核进行通信,将 CLICK 作为网络数据处理的中间层。 总结起来,这个项目提供了一个独特的视角去理解和实践TCP/IP协议栈的运作,通过 CLICK 平台,我们可以深入到...