关于netlink描述1

netlink原理及应用 netlink是一种基于网络的通信机制,允许内核内部、内核与用户态应用之间甚至用户态应用之间进行通信;netlink的主要作用是内核与用户态之间通信;它的思想是,基于BSD的socket使用网络框架在内核和用户态之间进行通信;如何在中新增netlink协议?参考nl80211模块初始化时,通过注册通用netlink协议族,将命令以及处理函数进行注册;int err; 阅读详情

netlink socket编程之why & how
作者: Kevin Kaichuan He@2005-1-5
翻译整理:duanjigang @2008-9-15<duanjigang1983@126.com>
原文: http://www.linuxjournal.com/article/7356
  
开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排在内核中。其它程序,比如GUI,管理以及控制部分的代码,一般都会作为用户态程序。在linux系统中,把系统的某个特性分割成在内核中和在用户空间中分别实现一部分的做法是很常见的(比如linux系统的防火墙就分成了内核态的Netfilter和用户态的iptables)。然而,内核程序与用户态的程序又是怎样行通讯的呢?
答案就是通过各种各样的用户态和内核态的IPC(interprocess   communication  )机制来实现。比如系统调用,ioctl接口,proc文件系统以及netlink socket,本文就是要讨论netlink socekt并向读者展示这种用网络
通讯接口方式实现的IPC机制的优点。

介绍:
netlink socekt是一种用于在内核态和用户态进程之间进行数据传输的特殊的IPC。它通过为内核模块提
供一组特殊的API,并为用户程序提供了一组标准的socket 接口的方式,实现了一种全双工的通讯连接。类似于TCP/IP中使用AF_INET地址族一样,netlink socket使用地址族AF_NETLINK。每一个netlink
socket在内核头文件

[Copy to clipboard] [ - ]
CODE:
include/linux/netlink.h

中定义自己的协议类型。
下面是netlink socket 目前的特性集合以及它支持的协议类型:

NETLINK_ROUTE 用户空间的路由守护程序之间的通讯通道,比如BGP,OSPF,RIP以及内核数据转发模块。用户态的路由守护程序通过此类型的协议来更新内核中的路由表。
NETLINK_FIREWALL:接收IPV4防火墙代码发送的数据包。
NETLINK_NFLOG:用户态的iptables管理工具和内核中的netfilter模块之间通讯的通道。
NETLINK_ARPD:用来从用户空间管理内核中的ARP表。


   为什么以上的功能在实现用户程序和内核程序通讯时,都使用netlink方法而不是系统调用,ioctls
或者proc文件系统呢?原因在于:为新的特性添加一个新的系统调用,ioctls或者一个proc文件的做法并不是很容易的一件事情,因为我们要冒着污染内核代码并且可能破坏系统稳定性的风险去完成这件事情。
然而,netlink socket却是如此的简单,你只需要在文件netlink.h中添加一个常量来标识你的协议类型,然后,内核模块和用户程序就可以立刻使用socket风格的API进行通讯了!
        Netlink提供了一种异步通讯方式,与其他socket API一样,它提供了一个socket队列来缓冲或者平滑
瞬时的消息高峰。发送netlink消息的系统调用在把消息加入到接收者的消息对列后,会触发接收者的接收处理函数。接收者在接收处理函数上下文中,可以决定立即处理消息还是把消息放在队列中,在以后其它上下文去处理它(因为我们希望接收处理函数执行的尽可能快)。系统调用与netlink不同,它需要一个同步的处理,因此,当我们使用一个系统调用来从用户态传递消息到内核时,如果处理这个消息的时间很长的话,内核调度的粒度就会受到影响。
        内核中实现系统调用的代码都是在编译时静态链接到内核的,因此,在动态加载模块中去包含一个系统调用的做法是不合适的,那是大多数设备驱动的做法。使用netlink socket时,动态加载模块中的netlink程序不会和linux内核中的netlink部分产生任何编译时依赖关系。
Netlink优于系统调用,ioctls和proc文件系统的另外一个特点就是它支持多点传送。一个进程可以把消息传输给一个netlink组地址,然后任意多个进程都可以监听那个组地址(并且接收消息)。这种机制为内核到用户态的事件分发提供了一种近乎完美的解决方案。
系统调用和ioctl都属于单工方式的IPC,也就是说,这种IPC会话的发起者只能是用户态程序。但是,如果内核有一个紧急的消息想要通知给用户态程序时,该怎么办呢?如果直接使用这些IPC的话,是没办法做到这点的。通常情况下,应用程序会周期性的轮询内核以获取状态的改变,然而,高频度的轮询势必会增加系统的负载。Netlink 通过允许内核初始化会话的方式完美的解决了此问题,我们称之为netlink socket的双工特性。
        最后,netlink socket提供了一组开发者熟悉的BSD风格的API函数,因此,相对于使用神秘的系统调用API或者ioctl而言,netlink开发培训的费用会更低些。
        与BSD的Routing socket的关系
