linux的配置接口-netlink原理和设计

2023-10-28 20:18

本文主要是介绍linux的配置接口-netlink原理和设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

refer from http://blog.csdn.net/dog250/article/details/6425664


linux内核是可配置的,配置的方式有好多种呢!对于linux平台上上的开发者和管理员来讲,这几种配置方式可困扰了不少人儿。这里的配置不是指运行中的内核动态的配置,而是指当有新的设备或者内核特性添加进内核的时候,用户需要进行的配置。本文主要列举三种配置方式,最终落实于netlink方式的配置。
1.传统方式
传统方式一般认为是使用ioctl或者系统调用的方式,如果使用ioctl,当我们为设备驱动或者内核本身增加一个新的配置时,需要增加一个新的ioctl命令,这就可能就要修改ioctl的分发代码,类似于为芯片增加一个功能需要增加一个引脚且新布一根线一样,如果使用系统调用的方式,当既有的系统调用无法满足我们的需求时,我们就不得不增加一个新的系统调用,这就意味着需要重新编译内核代码。
2.procfs和sysfs的方式
这两种方式基于文件系统,所有的配置都可以通过文件读写接口完成,主谓宾(定状补最为参数和约束)的配置方式需要操作者记住诸多的选项参数,而sysfs通过文件名(kobject的属性)代替了诸多的选项参数,诸多选项参数需要另一个额外的参数“-h/--help”来展示,而sysfs的方式只要要记住ls命令即可,通过ls列出的文件代表kobject的属性,一般而言,一个好程序员会将-h选项参数所展示的帮助信息体现在文件名上!
     然而你增加一个新配置的时候,意味着在去除这个新配置之前,procfs或者sysfs中会永久性的增加一个新的文件或者目录,这样会导致procfs和sysfs的膨胀,还容易引起程序员/管理员的误解,平添了复杂性,再者,即使增加一个再简单不过的配置,你也不得不实现一个proc的entry或者一个kobject(或者其attribute),而对于很多人,这并不是一件容易的事!
3.netlink的方式
netlink机制是一种最适宜的方式,在接口上,它使用socket,很简单,在实现上,它不依赖任何其它的内核业务组件,类似一个扩展的支持多点对多点的管道,在这种管道的任何一端,你都可以随意的加入自己的处理逻辑,形成一条自适应,自给自足的带有端点的通用链路。除了在通信过程中,其余任何时候,netlink都不会在内核中留下任何足迹,除非你想留下!比如一个简单的例子,一个用户进程想告诉内核“XX hello world”,那么它将通过netlink将XX hello world写入内核,然后内核收到之后会根据XX来处理之,如果XX是print的话,那么内核将输出一条hello world,如果XX为laugh at的话,内核将嘲笑你一番,因为你是个菜鸟,或者将hello world发给其它进程也是可以的!完成XX之后,这件事将不留下任何痕迹。
     除了在内核态和用户态之间架设了一条通用的链路之外,如果你不希望,netlink不在任何位置留下任何信息。
     netlink一般被理解为“内核态和用户态通信的接口”,然而它也可以用作进程间通信的接口,并且这种进程间通信可以基于组而不是基于进程的pid。典型的例子就是设置路由以及其它网络协议栈参数的iproute2工具链,其完全是基于netlink实现的,也就是说你在其源代码中再也看不到诸如ioctl之类的系统调用了。
     netlink在使用方面的优势在于其简单性,在设计上的精华在于它的“软化”,还记得软化是什么意思吗?就是将复杂的控制逻辑集中在通信实体的两端而不是链路上,关于软化,典型的例子一般都来自于硬件链路的设计,比如并行链路发展为串行链路,链路本身不再约束任何通信协议相关的控制逻辑,而将控制逻辑集中在链路两端的芯片中,比如串行链路使用帧来传输协议pdu,协议相关的控制全部又帧的格式决定,以往在并行链路上,协议控制逻辑是由链路而不是帧决定的,比如“第x根线路代表数据准备好,第y根线传输同步信息”等。对于netlink而言,其设计十分类似于这种出自于硬件的“软化”思想,不再需要特定的ioctl来实现控制,而将控制集中在netlink套结字的发送和接收端,发送和接收端之间的“netlink链路”对传输的信息还不知情,它仅仅负责传输!
     毫不夸张的说,有了netlink,就可以去除一切系统调用了,这里我们把系统调用比喻成“硬连线”的逻辑,每一根或者几根线完成一个特定的控制逻辑,而netlink作为一个“串行的,软化”的逻辑,只在链路上传输“帧”,只在发送方和接收方处理控制逻辑。使用netlink的方式,操纵系统接口的可扩展性将大大增强。
     我们可以把操作系统分为若干个模块,大的方面说可以分为:用户空间/内核空间。其中用户空间又可以根据应用分为若干组,同理内核空间也是如此,可以看出,这就是完全基于消息的接口方式,所有消息通过netlink传输,并且,可以超级简单的实现消息的路由和中转,基于中转机制,甚至用户空间进程的IPC都可以完全使用netlink机制。如下图:

