当前位置:必发365电子游戏 > 操作系统 > 【必发365电子游戏】Unix在进程通讯实现机制上的各有所分化
【必发365电子游戏】Unix在进程通讯实现机制上的各有所分化
2019-12-19

Linux中的进度间通信机制源自于Unix平台上的进度通信机制。Unix的两大分支AT&T Unix和BSD Unix在进度通讯完毕机制上的各有所分歧,前面二个形成了运行在单个Computer上的System V IPC,前面一个则得以落成了基于socket的长河间通讯机制。同临时间Linux也根据IEEE拟定的Posix IPC标准,在三者的根基之上达成了以下三种首要的IPC机制:管道(Pipe卡塔尔(قطر‎及命名管道(Named Pipe卡塔尔(英语:State of Qatar),确定性信号(Signal卡塔尔国,音信队列(Message queue卡塔尔国,分享内部存款和储蓄器(Shared Memory卡塔尔(قطر‎,功率信号量(Semaphore卡塔尔国,套接字(Socket卡塔尔(英语:State of Qatar)。通过那么些IPC机制,客商空间进度之间能够做到相互同信。为了完毕基本空间与客商空间通讯,Linux提供了遵照socket的Netlink通讯机制,能够兑现根基与顾客空间数据的当下沟通。

正文第二节概述相关商讨专门的工作,首节与别的IPC机制相比,详细介绍Netlink机制及其关键技术,第1节使用KGDB+GDB组合调节和测量检验,通过三个示范程序演示Netlink通讯进程。第5节做总计并提出Netlink通讯机制的美中不足。

2 相关讨论

到当下Linux提供了9种体制形成基本与客商空间的数据调换,分别是水源运行参数、模块参数与 sysfs、sysctl、系统调用、netlink、procfs、seq_file、debugfs和relayfs,当中模块参数与sysfs、procfs、debugfs、relayfs是基于文件系统的通讯机制,用于基本功空间向客户控件输出音信;sysctl、系统调用是由客户空间发起的通信机制。综上可得,以上均为单工通讯机制,在根本空间与顾客空间的双向相互作用数据调换上略显不足。Netlink是依赖socket的通讯机制,由于socket自个儿的双共性、突发性、不打断特点,由此能够很好的满意基本与客户空间一丝丝数据的立时相互影响,因而在Linux 2.6内核湖南中国广播公司大接收,比如SELinux,Linux系统的防火墙分为内核态的netfilter和客商态的iptables,netfilter与iptables的数据交流正是经过Netlink机制作而成功。 

3 Netlink机制及其关键才干

3.1 Netlink机制

Linux操作系统中当CPU处于根本状态时,能够分为有顾客上下文的情形和实行硬件、软件中断两种。个中当远在有顾客上下文时,由于内核态和客户态的内部存款和储蓄器映射机制差别,不可直接将本地变量传给客商态的内部存储器区;处于硬件、软件中断时,无法直接向客商内存区传递数据,代码实践不可中断。针对古板的进程间通讯机制,他们均不可能直接在内核态和用户态之间利用,原因如下表:

通信方法

无法介于内核态与用户态的原因

管道(不包括命名管道)

局限于父子进程间的通信。

消息队列

在硬、软中断中无法无阻塞地接收数据。

信号量

无法介于内核态和用户态使用。

内存共享

需要信号量辅助,而信号量又无法使用。

套接字

在硬、软中断中无法无阻塞地接收数据。

1*(引自 参照他事他说加以考查文献5)

    化解内核态和顾客态通讯机制可分为两类:

  1. 远在有客商上下文时,能够运用Linux提供的copy_from_user()和copy_to_user(卡塔尔(英语:State of Qatar)函数完毕,但出于这八个函数大概过不去,因而无法在硬件、软件的中断进度中利用。
  2. 居于硬、软件中断时。

2.1   能够透过Linux内核提供的spinlock自旋锁完结基本线程与中断进程的一块,由于内核线程运转在有上下文的长河中,由此能够在基本功线程中运用套接字或音讯队列来获得客商空间的数码,然后再将数据通过临界区传递给中断过程.

2.2   通过Netlink机制完成。Netlink 套接字的通讯依附是多个应者云集于经过的标志,日常定为该进度的 ID。Netlink通讯最大的特征是对对中断进度的支撑,它在根本空间收纳客商空间数据时不再要求客户自行运营七个内核线程,而是通过另二个软中断调用顾客优先钦定的收纳函数。通过软中断实际不是活动运营水源线程保险了数额传输的及时性。

3.2 Netlink优点

Netlink相对于此外的通信机制具备以下优点:

  1. 使用Netlink通过自定义意气风发种新的合计并参加公约族就可以通过socket API使用Netlink公约落成数据调换,而ioctl和proc文件系统均须要通进程序参加相应的设备或文件。
  2. Netlink使用socket缓存队列,是后生可畏种异步通讯机制,而ioctl是手拉手通讯机制,假使传输的数据量很大,会影响系统性情。
  3. Netlink帮忙多播,归属三个Netlink组的模块和经过都能收获该多播音讯。
  4. Netlink允许内核发起对话,而ioctl和系列调用只好由客户空间进度发起。

在基本源码有关Netlink左券的头文件中蕴涵了根本预订义的会谈项目,如下所示:

 

[cpp] view plain copy

 

  1. #define NETLINK_ROUTE         0     
  2.   
  3. #define NETLINK_W1             1      
  4.   
  5. #define NETLINK_USERSOCK     2      
  6.   
  7. #define NETLINK_FIREWALL      3       
  8.   
  9. #define NETLINK_INET_DIAG     4         
  10.   
  11. #define NETLINK_NFLOG         5        
  12.   
  13. #define NETLINK_XFRM          6        
  14.   
  15. #define NETLINK_SELINUX       7        
  16.   
  17. #define NETLINK_ISCSI           8        
  18.   
  19. #define NETLINK_AUDIT          9        
  20.   
  21. #define NETLINK_FIB_LOOKUP    10  
  22.   
  23. #define NETLINK_CONNECTOR    11  
  24.   
  25. #define NETLINK_NETFILTER      12       
  26.   
  27. #define NETLINK_IP6_FW          13  
  28.   
  29. #define NETLINK_DNRTMSG       14       
  30.   
  31. #define NETLINK_KOBJECT_UEVENT 15       
  32.   
  33. #define NETLINK_GENERIC        16  

 

 

上述那一个合同已经为不相同的系统利用所选拔,每种分歧的应用都有特有的传输数据的格式,由此风度翩翩旦客商不行使那些左券,需求投入本人定义的公约号。对于每贰个Netlink合同项目,能够有多达 32多播组,每三个多播组用多少个位代表,Netlink 的多播天性使得发送音讯给同一个组仅要求二回系统调用,由此对于急需多拨音讯的选用来讲,大大地下跌了系统调用的次数。

创建Netlink会话进程如下:

 必发365电子游戏 1

根本使用与正规socket API雷同的朝气蓬勃套API实现通讯进程。首先通过netlink_kernel_create(卡塔尔国创造套接字,该函数的原型如下:

[cpp] view plain copy

 

  1. struct sock *netlink_kernel_create(struct net *net,  
  2.   
  3.                   int unit,unsigned int groups,  
  4.   
  5.                   void (*input)(struct sk_buff *skb),  
  6.   
  7.                   struct mutex *cb_mutex,  
  8.   
  9.                   struct module *module);  

 

中间net参数是网络设施命名空间指针,input函数是netlink socket在收受到音信时调用的回调函数指针,module默感觉THIS_MODULE.

然后客商空间进程使用职业Socket API来创立套接字,将经过ID发送至内核空间,用户空间创设使用socket(卡塔尔成立套接字,该函数的原型如下:

int socket(int domain, int type, int protocol);

其中domain值为PF_NETLINK,即Netlink使用合同族。protocol为Netlink提供的商谈恐怕是客商自定义的切磋,Netlink提供的评论包涵NETLINK_ROUTE, NETLINK_FIREWALL, NETLINK_ARPD, NETLINK_ROUTE6和 NETLINK_IP6_FW。

进而使用bind函数绑定。Netlink的bind(卡塔尔(قطر‎函数把一个地点socket地址(源socket地址卡塔尔(英语:State of Qatar)与五个开垦的socket举行关联。完结绑定,内核空间收纳到客户进度ID之后便能够张开报道。

客户空间进程发送数据使用正式socket API中sendmsg(卡塔尔函数完毕,使用时需增多struct msghdr音讯和nlmsghdr新闻头。一个netlink新闻体由nlmsghdr和新闻的payload部分构成,输入音信后,内核会步向nlmsghdr指向的缓冲区。

底子空间发送数据使用独立创建的sk_buff缓冲区,Linux定义了如下宏方便对于缓冲区地址的设置,如下所示:

#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))

在对缓冲区设置完毕音信地址然后,可以利用netlink_unicast(卡塔尔(قطر‎来发布单播音讯,netlink_unicast(卡塔尔(英语:State of Qatar)原型如下:

int netlink_unicast(struct sock *必发365电子游戏,sk, struct sk_buff *skb, u32 pid, int nonblock);

参数sk为函数netlink_kernel_create(卡塔尔(قطر‎重返的socket,参数skb存放音信,它的data字段指向要发送的netlink音信构造,而skb的支配块保存了新闻的地址消息,前面包车型的士宏NETLINK_CB(skb卡塔尔(قطر‎就用来方便设置该调控块,参数pid为接到新闻进程的pid,参数nonblock表示该函数是还是不是为非梗塞,要是为1,该函数将要平昔不接到缓存可使用时立时赶回,而只要为0,该函数在未曾接过缓存可利用时睡觉。

内核模块或子系统也能够选择函数netlink_broadcast来发送广播音信:

void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation);

后边的八个参数与netlink_unicast相仿,参数group为选拔音信的多播组,该参数的每二个意味一个多播组,因而只要发送给多个多播组,就把该参数设置为三个多播组组ID的位或。参数allocation为基石内部存储器分配项目,日常地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以暂息),而GFP_KE凯雷德NEL用于非原子上下文。

选择数据时前后相继须要申请丰盛大的半空中来储存netlink新闻头和音信的payload部分。然后利用正规函数接口recvmsg(卡塔尔来接过netlink音信

4 Netlink通讯进度

调度平台:Vmware 5.5 + Fedora Core 10(两台,意气风发台作为host机,风流倜傥台作为target机卡塔尔。

调节和测量试验程序:分为内核模块和客户空间程序两局地,当内核模块被加载后,运营客商空间程序,由顾客空间发起Netlink会话,和内核模块举行数据调换。

被加载的内核模块不能够通过外加的调节和测量试验器实行调度,KGDB提供了意气风发种根底源码等级的调试机制。Linux内核自2.6.26本子之后在根本中放置了KGDB选项,编写翻译内核时必要接纳与之有关的选项,调节和测验时host端需利用含有符号表的vmlinz内核,target端应用gdb调节和测量试验顾客空间的次序。

顾客空间程序关键代码如下:

[cpp] view plain copy

 

  1. int send_pck_to_kern(u8 op, const u8 *data, u16 data_len)  
  2.   
  3. {  
  4.   
  5.     struct user_data_ *pck;  
  6.   
  7.     int ret;  
  8.   
  9.    
  10.   
  11.     pck = (struct user_data_*)calloc(1, sizeof(*pck) + data_len);  
  12.   
  13.     if(!pck) {  
  14.   
  15.        printf("calloc in %s failed!!!n", __FUNCTION__);  
  16.   
  17.        return -1;  
  18.   
  19.     }  
  20.   
  21.    
  22.   
  23.     pck->magic_num = MAGIC_NUM_RNQ;  
  24.   
  25.     pck->op = op;  
  26.   
  27.     pck->data_len = data_len;  
  28.   
  29.     memcpy(pck->data, data, data_len);  
  30.   
  31.    
  32.   
  33.     ret = send_to_kern((const u8*)pck, sizeof(*pck) + data_len);  
  34.   
  35.     if(ret)  
  36.   
  37.        printf("send_to_kern in %s failed!!!n", __FUNCTION__);  
  38.   
  39.      
  40.   
  41.     free(pck);  
  42.   
  43.    
  44.   
  45.     return ret ? -1 : 0;  
  46.   
  47. }  
  48.   
  49.    
  50.   
  51. static void recv_from_nl()  
  52.   
  53. {  
  54.   
  55.     char buf[1000];  
  56.   
  57.     int len;  
  58.   
  59.     struct iovec iov = {buf, sizeof(buf)};  
  60.   
  61.     struct sockaddr_nl sa;  
  62.   
  63.     struct msghdr msg;  
  64.   
  65.     struct nlmsghdr *nh;  
  66.   
  67.    
  68.   
  69.     memset(&msg, 0, sizeof(msg));  
  70.   
  71.     msg.msg_name = (void *)&sa;  
  72.   
  73.     msg.msg_namelen = sizeof(sa);  
  74.   
  75.     msg.msg_iov = &iov;  
  76.   
  77.     msg.msg_iovlen = 1;  
  78.   
  79.    
  80.   
  81.     //len = recvmsg(nl_sock, &msg, 0);  
  82.   
  83.     len = recvmsg(nl_sock, &msg, 0);  
  84.   
  85.    
  86.   
  87.     for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);  
  88.   
  89.            nh = NLMSG_NEXT (nh, len)) {  
  90.   
  91.        // The end of multipart message.  
  92.   
  93.        if (nh->nlmsg_type == NLMSG_DONE) {  
  94.   
  95.            puts("nh->nlmsg_type == NLMSG_DONE");  
  96.   
  97.            return;  
  98.   
  99.        }  
  100.   
  101.    
  102.   
  103.        if (nh->nlmsg_type == NLMSG_ERROR) {  
  104.   
  105.            // Do some error handling.  
  106.   
  107.            puts("nh->nlmsg_type == NLMSG_ERROR");  
  108.   
  109.            return;  
  110.   
  111.        }  
  112.   
  113.    
  114.   
  115.    
  116.   
  117. #if 1  
  118.   
  119.        puts("Data received from kernel:");  
  120.   
  121.        hex_dump((u8*)NLMSG_DATA(nh), NLMSG_PAYLOAD(nh, 0));  
  122.   
  123. #endif  
  124.   
  125.     }  
  126.   
  127. }  

内核模块供给幸免能源抢占,保障Netlink能源互斥占领,内核模块部分生死攸关代码如下:

 

[cpp] view plain copy

 

  1. static void nl_rcv(struct sk_buff *skb)  
  2.   
  3. {  
  4.   
  5.     mutex_lock(&nl_mtx);  
  6.   
  7.    
  8.   
  9.     netlink_rcv_skb(skb, &nl_rcv_msg);  
  10.   
  11.    
  12.   
  13.     mutex_unlock(&nl_mtx);  
  14.   
  15. }  
  16.   
  17.    
  18.   
  19.    
  20.   
  21. static int nl_send_msg(const u8 *data, int data_len)  
  22.   
  23. {  
  24.   
  25.     struct nlmsghdr *rep;  
  26.   
  27.     u8 *res;  
  28.   
  29.     struct sk_buff *skb;  
  30.   
  31.    
  32.   
  33.     if(g_pid < 0 || g_nl_sk == NULL) {  
  34.   
  35.        printk("Invalid parameter, g_pid = %d, g_nl_sk = %pn",  
  36.   
  37.                      g_pid, g_nl_sk);  
  38.   
  39.        return -1;  
  40.   
  41.     }  
  42.   
  43.    
  44.   
  45.     skb = nlmsg_new(data_len, GFP_KERNEL);  
  46.   
  47.     if(!skb) {  
  48.   
  49.        printk("nlmsg_new failed!!!n");  
  50.   
  51.        return -1;  
  52.   
  53.     }  
  54.   
  55.    
  56.   
  57.     if(g_debug_level > 0) {  
  58.   
  59.        printk("Data to be send to user space:n");  
  60.   
  61.        hex_dump((void*)data, data_len);  
  62.   
  63.     }  
  64.   
  65.    
  66.   
  67.     rep = __nlmsg_put(skb, g_pid, 0, NLMSG_NOOP, data_len, 0);  
  68.   
  69.     res = nlmsg_data(rep);  
  70.   
  71.     memcpy(res, data, data_len);  
  72.   
  73.     netlink_unicast(g_nl_sk, skb, g_pid, MSG_DONTWAIT);  
  74.   
  75.    
  76.   
  77.     return 0;  
  78.   
  79. }  
  80.   
  81.    
  82.   
  83. static int nl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)  
  84.   
  85. {  
  86.   
  87.     const u8 res_data[] = "Hello, user";  
  88.   
  89.     size_t data_len;  
  90.   
  91.      
  92.   
  93.     u8 *buf;  
  94.   
  95.     struct user_data_ *pck;  
  96.   
  97.     struct user_req *req, *match = NULL;  
  98.   
  99.      
  100.   
  101.      
  102.   
  103.     g_pid = NETLINK_CB(skb).pid;  
  104.   
  105.    
  106.   
  107.     buf = (u8*)NLMSG_DATA(nlh);  
  108.   
  109.     data_len = nlmsg_len(nlh);  
  110.   
  111.    
  112.   
  113.     if(data_len < sizeof(struct user_data_)) {  
  114.   
  115.        printk("Too short data from user space!!!n");  
  116.   
  117.        return -1;  
  118.   
  119.     }  
  120.   
  121.    
  122.   
  123.     pck = (struct user_data_ *)buf;  
  124.   
  125.     if(pck->magic_num != MAGIC_NUM_RNQ) {  
  126.   
  127.        printk("Magic number not matched!!!n");  
  128.   
  129.        return -1;  
  130.   
  131.     }  
  132.   
  133.    
  134.   
  135.     if(g_debug_level > 0) {  
  136.   
  137.        printk("Data from user space:n");  
  138.   
  139.        hex_dump(buf, data_len);  
  140.   
  141.     }  
  142.   
  143.    
  144.   
  145.    
  146.   
  147.     req = user_reqs;  
  148.   
  149.     while(req->op) {  
  150.   
  151.        if(req->op == pck->op) {  
  152.   
  153.            match = req;  
  154.   
  155.            break;  
  156.   
  157.        }  
  158.   
  159.    
  160.   
  161.        req++;  
  162.   
  163.     }  
  164.   
  165.    
  166.   
  167.     if(match) {  
  168.   
  169.        match->handler(buf, data_len);  
  170.   
  171.     }  
  172.   
  173.    
  174.   
  175.    
  176.   
  177.     nl_send_msg(res_data, sizeof(res_data));  
  178.   
  179.    
  180.   
  181.     return 0;  
  182.   
  183. }  

 

 

5.其余连锁认证

 

    Netlink 是生龙活虎种非常的 socket,它是 Linux 所特有的,相像于 BSD 中的AF_ROUTE 但又远比它的功效强盛,前段时间在风靡的 Linux 内核(2.6.14)中接受netlink 举行利用与根本通讯的使用非常多,满含:路由 daemon(NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态 socket 协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),socket 监视(NETLINK_INET_DIAG),netfilter 日志(NETLINK_NFLOG),ipsec 安全攻略(NETLINK_XFRM),SELinux 事件通报(NETLINK_SELINUX),iSCSI 子系统(NETLINK_ISCSI),进度审计(NETLINK_EquinoxT),转载消息表查询(NETLINK_FIB_LOOKUP),netlink connector(NETLINK_CONNECTOR),netfilter 子系统(NETLINK_NETFILTER),IPv6 防火墙(NETLINK_IP6_FW),DECnet 路由消息(NETLINK_DNRTMSG),内核事件向客商态公告(NETLINK_KOBJECT_UEVENT),通用 netlink(NETLINK_GENERIC)。

    Netlink 是黄金年代种在功底与顾客使用间张开双向数据传输的不得了好的点子,客商态应用使用规范的 socket API 就足以行使 netlink 提供的强盛成效,内核态要求利用特地的内核 API 来使用 netlink。

Netlink 相对于系统调用,ioctl 以致 /proc 文件系统来说具备以下优点:

    1,为了利用 netlink,顾客仅要求在 include/linux/netlink.h 中加进一个新品类的 netlink 公约定义就能够, 如 #define NETLINK_MYTEST 17 然后,内核和顾客态应用就可以立时通过 socket API 使用该 netlink 公约项目进行数据沟通。但系统调用要求充实新的体系调用,ioctl 则须要追加设备或文件, 那须要过多代码,proc 文件系统则须求在 /proc 下加多新的文书或目录,那将使本来就混乱的 /proc 尤其混乱。

    2. netlink是生龙活虎种异步通讯机制,在基本与客户态应用之间传递的音信保存在socket缓存队列中,发送音信只是把音讯保存在选择者的socket的收到队列,而无需拭目以俟选拔者收到信息,但系统调用与 ioctl 则是大器晚成道通讯机制,假诺传递的数额太长,将震慑调节粒度。

    3.行使 netlink 的基业部分能够运用模块的不二秘技贯彻,使用 netlink 的运用有的和底蕴部分没有编写翻译时信任,但系统调用就有凭仗,况兼新的体系调用的兑现必需静态地连接纳内核中,它不恐怕在模块中落到实处,使用新系统调用的施用在编写翻译时索要信赖内核。

    4.netlink 帮忙多播,内核模块或利用可以把音信多播给一个netlink组,归属该neilink 组的其余内核模块或行使都能接过到该音信,内核事件向客商态的打招呼机制就应用了那生龙活虎特点,任何对根本领件感兴趣的使用都能吸收接纳该子系统一发布送的基技巧件,在背后的篇章少将介绍这一机制的施用。

    5.内核能够行使 netlink 首头阵起对话,但系统调用和 ioctl 只可以由顾客选拔发起调用。

    6.netlink 使用标准的 socket API,因而非常轻松接纳,但系统调用和 ioctl则必要特地的培育本事采用。

客户态使用 netlink

   顾客态应用使用正规的socket APIs, socket(卡塔尔(英语:State of Qatar), bind(卡塔尔, sendmsg(卡塔尔, recvmsg(卡塔尔(قطر‎ 和 close(卡塔尔 就能够相当轻便地利用 netlink socket,查询手册页能够理解那么些函数的选拔细节,本文只是教授使用 netlink 的顾客应该怎么运用那一个函数。注意,使用 netlink 的选择必需带有头文件 linux/netlink.h。当然 socket 须要的头文件也少不了,sys/socket.h。

   为了创设三个 netlink socket,客商须要使用如下参数调用 socket(卡塔尔:

 socket(AF_NETLINK, SOCK_RAW, netlink_type)

 

 

   第五个参数必须是 AF_NETLINK 或 PF_NETLINK,在 Linux 中,它们俩事实上为二个东西,它象征要使用netlink,首个参数必得是SOCK_RAW或SOCK_DGRAM,第八个参数钦赐netlink合同项目,如前方讲的客商自定义公约项目NETLINK_MYTEST, NETLINK_GENE讴歌MDXIC是三个通用的交涉项目,它是特别为客商使用的,因而,客商能够直接使用它,而没有必要再增添新的说道项目。内核预订义的商业事务项目有:

[cpp] view plain copy

 

  1. #define NETLINK_ROUTE 0  
  2. #define NETLINK_W1 1  
  3. #define NETLINK_USERSOCK 2   
  4. #define NETLINK_FIREWALL 3  
  5. #define NETLINK_INET_DIAG 4  
  6. #define NETLINK_NFLOG 5  
  7. #define NETLINK_XFRM 6   
  8. #define NETLINK_SELINUX 7   
  9. #define NETLINK_ISCSI 8   
  10. #define NETLINK_AUDIT 9   
  11. #define NETLINK_FIB_LOOKUP 10   
  12. #define NETLINK_CONNECTOR 11   
  13. #define NETLINK_NETFILTER 12   
  14. #define NETLINK_IP6_FW 13   
  15. #define NETLINK_DNRTMSG 14   
  16. #define NETLINK_KOBJECT_UEVENT 15   
  17. #define NETLINK_GENERIC 16  

 

对此每三个netlink左券项目,能够有多达 32多播组,每三个多播组用三个位代表,netlink 的多播脾性使得发送消息给同八个组仅需求壹遍系统调用,由此对于供给多拨音讯的应用来讲,大大地收缩了系统调用的次数。

   函数 bind() 用于把三个开发的 netlink socket 与 netlink 源 socket 地址绑定在一齐。netlink socket 之处构造如下:

[cpp] view plain copy

 

  1. struct sockaddr_nl {  
  2.    sa_family_t nl_family;  
  3.    unsigned short nl_pad;  
  4.    __u32 nl_pid;  
  5.    __u32 nl_groups;  
  6.  };  

 

字段 nl_family 必需安装为 AF_NETLINK 或着 PF_NETLINK,字段 nl_pad 当前从不选用,由此要连接设置为 0,字段 nl_【必发365电子游戏】Unix在进程通讯实现机制上的各有所分化。pid 为收到或发送音讯的进度的 ID,要是指望内核管理音讯或多播音讯,就把该字段设置为 0,不然设置为拍卖音信的进度 ID。字段 nl_groups 用于钦赐多播组,bind 函数用于把调用进度步入到该字段钦定的多播组,要是设置为 0,表示调用者不到场别的多播组。

   传递给 bind 函数的地点的 nl_pid 字段应当设置为本进度的进程ID,这一定于 netlink socket 的本土地址。不过,对于一个经过的五个线程使用 netlink socket 的气象,字段 nl_pid 则足以设置为别的的值,如:

pthread_self() << 16 | getpid();

 

 

   因而字段 nl_pid 实际上未必是经过 ID,它只是用来区分差别的收信人或发送者的叁个标志,客户能够依靠自身须要安装该字段。函数 bind 的调用格局如下:

bind(fd, (struct sockaddr*)&nladdr, sizeof(struct sockaddr_nl));

 

 

   fd为近期的 socket 调用重回的文本描述符,参数 nladdr 为 struct sockaddr_nl 类型之处。为了发送三个 netlink 音信给底工或其余客户态应用,要求填写指标 netlink socket 地址,那个时候,字段 nl_pid 和 nl_groups 分别表示接到音信者的进程 ID 与多播组。借使字段 nl_pid 设置为 0,表示音讯接纳者为基本或多播组,要是 nl_groups为 0,表示该信息为单播音讯,不然表示多播信息。使用函数 sendmsg 发送 netlink 新闻时还亟需援用结构 struct msghdr、struct nlmsghdr 和 struct iovec,构造 struct msghdr 需如下设置:

[cpp] view plain copy

 

  1. struct msghdr msg;   
  2. memset(&msg, 0, sizeof(msg));   
  3. msg.msg_name = (void *)&(nladdr);   
  4. msg.msg_namelen = sizeof(nladdr);  

 

里面 nladdr 为音讯选拔者的 netlink 地址。

   struct nlmsghdr 为 netlink socket 自个儿的音信头,那用于多路复用和多路降解 netlink 定义的享有左券项目甚至此外一些操纵,netlink 的水源达成将选拔那么些消息头来多路复用和多路降解已经别的的有个别调控,由此它也被誉为netlink 调节块。由此,应用在发送 netlink 音信时必得提供该音讯头。

[cpp] view plain copy

 

  1. struct nlmsghdr {  
  2.     __u32 nlmsg_len;   
  3.     __u16 nlmsg_type;   
  4.     __u16 nlmsg_flags;  
  5.     __u32 nlmsg_seq;  
  6.     __u32 nlmsg_pid;  
  7. };  

字段 nlmsg_len 钦点新闻的总参谋长度,富含紧跟该组织的数额部分长度以至该社团的朗朗上口,字段 nlmsg_type 用于接受内部定义音讯的档案的次序,它对 netlink 内核查现是透明的,由此半数以上情况下设置为 0,字段 nlmsg_flags 用于安装新闻申明,可用的评释包罗:

 

[cpp] view plain copy

 

  1. #define NLM_F_REQUEST 1   
  2. #define NLM_F_MULTI     2   
  3. #define NLM_F_ACK        4   
  4. #define NLM_F_ECHO      8   
  5. #define NLM_F_ROOT     0x100   
  6. #define NLM_F_MATCH    0x200   
  7. #define NLM_F_ATOMIC  0x400   
  8. #define NLM_F_DUMP      (NLM_F_ROOT|NLM_F_MATCH)   
  9. #define NLM_F_REPLACE  0x100   
  10. #define NLM_F_EXCL       0x200   
  11. #define NLM_F_CREATE   0x400   
  12. #define NLM_F_APPEND   0x800  

 

 

标志NLM_F_REQUEST用于表示新闻是一个倡议,全体应用首首发起的音信都应设置该标记。

标志NLM_F_MULTI 用于提示该音信是一个多一些消息的大器晚成部分,后续的音信能够经过宏NLMSG_NEXT来获得。

宏NLM_F_ACK代表该新闻是前二个须要新闻的响应,顺序号与经过ID能够把诉求与响应关联起来。

标志NLM_F_ECHO代表该消息是相关的三个包的回传。

标志NLM_F_ROOT 被众多 netlink 合同的种种数据拿到操作使用,该标记提示被号令的数据表应当完好再次来到顾客使用,实际不是一个中规中矩三个中规中矩地回去。有该标识的号令平时引致响应消息设置NLM_F_MULTI标记。注意,当设置了该标记时,央浼是说道一定的,因而,须求在字段 nlmsg_type 中钦定合同项目。

标志 NLM_F_MATCH 表示该左券一定的号令只须要三个数据子集,数据子集由钦定的协商一定的过滤器来同盟。

标志 NLM_F_ATOMIC 提示乞求再次回到的数量应当原子地搜聚,那防止数据在得到时期被改良。

标志 NLM_F_DUMP 未实现。

标志 NLM_F_REPLACE 用于代替在数据表中的共处条约。

标志 NLM_F_EXCL_ 用于和 CREATE 和 APPEND 卓越使用,若是条目款项已经存在,将失败。

标志 NLM_F_CREATE 提示应当在钦赐的表中成立贰个条约。

标志 NLM_F_应用软件END 指示在表末尾增多新的条目款项。

根本需求读取和改换那个标记,对于常常的行使,顾客把它设置为 0 就能够,只是局地尖端应用(如 netfilter 和路由 daemon 必要它举行局地头昏眼花的操作),字段 nlmsg_seq 和 nlmsg_pid 用于接受跟踪音讯,前面一个表示顺序号,前者为音讯来源进度ID。上边是一个示范:

[cpp] view plain copy

 

  1. #define MAX_MSGSIZE 1024  
  2. char buffer[] = "An example message";   
  3. struct nlmsghdr nlhdr;   
  4. nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));   
  5. strcpy(NLMSG_DATA(nlhdr),buffer);   
  6. nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));   
  7. nlhdr->nlmsg_pid = getpid();   
  8. nlhdr->nlmsg_flags = 0;  

组织 struct iovec 用于把多个音信通过三回系统调用来发送,上边是该协会选择示例:

 

[cpp] view plain copy

 

  1. struct iovec iov;   
  2. iov.iov_base = (void *)nlhdr;   
  3. iov.iov_len = nlh->nlmsg_len;   
  4. msg.msg_iov = &iov;   
  5. msg.msg_iovlen = 1;  

 

 

在变成以上步骤后,音信就足以因此上边语句直接发送:

sendmsg(fd, &msg, 0);

运用选择音讯时必要首先分配二个十足大的缓存来保存新闻头以致音信的数量部分,然后填充信息头,添完后就足以一贯调用函数 recvmsg(卡塔尔(英语:State of Qatar) 来选取。

 

[cpp] view plain copy

 

  1. #define MAX_NL_MSG_LEN 1024   
  2. struct sockaddr_nl nladdr;   
  3. struct msghdr msg;   
  4. struct iovec iov;   
  5. struct nlmsghdr * nlhdr;   
  6. nlhdr = (struct nlmsghdr *)malloc(MAX_NL_MSG_LEN);   
  7. iov.iov_base = (void *)nlhdr;   
  8. iov.iov_len = MAX_NL_MSG_LEN;   
  9. msg.msg_name = (void *)&(nladdr);   
  10. msg.msg_namelen = sizeof(nladdr);   
  11. msg.msg_iov = &iov;   
  12. msg.msg_iovlen = 1;   
  13. recvmsg(fd, &msg, 0);  

 

 

小心:fd为socket调用张开的netlink socket描述符。

在新闻选择后,nlhdr指向选拔到的新闻的新闻头,nladdr保存了抽取到的新闻的靶子地点,宏NLMSG_DATA(nlhdr卡塔尔(قطر‎再次回到指向音信的数码部分的指针。

在linux/netlink.h中定义了有些有益于对音信进行拍卖的宏,这几个宏包涵:

#define NLMSG_ALIGNTO 4 
#define NLMSG_ALIGN(len)    ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

 

宏NLMSG_ALIGN(len卡塔尔(英语:State of Qatar)用于获取十分的大于len且字节对齐的微小数值。

#define NLMSG_LENGTH(len)   ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))

 

宏NLMSG_LENGTH(len卡塔尔(قطر‎用于总括数据部分长度为len时实际的信息长度。它日常用于分配音信缓存。

#define NLMSG_SPACE(len)   NLMSG_ALIGN(NLMSG_LENGTH(len))

 

宏NLMSG_SPACE(len卡塔尔(قطر‎再次来到一点都不小于NLMSG_LENGTH(len卡塔尔(قطر‎且字节对齐的细微数值,它也用于分配音讯缓存。

#define NLMSG_DATA(nlh)   ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

 

宏NLMSG_DATA(nlh卡塔尔用于获取音讯的数据部分的首地址,设置和读取音信数据部分时要求接纳该宏。

#define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), 
 (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))

 

宏NLMSG_NEXT(nlh,len卡塔尔(قطر‎用于获取下三个新闻的首地址,同有的时候间len也回退为剩下音讯的总参谋长度,该宏经常在三个音信被分为多少个部分发送或收取时利用。

#define NLMSG_OK(nlh,len)   ((len) >= (int)sizeof(struct nlmsghdr) && 
 (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) &&  
(nlh)->nlmsg_len <= (len))

 

宏NLMSG_OK(nlh,len卡塔尔国用于决断新闻是或不是有len这么长。

#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

 

宏NLMSG_PAYLOAD(nlh,len卡塔尔国用于重临payload的长度。

函数close用于关闭展开的netlink socket。

netlink内核API

netlink的木本实以后.c文件net/core/af_netlink.c中,内核模块要想使用netlink,也必需包蕴头文件linux/netlink.h。内核使用netlink须要极度的API,那全然不相同于顾客态应用对netlink的行使。假如客商必要增添新的netlink左券项目,必需经过纠正linux/netlink.h来达成,当然,近来的netlink达成已经富含了四个通用的公约项目NETLINK_GENEHighlanderIC以便于顾客使用,客商能够直接利用它而不要求扩展新的争辨项目。前面讲到,为了扩充新的netlink合同项目,顾客仅需追加如下概念到linux/netlink.h就能够:

#define NETLINK_MYTEST 17

 

假定扩充这一个定义之后,客商就足以在根本的别样地点援引该左券。

在基本中,为了制造多少个netlink socket顾客供给调用如下函数:

struct sock * netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));

 

参数unit表示netlink契约项目,如NETLINK_MYTEST,参数input则为内核模块定义的netlink音信管理函数,当有音讯达到这一个netlink socket时,该input函数指针就能够被引述。函数指针input的参数sk实际上便是函数netlink_kernel_create重返的struct sock指针,sock实际是socket的四个基本表示数据构造,顾客态应用创设的socket在底蕴中也可以有二个struct sock构造来代表。上面是一个input函数的以身作则:

void input (struct sock *sk, int len) { struct sk_buff *skb; struct nlmsghdr *nlh = NULL; u8 *data = NULL; while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { nlh = (struct nlmsghdr *)skb->data; data = NLMSG_DATA(nlh); } }

 

函数input(卡塔尔国会在出殡和下葬进度推行sendmsg(卡塔尔(英语:State of Qatar)时被调用,那样管理音讯相比较及时,不过,借使音信极短时,那样管理将扩展系统调用sendmsg(卡塔尔(英语:State of Qatar)的实践时间,对于这种情景,能够定义多个底蕴线程专责音信接纳,而函数input的办事只是提醒该内核线程,那样sendmsg将一点也不慢再次回到。

函数skb = skb_dequeue(&sk->receive_queue卡塔尔(قطر‎用于获取socket sk的收取队列上的音讯,再次回到为一个struct sk_buff的布局,skb->data指向实际的netlink音讯。

函数skb_recv_datagram(nl_sk卡塔尔也用于在netlink socket nl_sk上选用消息,与skb_dequeue的不如建议是,如若socket的接纳队列上未曾消息,它将促成调用进度睡眠在等待队列nl_sk->sk_sleep,因而它必需在进度上下文使用,刚才讲的内核线程就足以选拔这种方法来选择音信。

上边包车型地铁函数input正是这种使用的演示:

void input (struct sock *sk, int len) { wake_up_interruptible(sk->sk_sleep); }

 

当内核中发送netlink音讯时,也亟需设置目之处与源地址,並且根本中国国投息是通过struct sk_buff来治本的, linux/netlink.h中定义了二个宏:

#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。

在基本中,模块调用函数 netlink_unicast 来发送单播新闻:

int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);

 

参数sk为函数netlink_kernel_create(卡塔尔(قطر‎重回的socket,参数skb寄放信息,它的data字段指向要发送的netlink音讯构造,而skb的主宰块保存了音讯的地址新闻,后面包车型大巴宏NETLINK_CB(skb卡塔尔(قطر‎就用来方便设置该调控块,参数pid为选拔新闻进度的pid,参数nonblock表示该函数是不是为非堵塞,借使为1,该函数就要未曾选用缓存可利用时立时再次回到,而只要为0,该函数在并未有收到缓存可接纳时睡觉。

内核模块或子系统也得以利用函数netlink_broadcast来发送广播音讯:

void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation);

 

如今的多少个参数与netlink_unicast相同,参数group为接到消息的多播组,该参数的每叁个代表二个多播组,因而假如发送给多少个多播组,就把该参数设置为多少个多播组组ID的位或。参数allocation为根本内存分配项目,平日地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡觉),而GFP_KEKoleosNEL用于非原子上下文。

在根本中应用函数sock_release来释放函数netlink_kernel_create()创建的netlink socket:

void sock_release(struct socket * sock);

 

静心函数netlink_kernel_create(卡塔尔(قطر‎重回的连串为struct sock,因而函数sock_release应该这种调用:

sock_release(sk->sk_socket);

 

sk为函数netlink_kernel_create(卡塔尔(英语:State of Qatar)的重返值。

sk为函数netlink_kernel_create(卡塔尔的重回值。在源代码包中给出了一个施用 netlink 的示范,它回顾二个内核模块 netlink-exam-kern.c 和八个应用程序 netlink-exam-user-recv.c, netlink-exam-user-send.c。内核模块必需先插入到根本,然后在二个终端上运维客商态选择程序,在另三个尖峰上运转客商态发送程序,发送程序读取参数钦命的文书文件并把它看作 netlink 消息的剧情发送给内核模块,内核模块采取该消息保存到底工缓存中,它也由此proc接口出口到 procfs,因而客户也能够透过 /proc/netlink_exam_buffer 见到整个的剧情,相同的时候内核也把该音讯发送给客商态选择程序,客商态选择程序将把选择到的剧情输出到显示器上。