在BSD TCP/IP的协议栈实现中,有一种特殊的socket叫做Routing socket.它的地址族为AF_ROUTE, 协议族为PF_ROUTE, socket类型为SOCK_RAW. 这种Routing socket是用户态进程用来向内核中的路由表增加或者删除路由信息用的。在Linux系统中,netlink socket通过协议类型NETLINK_ROUTE实现了与Routing socket相同的功能,可以说,netlink socket提供了BSD Routing socket功能的超集。
Netlink Socket 的API
     标准的socket API函数-

[Copy to clipboard] [ - ]
CODE:
socket(), sendmsg(), recvmsg()和close()

-都能够被用户态程序直接调用来访问netlink socket.你可以访问man手册来获取这些函数的详细定义。在本文,我们只讨论怎样在netlink socket的上下文中为这些函数选择参数。这些API对于使用TCP/IP socket写过一些简单网络程序的读者来说应该很熟悉了。
使用socket()函数创建一个socket,输入:

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

 

socket域(地址族)是AF_NETLINK,socket的类型是SOCK_RAW或者SOCK_DGRAM,因为netlink是一种面向数据包的服务。
协议类型选择netlink要使用的类型即可。下面是一些预定义的netlink协议类型:

[Copy to clipboard] [ - ]
CODE:
NETLINK_ROUTE, NETLINK_FIREWALL, NETLINK_ARPD, NETLINK_ROUTE6
和 NETLINK_IP6_FW.

你同样可以很轻松的在netlink.h中添加自定义的协议类型。

每个netlink协议类型可以定义高达32个多点传输的组。每个组用一个比特位来表示,1<<i,0<=i<=31.
当一组用户态进程和内核态进程协同实现一个相同的特性时,这个方法很有用,因为发送多点传输的netlink消息可以减少系统调用的次数,并且减少了相关应用程序的个数,这些程序本来是要用来处理维护多点传输组之间关系而带来的负载的。
bind()函数
跟TCP/IP中的socket一样,netlink的bind()函数把一个本地socket地址(源socket地址)与一个打开的socket进行关联,netlink地址结构体如下:

struct sockaddr_nl
{
  sa_family_t    nl_family;  /* AF_NETLINK   */
  unsigned short nl_pad;     /* zero         */
  __u32          nl_pid;     /* process pid */
  __u32          nl_groups;  /* mcast groups mask */
} nladdr;


当上面的结构体被bind()函数调用时,sockaddr_nl的nl_pid属性的值可以设置为访问netlink socket的当前进程的PID,nl_pid作为这个netlink socket的本地地址。应用程序应该选择一个唯一的32位整数来填充nl_pid的值。

[Copy to clipboard] [ - ]
CODE:
NL_PID 公式 1:  nl_pid = getpid();

公式一使用进程的PID作为nl_pid的值,如果这个进程只需要一个该类型协议的netlink socket的话,选用进程pid作为nl_pid是一个很自然的做法。
换一种情形,如果一个进程的多个线程想要创建属于各个线程的相同协议类型的netlink socket的话,公式二可以用来为每个线程的netlink socket产生nl_pid值。

[Copy to clipboard] [ - ]
CODE:
NL_PID 公式 2: pthread_self() << 16 | getpid();

采用这种方法,同一进程的不同线程都能获取属于它们的相同协议类型的不同netlink socket。事实上,即便是在一个单独的线程里,也可能需要创建同一协议类型的多个netlink socket。所以开发人员需要更多聪明才智去创建不同的nl_pid值,然而本文中不会就如何创建多个不同的nl_pid的值进行过多的讨论
如果应用程序想要接收特定协议类型的发往指定多播组的netlink消息的话,所有接收组的比特位应该进行与运算,形成sockaddr_nl的nl_groups域的值。否则的话,nl_groups应该设置为0,以便应用程序只能够收到发送给它的netlink消息。在填充完结构体nladdr后,作如下的绑定工作:

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

 