以上为传统的方式

以上为本文预测的netlink方式

使用netlink完成系统调用可以很简单的做到AIO,这完全交给socket来做即可,通过设置socket的block状态即可。比如用户执行一个同步的read调用,需要生成一个netlink套结字,然后设置成阻塞模式,然后通过send发送一个read消息后,recv内核的返回,read消息中包含需要读取的文件的描述符,offset,大小等信息,recv参数中包含用户缓冲区信息...如果执行一个异步的read调用,通过send发送了read消息之后,直接返回,在另一个帮助线程中recv返回后通知主线程即可...
     光说不练假把式,为了能说明问题,这里举出一个最简单的例子,这就是使用netlink机制替代发送信号的系统调用kill,内核模块源码如下:

[cpp] view plain copy
  1. #include <linux/module.h>  
  2. #include <linux/netlink.h>  
  3. #include <linux/sched.h>  
  4. #include <net/sock.h>  
  5. #include <linux/proc_fs.h>  
  6. #include <asm/siginfo.h>  
  7. #include <linux/signal.h>  
  8. static struct sock *netlink_kill_sock;  
  9. static int flag = 0;  
  10. struct sig_struct_info {  
  11.     int pid;  
  12.     int sig  
  13. };  
  14. static DECLARE_COMPLETION(exit_completion);  
  15. static void recv_handler(struct sock * sk, int length)  
  16. {  
  17.         wake_up(sk->sk_sleep);  
  18. }  
  19. static int process_message_thread(void * data)  
  20. {  
  21.         struct sk_buff * skb = NULL;  
  22.         struct nlmsghdr * nlhdr = NULL;  
  23.         int len;  
  24.         DEFINE_WAIT(wait);  
  25.     struct sig_struct_info sigi;  
  26.     struct siginfo info;  
  27.         daemonize("netlink-kill");  
  28.         while (flag == 0) {  
  29.                 prepare_to_wait(netlink_kill_sock->sk_sleep, &wait, TASK_INTERRUPTIBLE);  
  30.                 schedule();  
  31.                 finish_wait(netlink_kill_sock->sk_sleep, &wait);  
  32.                 while ((skb = skb_dequeue(&netlink_kill_sock->sk_receive_queue)) != NULL) {  
  33.             struct task_struct *p;  
  34.                         nlhdr = (struct nlmsghdr *)skb->data;  
  35.                         len = nlhdr->nlmsg_len - NLMSG_LENGTH(0);  
  36.             memset(&sigi, 0, sizeof(struct sig_struct_info));  
  37.                         memcpy(&sigi, NLMSG_DATA(nlhdr), len);  
  38.             info.si_signo = sigi.sig;  
  39.             info.si_errno = 0;  
  40.             info.si_code = SI_USER;  
  41.             info.si_pid = current->tgid;  
  42.             info.si_uid = current->uid;  
  43.             p = find_task_by_pid(sigi.pid);  
  44.             if (p)  
  45.                 force_sig_info(sigi.sig, &info, p);  
  46.                 }  
  47.         }  
  48.         complete(&exit_completion);  
  49.         return 0;  
  50. }  
  51. static int __init netlink_kill_init(void)  
  52. {  
  53.         netlink_kill_sock = netlink_kernel_create(111, recv_handler);  
  54.         if (!netlink_kill_sock) {  
  55.                 return 1;  
  56.         }  
  57.         kernel_thread(process_message_thread, NULL, CLONE_KERNEL);  
  58.         return 0;  
  59. }  
  60. static void __exit netlink_kill_exit(void)  
  61. {  
  62.         flag = 1;  
  63.         wake_up(netlink_kill_sock->sk_sleep);  
  64.         wait_for_completion(&exit_completion);  
  65.         sock_release(netlink_kill_sock->sk_socket);  
  66. }  
  67. module_init(netlink_kill_init);  
  68. module_exit(netlink_kill_exit);  
  69. MODULE_LICENSE("GPL");  