发送一个netlink 消息
为了能够把一个netlink消息发送给内核或者别的用户进程,类似于UDP数据包发送的sendmsg()函数一样,我们需要另外一个结构体struct sockaddr_nl nladdr作为目的地址。如果这个netlink消息是发往内核的话,nl_pid属性和nl_groups属性都应该设置为0。
如果这个消息是发往另外一个进程的单点传输消息,nl_pid应该设置为接收者进程的PID,nl_groups应该设置为0,假设系统中使用了公式1。
如果消息是发往一个或者多个多播组的话,应该用所有目的多播组的比特位与运算形成nl_groups的值。然后我们就可以将netlink地址应用到结构体struct msghdr msg中,供函数sendmsg()来调用:

struct msghdr msg;
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);
 


netlink消息同样也需要它自身的消息头,这样做是为了给所有协议类型的netlink消息提供一个通用的背景。
由于linux内核的netlink部分总是认为在每个netlink消息体中已经包含了下面的消息头,所以每个应用程序在发送netlink消息之前需要提供这个头信息:

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 消息体的总长度来填充,包含头信息在内,这个是netlink核心需要的信息。mlmsg_type可以被应用程序所用,它对于netlink核心来说是一个透明的值。Nsmsg_flags 用来该对消息体进行另外的控制,会被netlink核心代码读取并更新。Nlmsg_seq和nlmsg_pid同样对于netlink核心部分来说是透明的,应用程序用它们来跟踪消息。
因此,一个netlink消息体由nlmsghdr和消息的payload部分组成。一旦输入一个消息,它就会进入一个被nlh指针指向的缓冲区。我们同样可以把消息发送个结构体struct msghdr msg:

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

 

在完成了以上步骤后,调用一次sendmsg()函数就能把netlink消息发送出去:

sendmsg(fd, &msg, 0);
 


接收netlink消息:
接收程序需要申请足够大的空间来存储netlink消息头和消息的payload部分。它会用如下的方式填充结构体 struct msghdr msg,然后使用标准函数接口recvmsg()来接收netlink消息,假设nlh指向缓冲区:

struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;

iov.iov_base = (void *)nlh;
iov.iov_len = MAX_NL_MSG_LEN;
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);

msg.msg_iov = &iov;
msg.msg_iovlen = 1;
recvmsg(fd, &msg, 0);
 

 

当消息正确接收后,nlh应该指向刚刚接收到的netlink消息的头部分。Nladdr应该包含接收到消息体的目的地信息,这个目的地信息由pid和消息将要发往的多播组的值组成。Netlink.h中的宏定义NLMSG_DATA(nlh)返回指向netlink消息体的payload的指针。调用

[Copy to clipboard] [ - ]
CODE:
close(fd)

就可以关闭掉fd描述符代表的netlink socket.
内核空间的netlink API接口
   内核空间的netlink API是由内核中的netlink核心代码支持的,在net/core/af_netlink.c中实现。从内核的角度来说,API接口与用户空间的API是不一样的。内核模块通过这些API访问netlink socket并且与用户空间的程序进行通讯。如果你不想使用netlink预定义好的协议类型的话,可以在netlink.h中添加一个自定义的协议类型。例如,我们可以通过在netlink.h中插入下面的代码行,添加一个测试用的协议类型:

[Copy to clipboard] [ - ]
CODE:
#define NETLINK_TEST  17

然后,就可以在linux内核的任何部分访问这个协议类型了。
在用户空间,我们通过socket()调用来创建一个netlink socket,但是在内核空间,我们调用如下的API:

[Copy to clipboard] [ - ]
CODE:
struct sock * netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));

参数uint是netlink协议类型,例如NETLINK_TEST。函数指针,input,是netlink socket在收到消息时调用的处理消息的回调函数指针。
在内核创建了一个NETLINK_TEST类型的netlink socket后,无论什么时候,只要用户程序发送一个NETLINK_TEST类型的netlink消息到内核的话,通过netlink_kernel_create()函数注册的回调函数input()都会被调用。下面是一个实现了消息处理函数input的例子。

void input (struct sock *sk, int len)
{
   struct sk_buff *skb;
   struct nlmsghdr *nlh = NULL;
  u8 *payload = NULL;

while ((skb = skb_dequeue(&sk->receive_queue))!= NULL)
  {
     /* process netlink message pointed by skb->data */
     nlh = (struct nlmsghdr *)skb->data;
     payload = NLMSG_DATA(nlh);
     /* process netlink message with header pointed by
     * nlh        and payload pointed by payload
     */
   }
}
 

 

回调函数input()是在发送进程的系统调用sendmsg()的上下文被调用的。如果input函数中处理消息很快的话,一切都没有问题。但是如果处理netlink消息花费很长时间的话,我们则希望把消息的处理部分放在input()函数的外面,因为长时间的消息处理过程可能会阻止其它系统调用进入内核。取而代之,我们可以牺牲一个内核线程来完成后续的无限的的处理动作。
使用

[Copy to clipboard] [ - ]
CODE:
skb = skb_recv_datagram(nl_sk)

来接收消息。nl_sk是netlink_kernel_create()函数返回的netlink socket,然后,只需要处理skb->data指针指向的netlink消息就可以了。
这个内核线程会在nl_sk中没有消息的时候睡眠。因此,在回调函数input()中我们要做的事情就是唤醒睡眠的内核线程,像这样的方式:

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

 

这就是一个升级版的内核与用户空间的通讯模型,它提高了上下文切换的粒度。
从内核中发送netlink消息
就像从用户空间发送消息一样,内核在发送netlink消息时也需要设置源netlink地址和目的netlink地址。假设结构体struct sk_buff * skb指向存储着要发送的netlink消息的缓冲区,源地址可以这样设置:

NETLINK_CB(skb).groups = local_groups;
NETLINK_CB(skb).pid = 0;   /* from kernel */
目的地址可以这样设置:
NETLINK_CB(skb).dst_groups = dst_groups;
NETLINK_CB(skb).dst_pid = dst_pid;

 

这些信息并不存储在 skb->data中,相反,它们存储在socket缓冲区的netlink控制块skb中.
发送一个单播消息,使用:

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


ssk是by netlink_kernel_create()函数返回的netlink socket, skb->data指向需要发送的netlink消息体,如果使用公式一的话,pid是接收程序的pid,noblock表明当接收缓冲区不可用时是否应该阻塞还是立即返回一个失败信息。
你同样可以从内核发送一个多播消息。下面的函数同时把一个netlink消息发送给pid指定的进程和group标识的多个组。

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


group的值是接收消息的各个组的比特位进行与运算的结果。Allocation是内核内存的申请类型。通常情况下在中断上下文使用GFP_ATOMIC,否则使用GFP_KERNEL。这是由于发送多播消息时,API可能需要申请一个或者多个socket缓冲区并进行拷贝所引起的。
从内核空间关闭netlink socket
netlink_kernel_create()函数返回的netlink socket为struct sock *nl_sk,我们可以通过访问下面的API来从内核空间关闭这个netlink socket:
sock_release(nl_sk->socket);
到目前为止,我们已经演示了netlink编程概念的最小代码框架。接着我们会使用NETLINK_TEST协议类型,并且假设它已经被添加到内核头文件中了。这里列举的内核模块代码只是与netlink相关的,所以,你应该把它插入到一个完整的内核模块代码当中去,这样的完整代码在其它代码中可以找到很多。