用户态代码如下:
[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <string.h>  
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7. #include <linux/netlink.h>  
  8. struct sig_struct_info {  
  9.     int pid;  
  10.     int sig  
  11. };  
  12. int main(int argc, char * argv[])  
  13. {  
  14.         struct sockaddr_nl saddr, daddr;  
  15.         struct nlmsghdr *nlhdr = NULL;  
  16.         struct msghdr msg;  
  17.         struct iovec iov;  
  18.         int sd;  
  19.         char text_line[MAX_MSGSIZE];  
  20.         int ret = -1;  
  21.     struct sig_struct_info sigi;  
  22.     memset(&sigi, 0, sizeof(sigi));  
  23.     sigi.pid=atoi(argv[1]);  
  24.     sigi.sig=atoi(argv[2]);  
  25.      
  26.         sd = socket(AF_NETLINK, SOCK_RAW, 111);  
  27.         memset(&saddr, 0, sizeof(saddr));  
  28.         memset(&daddr, 0, sizeof(daddr));  
  29.         saddr.nl_family = AF_NETLINK;  
  30.         saddr.nl_pid = getpid();  
  31.         saddr.nl_groups = 0;  
  32.         bind(sd, (struct sockaddr*)&saddr, sizeof(saddr));  
  33.         daddr.nl_family = AF_NETLINK;  
  34.         daddr.nl_pid = 0;  
  35.         daddr.nl_groups = 0;  
  36.         nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));  
  37.     memcpy(NLMSG_DATA(nlhdr), &sigi, sizeof(sigi));  
  38.     memset(&msg, 0 ,sizeof(struct msghdr));  
  39.     ...  
  40.     iov.iov_base = (void *)nlhdr;  
  41.     iov.iov_len = nlhdr->nlmsg_len;  
  42.     msg.msg_name = (void *)&daddr;  
  43.     msg.msg_namelen = sizeof(daddr);  
  44.     msg.msg_iov = &iov;  
  45.     msg.msg_iovlen = 1;  
  46.     ret = sendmsg(sd, &msg, 0);  
  47.         close(sd);  
  48.         return 0;  
  49. }  

netlink的应用越来越广泛,你可以将其视为一条总线,该总线是通用的,所有的信息都封装为“帧”在这条总线上传输,这些信息包括系统调用,IPC,内核态和用户态的通信等,具体是哪种信息以及该如何处理信息,netlink这条总线是不过问的,它就类似于PCIe总线,msi,read请求,dma请求都在PCIe上跑,具体是哪种帧由帧的格式区分并且仅由端点处理,如果想更加模块块,端点处的处理可以是分层的,PCIe就是这么做的,同样的道理,netlink也可以这么做。

     我觉得,既然硬件都软化了,操作系统也应该“软化”,特别是硬件性能大幅提高的今天!!


这篇关于linux的配置接口-netlink原理和设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/295685

相关文章

Spring @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建

Nginx 重写与重定向配置方法

《Nginx重写与重定向配置方法》Nginx重写与重定向区别:重写修改路径(客户端无感知),重定向跳转新URL(客户端感知),try_files检查文件/目录存在性,return301直接返回永久重... 目录一.try_files指令二.return指令三.rewrite指令区分重写与重定向重写: 请求

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

gitlab安装及邮箱配置和常用使用方式

《gitlab安装及邮箱配置和常用使用方式》:本文主要介绍gitlab安装及邮箱配置和常用使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.安装GitLab2.配置GitLab邮件服务3.GitLab的账号注册邮箱验证及其分组4.gitlab分支和标签的

spring中的ImportSelector接口示例详解

《spring中的ImportSelector接口示例详解》Spring的ImportSelector接口用于动态选择配置类,实现条件化和模块化配置,关键方法selectImports根据注解信息返回... 目录一、核心作用二、关键方法三、扩展功能四、使用示例五、工作原理六、应用场景七、自定义实现Impor

MySQL MCP 服务器安装配置最佳实践

《MySQLMCP服务器安装配置最佳实践》本文介绍MySQLMCP服务器的安装配置方法,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下... 目录mysql MCP 服务器安装配置指南简介功能特点安装方法数据库配置使用MCP Inspector进行调试开发指

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