内核与用户空间的单工通讯:
在这个例子中,一个用户态进程发送一个netlink消息给内核模块,内核模块把消息回送给发送进程,下面是用户态的代码:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define NETLINK_TEST 17
#define MAX_PAYLOAD 1024  /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main(int argc, char* argv[])
{
        sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
        memset(&msg, 0, sizeof(msg));
        memset(&src_addr, 0, sizeof(src_addr));
        src_addr.nl_family = AF_NETLINK;
        src_addr.nl_pid = getpid();  /* self pid */
        src_addr.nl_groups = 0;  /* not in mcast groups */
        bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
        memset(&dest_addr, 0, sizeof(dest_addr));
        dest_addr.nl_family = AF_NETLINK;
        dest_addr.nl_pid = 0;   /* For Linux Kernel */
        dest_addr.nl_groups = 0; /* unicast */

        nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
        /* Fill the netlink message header */
        nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
        nlh->nlmsg_pid = getpid();  /* self pid */
        nlh->nlmsg_flags = 0;
        /* Fill in the netlink message payload */
        strcpy(NLMSG_DATA(nlh), "Hello you!");

        iov.iov_base = (void *)nlh;
        iov.iov_len = nlh->nlmsg_len;
        msg.msg_name = (void *)&dest_addr;
        msg.msg_namelen = sizeof(dest_addr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;

        sendmsg(sock_fd, &msg, 0);

        /* Read message from kernel */
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
        recvmsg(sock_fd, &msg, 0);
        printf(" Received message payload: %s/n",
        NLMSG_DATA(nlh));

         /* Close Netlink Socket */
        close(sock_fd);
}

 

下面是内核代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>

#define NETLINK_TEST 17
struct sock *nl_sk = NULL;
void nl_data_ready (struct sock *sk, int len)
{
          wake_up_interruptible(sk->sk_sleep);
}

void test_netlink(void)
{
        struct sk_buff * skb = NULL;
        struct nlmsghdr * nlh = NULL;
        int err;
        u32 pid;

        nl_sk = netlink_kernel_create(NETLINK_TEST, nl_data_ready);
        /* wait for message coming down from user-space */
        skb = skb_recv_datagram(nl_sk, 0, 0, &err);

        nlh = (struct nlmsghdr *)skb->data;
        printk("%s: received netlink message payload:%s/n", __FUNCTION__, (char*)NLMSG_DATA(nlh));

        pid = nlh->nlmsg_pid; /*pid of sending process */
        NETLINK_CB(skb).groups = 0; /* not in mcast group */
        NETLINK_CB(skb).pid = 0;      /* from kernel */
        NETLINK_CB(skb).dst_pid = pid;
        NETLINK_CB(skb).dst_groups = 0;  /* unicast */
        netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
        sock_release(nl_sk->sk_socket);
}

int init_module()
{
        test_netlink();
        return 0;
}
void cleanup_module( )
{
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("duanjigang");
 


在加载完上面的内核代码生成的模块以后,运行用户态程序,可以看到下面输出:

[Copy to clipboard] [ - ]
CODE:
Received message payload: Hello you!

并且,下面的信息会在dmesg中输出:

[Copy to clipboard] [ - ]
CODE:
netlink_test: received netlink message payload:
Hello you!

内核与用户空间之间的多点传输通讯:

在这个例子中,两个用户空间程序监听相同的netlink多播组。内核模块通过netlink socket向这个组发送一个消息,所有的应用程序都收到了这个消息,下面是用户态的代码:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define NETLINK_TEST 17
#define MAX_PAYLOAD 1024  /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main(int argc, char* argv[])
{

        sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
        memset(&src_addr, 0, sizeof(src_addr));
        memset(&msg, 0, sizeof(msg));
       
        src_addr.nl_family = AF_NETLINK;
        src_addr.nl_pid = getpid();  /* self pid */
        /* interested in group 1<<0 */
        src_addr.nl_groups = 1;
        bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
        memset(&dest_addr, 0, sizeof(dest_addr));
        nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));

        iov.iov_base = (void *)nlh;
        iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
        msg.msg_name = (void *)&dest_addr;
        msg.msg_namelen = sizeof(dest_addr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;

        printf("Waiting for message from kernel/n");

         /* Read message from kernel */
        recvmsg(sock_fd, &msg, 0);
       
        printf("Received message payload: %s/n", NLMSG_DATA(nlh));
        close(sock_fd);        
}


下面是内核态的代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>

#define MAX_PAYLOAD 1024
#define NETLINK_TEST 17

struct sock *nl_sk = NULL;

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

void test_netlink(void)
{
        struct sk_buff *skb = NULL;
        struct nlmsghdr *nlh;
       
        nl_sk = netlink_kernel_create(NETLINK_TEST, nl_data_ready);
        skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD),GFP_KERNEL);
        nlh = (struct nlmsghdr *)skb->data;
        nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
        nlh->nlmsg_pid = 0;  /* from kernel */
        nlh->nlmsg_flags = 0;
        nlh = (struct nlmsghdr *) skb_put(skb, NLMSG_SPACE(MAX_PAYLOAD));
        strcpy(NLMSG_DATA(nlh), "Greeting from kernel!");
        /* sender is in group 1<<0 */
        NETLINK_CB(skb).groups = 1;
        NETLINK_CB(skb).pid = 0;  /* from kernel */
        NETLINK_CB(skb).dst_pid = 0;  /* multicast */
        /* to mcast group 1<<0 */
        NETLINK_CB(skb).dst_groups = 1;
        /*multicast the message to all listening processes*/
        netlink_broadcast(nl_sk, skb, 0, 1, GFP_KERNEL);
        //printk("%s/n", NLMSG_DATA(nlh));

        sock_release(nl_sk->sk_socket);        
}

int init_module()
{
        test_netlink();
        return 0;
}
void cleanup_module( )
{
       
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("duanjigang");
 


假设用户态的代码编译后生成的可执行程序为nl_recv,我们可以运行nl_recv的两个实例:

[Copy to clipboard] [ - ]
CODE:
./nl_recv &
Waiting for message from kernel
./nl_recv &
Waiting for message from kernel

然后,当我们加载了内核模块后,两个nl_recv的实例都应该会输出如下的消息:

[Copy to clipboard] [ - ]
CODE:
Received message payload: Greeting from kernel!
Received message payload: Greeting from kernel!

总结: Netlink socket是用户空间程序和内核模块之间一种很灵活的通讯接口。它为应用程序和内核程序都提供了一个方便使用的API。它还提供了高级的通讯特性,比如全双工,缓冲I/O,多点传送和异步通讯,这些都是其他内核/用户态的IPC所不具有的功能。

 

Netlink基本使用 Netlink基本使用 1. 什么是Netlink 什么是Netlink?Netlink是linux提供的用于内核和用户态进程之间的通信方式。但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的广播特性时。 那么Netlink有什么优势呢?一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,但是Netlink可以实现双工通信。 Ne 阅读详情

相关推荐

Linux netlink socket使用总结

一、netlink机制的引入Linux提供了多种机制来完成内核空间与用户空间之间的数据交换,分别有内核启动参数、模块参数、sysfs、sysctl、系统调用、procfs、seq_file、debugfs、relayfs。其中,模块参数、sysfs、sysctl、procfs、seq_file、debugfs、relayfs是基于文件系统的通信机制,用于内核空间向用户空间输出信息;sysctl、系统

sprintwind的专栏 1万+

Linux非root用户使用socket出现“Permission denied”解决方式

非root用户使用socket出现“Permission denied”解决方式

chengwei_peng 1万+

IMX6ULL移植hostapd2.5使用AP模式报错解决

1.致命错误: netlink/genl/genl,h: 没有那个文件或目录 2.arm-linux-gnueabihf/bin/ld: 找不到 -lnl 两个报错解决

weixin_43887897的博客 444

LINUX netlink机制

Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态 socket 协议(NETLINK_USERSOCK),防火

bingyang_xue的专栏 989

[Android][Linux][Netlink]内核态和用户态使用netlink方法总结

一、Netlink概述 二、用户态如何使用netlink 三、内核态如何使用netlink 四、内核态和用户态之间使用netlink实例(NETD) 五、内核态和用户态之间使用netlink实例(SYN FIREWALL) 一、Netlink概述 (本段摘抄自网络) Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket A

ganjinrui的专栏 3586

linux netlink机制详解

netlink是一种基于网络的机制,允许在内核内部以及内核与用户层之间进行通信。最早在内核2.2引入,旨在替代笨拙的IOCTL,IOCTL不能从内核向用户空间发送异步消息,而且必须定义IOCTL号。 Netlink协议定义在RFC3549中。以前是可以编译成模块,现在直接集成到内核了。与profs和sysfs相比,有一些优势如下: 不需要轮询;系统调用和ioctl也能从用户层想内核传递信息,但是难以实现,另外netlink不会和模块冲突;内核可以直接向用户层发送信息;使用标准的套接字即可。 /proc

oqqYuJi12345678的博客 2479

netlink socket编程之why & how

netlink socket编程之why & how 作者: Kevin Kaichuan He@2005-1-5 翻译整理:duanjigang @2008-9-15duanjigang1983@126.com>原文: http://www.linuxjournal.com/article/7356   开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排

么刚的专栏 4417

Netlink系列1概述

netlink用于实现内核间双向通信、用户空间与内核空间的双向通信,是比较优秀的方式之一,例如比ioctl方式要好很多,而且功能更加强大。通俗的讲,Netlink获取\设置:网络接口配置、地址、路由、邻居发现、防火墙、ARP消息、netfilter等众多功能。直到目前,Netli

cjchina的专栏 1373

如何使用ios进行nmap扫描,如何处理ish扫描报错?(can not link AF_NETLINK)

首先,如果要在ios使用nmap的话我们需要下载ish软件,在appstore内就能搜索到,具体简易配置参考<TY汤圆>写的。nmap是一种常见的端口扫描工具,用于在本地和外部检查打开/过滤/关闭端口。只要能出结果就行,虽然它可能很烦人,但它们似乎不会影响nmap的基础功能。如果已经完成如上配置,那么废话不多说直接开始,上指令。尝试运行:nmap <你的网段>/24。#如果有密码则输入你的密码。有如标题报错直接往下翻。

weixin_70956597的博客 1616

Linux之Netlink

Netlink是Linux系统中用户态与内核态之间双向通信的机制,基于套接字接口,采用消息传递协议。相比传统方法如ioctl、procfs等,Netlink具有双向异步、结构化数据、可扩展和多播支持等优势,适用于网络子系统配置和内核模块开发。它通过定义不同协议族(如NETLINK_ROUTE、NETLINK_GENERIC)实现多种功能,是现代Linux网络工具的核心通信技术。

DD'Notes 1010

linux内核协议栈 netfilter 之连接跟踪子系统AF_INET钩子函数

1连接跟踪子系统钩子函数概述 如《linux内核协议栈 netfilter 之 ip 层的 hook 注册概述以及 hook 的调用场景》中的1.4小结所述,为了支持连接跟踪子系统,AF_INET协议族在4个HOOK点共注册了8个钩子函数,每个HOOK点有两个。这些钩子函数可分为入口(PRE_ROUTING和LOCAL_IN)和出口(LOCAL_OUT和POST_ROUTING)两大类。 1.1数据包流向 在分析之前,我们再次回顾一下数据包在连接跟踪模块中的走向。 1.1.1 发往本地的数...

1229

netlink(1)_socket及netlink 相关宏

1.基础概念: 内核空间:Linux 系统包含内核空间和用户空间,一般将比较重要的内容放置内核空间运行。 用户空间:把GUI ,管理,控制程序放置用户空间运行。 IPC: 内核空间和用户空间之间需要进行通信,如何通信呢?这就需要IPC. 常用的IPC 有ioctl,系统调用,netlink socket. 2. netlink socket 基本原理: netlink socket 是IPC 中的一种,是一种异步通信机制。发送的消息只暂存在socket 接收缓存中,不需要等待接收者立即处理。 使用netl

gege_hxg的博客 2141

用Python实现四叉树(quad tree)

import numpy as np #正方形类 class square: #lx、ly为左上角坐标(x对应行号,y对应列号),rx、ry为右下角坐标 def __init__(self,lx,ly,rx,ry): self.lx=lx self.ly=ly self.rx=rx self.ry=ry #检查该矩阵中是否满足条件 #参数:原矩阵,阈值 #返回值:布尔类型 def check(s

weixin_44164489的博客 5868

linux netlink详解1-netlink初始化

转载自:https://www.cnblogs.com/xinghuo123/p/13782009.html Netlink 是一种IPC(Inter Process Commumicate)机制,它是一种用于内核与用户空间通信的机制,同时它也以用于进程间通信(Netlink更多用于内核通信,进程之间通信更多使用Unix域套接字)。在一般情况下,用户态和内核态通信会使用三种传统的通信方式: 1 ioctl; 2 sysfs属性文件; 3 proc fs属性文件; 但这3种通信方式都是同步通信方式,.

wsfcc的专栏 1115

MATLAB实战:5步搞定铣削稳定性叶瓣图(附完整代码解析)

本文提供了一份基于MATLAB的铣削稳定性叶瓣图实战指南。通过5个清晰步骤,从动力学原理出发,详细解析了利用半离散法构建稳定性判据、模块化代码设计、高效参数扫描及高级可视化的完整流程,并附有完整代码,帮助工程师快速预测和规避加工颤振,优化铣削工艺参数。

weixin_29197051的博客 195

振动信号相位测量的关键技术解析

本文深入解析了振动信号相位测量的关键技术,强调相位是振动信号的“身份证”,对于动平衡和故障诊断至关重要。文章详细阐述了键相传感器与振动传感器的选型、安装要点,以及时域和频域(FFT)核心算法,并针对现场噪声、转速波动等挑战提供了实战解决方案,帮助工程师实现精准、稳定的相位测量。

vv45678的博客 838

Linux 网络之netlink 简介

Linux 网络之netlink 简介以及一个简单使用demo

小立仔 1万+

2025届程序技术类笔试题

希尔排序在直接插入排序的基础上进行了改进,它通过比较相距一定间隔的元素来进行,各轮比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一轮排序为止。某人逆着水流的方向向岸边划船,每次行进前进x米后需要休息一下,休息期间船会自行倒退y米,每次休息后再次行进,前进距离缩短1/5,若起始距离岸边为m米,问:能否到达岸上,需要行进多少次才能上岸。直接插入排序的原理是将第i个元素插入前面i-1个已排好序的记录中,将比第i个元素大的记录依次向后移动一个位置,将第i个元素插入空位。第二轮相邻比较,后移2次。

博客 595

af_netlink_2、netlink简介

Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态 socket 协议(NETLINK_USERSOCK),防火...

weixin_39832448的博客 1950

uniapp [全端兼容] - 实现连接蓝牙电子秤+监听数据转换并实时返回示例代码,uniApp实现BLE蓝牙连接多个电子秤设备、蓝牙通讯协议对接详细教程(全平台兼容H5、微信小程序、苹果安卓App)

uniapp连接蓝牙电子秤,uniapp如何连接电子秤蓝牙,蓝牙称重,实现监听数据转换并实时返回,多蓝牙,uni-app电子秤16进制蓝牙通讯协议对接,uniapp微信小程序蓝牙获取电子秤的值,uniapp蓝牙称重电子秤,蓝牙电子秤硬件,APP端连接蓝牙秤获取重量实时显示在页面上蓝牙秤,插件组件,蓝牙连接失败,uniapp安卓苹果ios连接蓝牙电子秤没反应,uniapp微信小程序 蓝牙低功耗连接发送数据监听接收,uni-app小程序蓝牙连接、读写数据全过程,uniapp连接蓝牙体脂秤,蓝牙搜索不到怎么办,如

🏆 前端领域优质创作者 1万+

C++-ONNX:用onnxruntime部署自己的模型【Pytorch导出.onnx】【Tensorflow导出.onnx】【C++用onnxruntime框架部署并推理】

微软联合Facebook等在2017年搞了个深度学习以及机器学习模型的格式标准–ONNX,旨在将所有模型格式统一为一致,更方便地实现模型部署。现在大多数的深度学习框架都支持ONNX模型转出并提供相应的导出接口。ONNXRuntime(Open Neural Network Exchange)是微软推出的一款针对ONNX模型格式的推理框架,用户可以非常便利的用其运行一个onnx模型。ONNXRuntime支持多种运行后端包括CPU,GPU,TensorRT,DML等。

u013250861的博客 5766

netlink

Netlink套接字是用以实现内核进程和用户进程通信的一种特殊的进程间通信(IPC),从linux 2.2开始引入内核,当时名为AF_NETLINK,旨在提供一种更灵活的内核和用户空间的通信方法,用以替换笨拙的IOCTL.IOCTL 方式通信,需要定义IOCTL号,而且只能从用户空间到内核空间空间单向通信,相比之下netlink具有一下有点。netlink可以用于用户进程通信,不过一般推荐使用,如果进程之间使用socket通信,一般是 Unix域socket。

我的专栏 1602

3.7 改造模型:为GPT添加一个分类头(Classification Head)

本文介绍如何改造GPT模型用于文本分类任务。主要内容包括: 分类头设计思路:GPT作为语言模型需要添加分类头才能适应分类任务,类似于为语言大师添加裁判功能。 序列表示选择策略: 分析三种常见方法(CLS标记、平均池化、最后词元表示) 推荐使用最后词元表示,因其最能反映GPT的单向信息流特性 代码实现: 构建GPT2ForSequenceClassification类 包含预训练GPT主体和线性分类头 使用attention_mask智能定位句子结尾 通过高级索引提取最后词元特征 该方法通过简单改造使GPT模

yonggeit的博客 2972

netlink知识

(1)netlink使用cb字段传输数据。(sk_buff是Linux在其协议栈里传送的结构体,也就是所谓的“包”) (2)用户空间的netlink套接字很简单,和传统的网络套接字类似,只是修改了一些参数:        sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);        第三个参数指定netlink协议类型,可以是用户自定义

haokaihaohe110的专栏 2545

廉价却有效?ESD防护中的电容

摘要:在成本敏感型电子产品设计中,多层陶瓷电容(MLCC)可作为TVS二极管的替代方案用于静电放电(ESD)防护。其工作原理是通过电容充放电特性吸收静电能量,而非电压钳位。计算表明100nF/50V MLCC可有效缓冲8kV静电冲击,但存在吸收能量有限、无自恢复等局限。建议将MLCC作为一级缓冲措施,配合TVS等器件形成多级防护,并优先选择X7R材质、100nF容值、50V耐压的0402/0603封装MLCC。该方案在低速信号和电源引脚防护中具有显著成本优势,但不能完全替代专业ESD抑制器。

硬核科技 431

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

netlink1

<br />netlink使用了软中断而不是内核线程来接收数据,这样就可以保证数据接收的实时性。<br />用户态程序:<br />1.1 为了创建一个 netlink socket,用户需要使用socket(PF_NETLINK, SOCK_RAW, netlink_type)创建一个套接字。第一个参数协议簇是 PF_NETLINK<br />第二个参数必须是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型。<br />1

babyfans的专栏 928

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

上一篇: 常用电平转换方案
下一篇: linux jiffies tick hz
WeekyHuang
博客等级 码龄21年 5粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值