Linux Netlinkの基本的な使い方
Netlinkの基本的な使い方
——lvyilong316
1. Netlinkとは
Netlinkとは何ですか? Netlink は、カーネル プロセスとユーザー モード プロセス間の通信のために Linux によって提供される通信方法です。ただし、Netlink は主にユーザー空間とカーネル空間の間の通信に使用されますが、ユーザー空間内の 2 つのプロセス間の通信にも使用できることに注意してください。プロセス間で通信する方法は他にもたくさんありますが、Netlink は通常は使用されません。 Netlink のブロードキャスト機能を使用する必要がある場合を除きます。
それでは、Netlink の利点は何でしょうか?一般に、ユーザー空間とカーネル空間の間には、/proc、ioctl、Netlink の 3 つの通信方法があります。最初の 2 つは一方向ですが、Netlink は二重通信を実現できます。
Netlink プロトコルは、BSDsocket および AF_NETLINK アドレス ファミリ (アドレス ファミリ) に基づいており、32 ビット ポート番号アドレッシング (以前は PID と呼ばれていました) を使用し、各 Netlink プロトコル (またはマニュアルでは netlinkfamily と呼ばれるバス) は通常、1 つまたはカーネル サービス/コンポーネントのグループ。ルーティングおよびリンク情報を取得および設定するための NETLINK_ROUTE、カーネルがユーザー空間の udev プロセスに通知を送信するための NETLINK_KOBJECT_UEVENT など。 Netlink には次のような特徴があります:
①全二重、非同期通信をサポートします (もちろん同期もサポートします)
②ユーザー空間は標準の BSDsocket インターフェイスを使用できます (ただし、netlink はプロトコル パッケージの構築と解析プロセスをシールドしません。 libnl およびその他のサードパーティ ライブラリを使用することをお勧めします)
③カーネル空間で専用のカーネル API インターフェイスを使用します
④マルチキャストをサポートします (したがって、「バス」通信とメッセージ サブスクリプションをサポートします)
⑤プロセス コンテキストと割り込みコンテキストに使用できますカーネル側で
Netlink を学ぶにはどうすればよいですか? Netlink と UDPsocket を比較するのが最善の方法だと思います。だって、本当に似ているところがあるんです。 AF_NETLINK は AF_INET に対応し、プロトコル ファミリです。一方、NETLINK_ROUTE および NETLINK_GENERIC はプロトコルで、UDP に対応します。
次に、Netlink と UDPsocket の違いに主に焦点を当てます。最も重要な点は、UDPsocket を使用してデータ パケットを送信する場合、ユーザーは UDP データ パケットのヘッダーを構築する必要がないということです。カーネル プロトコル スタックはオリジナルを使用します。および宛先アドレス (sockaddr_in) ヘッダー情報を入力します。ただし、Netlink ではヘッダーを自分で構築する必要があります (このヘッダーの使用については後で説明します)。
通常、Netlink を使用するときは、カーネルによって予約された NETLINK_GENERIC (linux/netlink.h で定義) を使用するか、独自のカスタマイズされたプロトコルを使用することができます。カーネル番号が占有していないプロトコル。以下では、例を書くために定義したプロトコルとして NETLINK_TEST を使用します (注: ユーザー モード コードとカーネル モード コードの両方で定義を見つけることができる限り、カスタム プロトコルを linux/netlink.h に追加する必要はありません)。 UDP を使用してメッセージを送信するには、sendto と sendmsg の 2 つの方法があることがわかっています。Netlink もこれら 2 つの方法をサポートしています。まずはsendmsgの使い方を見てみましょう。
2. ユーザーモードのデータ構造
まず、いくつかの重要なデータ構造間の関係を見てみましょう:
2.1structmsghdr
msghdr この構造はソケット生成で使用され、Netlink に限定されません。 , ここではあまり説明しません。この構造の機能をよりよく理解する方法を説明してください。ソケット メッセージの送受信関数には通常、recv/send、readv/writev、recvfrom/sendto というペアがあることがわかっています。もちろん、最初の 3 つの関数のペアにはそれぞれ独自の関数があり、recvmsg/sendmsg には最初の 3 つのペアの関数がすべて含まれており、もちろん独自の特別な用途があります。 msghdr の最初の 2 つのメンバーは、recvfrom/sendto の機能を満たし、真ん中の 2 つのメンバー msg_iov および msg_iovlen は、readv/writev の機能を満たし、最後の msg_flags は、recv/send の flag の機能を満たします。残り msg_control と msg_controllen は、recvmsg/sendmsg の独自の機能を満たします。
2.2Structsockaddr_ln
Structsockaddr_ln は、通常のソケットプログラミングにおける sockaddr_in と同じ機能を持ちます。
structsockaddr_nl{} の詳細な定義と説明は次のとおりです:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>struct sockaddr_nl<br /></li><li>{<br /></li><li>sa_family_t nl_family; /*该字段总是为AF_NETLINK */<br /></li><li>unsigned short nl_pad; /* 目前未用到,填充为0*/<br /></li><li>__u32 nl_pid; /* process pid */<br /></li><li>__u32 nl_groups; /* multicast groups mask */<br /></li><li>};</li></ol>
(1)nl_pid:在Netlink规范里,PID全称是Port-ID(32bits),其主要作用是用于唯一的标识一个基于netlink的socket通道。通常情况下nl_pid都设置为当前进程的进程号。前面我们也说过,Netlink不仅可以实现用户-内核空间的通信还可使现实用户空间两个进程之间,或内核空间两个进程之间的通信。该属性为0时一般指内核。
(2)nl_groups:如果用户空间的进程希望加入某个多播组,则必须执行bind()系统调用。该字段指明了调用者希望加入的多播组号的掩码(注意不是组号,后面我们会详细讲解这个字段)。如果该字段为0则表示调用者不希望加入任何多播组。对于每个隶属于Netlink协议域的协议,最多可支持32个多播组(因为nl_groups的长度为32比特),每个多播组用一个比特来表示。
2.3structnlmsghdr
Netlink的报文由消息头和消息体构成,structnlmsghdr即为消息头。消息头定义在文件里,由结构体nlmsghdr表示:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>struct nlmsghdr<br /></li><li>{<br /></li><li>__u32 nlmsg_len; /* Length of message including header */<br /></li><li>__u16 nlmsg_type; /* Message content */<br /></li><li>__u16 nlmsg_flags; /* Additional flags */<br /></li><li>__u32 nlmsg_seq; /* Sequence number */<br /></li><li>__u32 nlmsg_pid; /* Sending process PID */<br /></li><li>};</li></ol>
消息头中各成员属性的解释及说明:
(1)nlmsg_len:整个消息的长度,按字节计算。包括了Netlink消息头本身。
(2)nlmsg_type:消息的类型,即是数据还是控制消息。目前(内核版本2.6.21)Netlink仅支持四种类型的控制消息,如下:
a)NLMSG_NOOP-空消息,什么也不做;
b)NLMSG_ERROR-指明该消息中包含一个错误;
c)NLMSG_DONE-如果内核通过Netlink队列返回了多个消息,那么队列的最后一条消息的类型为NLMSG_DONE,其余所有消息的nlmsg_flags属性都被设置NLM_F_MULTI位有效。
d)NLMSG_OVERRUN-暂时没用到。
(3)nlmsg_flags:附加在消息上的额外说明信息,如上面提到的NLM_F_MULTI。
那消息体怎么设置呢?可以使用NLMSG_DATA,具体见后面例子。
3.用户态范例一
l客户端1
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>#include <sys/stat.h><br /></li><li>#include <unistd.h><br /></li><li>#include <stdio.h><br /></li><li>#include <stdlib.h><br /></li><li>#include <sys/socket.h><br /></li><li>#include <sys/types.h><br /></li><li>#include <string.h><br /></li><li>#include <br /></li><li>#include <linux/netlink.h><br /></li><li>#include <linux/socket.h><br /></li><li>#include <errno.h><br /></li><li>#define MAX_PAYLOAD 1024 // maximum payload size<br /></li><li>#define NETLINK_TEST 25 //自定义的协议<br /></li><li>int main(int argc, char* argv[])<br /></li><li>{<br /></li><li>int state;<br /></li><li>struct sockaddr_nl src_addr, dest_addr;<br /></li><li>struct nlmsghdr *nlh = NULL; //Netlink数据包头<br /></li><li>struct iovec iov;<br /></li><li>struct msghdr msg;<br /></li><li>int sock_fd, retval;<br /></li><li>int state_smg = 0;<br /></li><li>// Create a socket<br /></li><li>sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);<br /></li><li>if(sock_fd == -1){<br /></li><li>printf("error getting socket: %s", strerror(errno));<br /></li><li>return -1;<br /></li><li>}<br /></li><li>// To prepare binding<br /></li><li>memset(&src_addr, 0, sizeof(src_addr));<br /></li><li>src_addr.nl_family = AF_NETLINK;<br /></li><li>src_addr.nl_pid = 100; //A:设置源端端口号<br /></li><li>src_addr.nl_groups = 0;<br /></li><li>//Bind<br /></li><li>retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));<br /></li><li>if(retval < 0){<br /></li><li>printf("bind failed: %s", strerror(errno));<br /></li><li>close(sock_fd);<br /></li><li>return -1;<br /></li><li>}<br /></li><li>// To orepare create mssage<br /></li><li>nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));<br /></li><li>if(!nlh){<br /></li><li>printf("malloc nlmsghdr error!\n");<br /></li><li>close(sock_fd);<br /></li><li>return -1;<br /></li><li>}<br /></li><li>memset(&dest_addr,0,sizeof(dest_addr));<br /></li><li>dest_addr.nl_family = AF_NETLINK;<br /></li><li>dest_addr.nl_pid = 0; //B:设置目的端口号<br /></li><li>dest_addr.nl_groups = 0;<br /></li><li>nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);<br /></li><li>nlh->nlmsg_pid = 100; //C:设置源端口<br /></li><li>nlh->nlmsg_flags = 0;<br /></li><li>strcpy(NLMSG_DATA(nlh),"Hello you!"); //设置消息体<br /></li><li>iov.iov_base = (void *)nlh;<br /></li><li>iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);<br /></li><li>//Create mssage<br /></li><li>memset(&msg, 0, sizeof(msg));<br /></li><li>msg.msg_name = (void *)&dest_addr;<br /></li><li>msg.msg_namelen = sizeof(dest_addr);<br /></li><li>msg.msg_iov = &iov;<br /></li><li>msg.msg_iovlen = 1;<br /></li><li>//send message<br /></li><li>printf("state_smg\n");<br /></li><li>state_smg = sendmsg(sock_fd,&msg,0);<br /></li><li>if(state_smg == -1)<br /></li><li>{<br /></li><li>printf("get error sendmsg = %s\n",strerror(errno));<br /></li><li>}<br /></li><li>memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));<br /></li><li>//receive message<br /></li><li>printf("waiting received!\n");<br /></li><li>while(1){<br /></li><li>printf("In while recvmsg\n");<br /></li><li>state = recvmsg(sock_fd, &msg, 0);<br /></li><li>if(state<0)<br /></li><li>{<br /></li><li>printf("state<1");<br /></li><li>}<br /></li><li>printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));<br /></li><li>}<br /></li><li>close(sock_fd);<br /></li><li>return 0;<br /></li><li>}</li></ol>
上面程序首先向内核发送一条消息;“Helloyou”,然后进入循环一直等待读取内核的回复,并将收到的回复打印出来。如果看上面程序感觉很吃力,那么应该首先复习一下UDP中使用sendmsg的用法,特别时structmsghdr的结构要清楚,这里再赘述。下面主要分析与UDP发送数据包的不同点:
1.socket地址结构不同,UDP为sockaddr_in,Netlink为structsockaddr_nl;
2.与UDP发送数据相比,Netlink多了一个消息头结构structnlmsghdr需要我们构造。
注意代码注释中的A、B、C三处分别设置了pid。首先解释一下什么是pid,网上很多文章把这个字段说成是进程的pid,其实这完全是望文生义。这里的pid和进程pid没有什么关系,仅仅相当于UDP的port。对于UDP来说port和ip标示一个地址,那对我们的NETLINK_TEST协议(注意Netlink本身不是一个协议)来说,pid就唯一标示了一个地址。所以你如果用进程pid做为标示当然也是可以的。当然同样的pid对于NETLINK_TEST协议和内核定义的其他使用Netlink的协议是不冲突的(就像TCP的80端口和UDP的80端口)。
下面分析这三处设置pid分别有什么作用,首先A和B位置的比较好理解,这是在地址(sockaddr_nl)上进行的设置,就是相当于设置源地址和目的地址(其实是端口),只是注意B处设置pid为0,0就代表是内核,可以理解为内核专用的pid,那么用户进程就不能用0做为自己的pid吗?这个只能说如果你非要用也是可以的,只是会产生一些问题,后面在分析。接下来看为什么C处的消息头仍然需要设置pid呢?这里首先要知道一个前提:内核不会像UDP一样根据我们设置的原、目的地址为我们构造消息头,所以我们不在包头写入我们自己的地址(pid),那内核怎么知道是谁发来的报文呢?当然如果内核只是处理消息不需要回复进程的话舍不设置这个消息头pid都可以。
所以每个pid的设置功能不同:A处的设置是要设置发送者的源地址,有人会说既然源地址又不会自动填充到报文中,我们为什么还要设置这个,因为你还可能要接收回复啊。就像寄信,你连“门牌号”都没有,即使你在写信时候写上你的地址是100号,对方回信目的地址也是100号,但是邮局发现根本没有这个地址怎么可能把信送到你手里呢?所以A的主要作用是注册源地址,保证可以收到回复,如果不需要回复当然可以简单将pid设置为0;B处自然就是收信人的地址,pid为0代表内核的地址,假如有一个进程在101号上注册了地址,并调用了recvmsg,如果你将B处的pid设置为101,那数据包就发给了另一个进程,这就实现了使用Netlink进行进程间通信;C相当于你在信封上写的源地址,通常情况下这个应该和你的真实地址(A)处注册的源地址相同,当然你要是不想收到回信,又想恶搞一下或者有特殊需求,你可以写成其他进程注册的pid(比如101)。这和我们现实中寄信是一样的,你给你朋友写封情书,把写信人写成你的另一个好基友,然后后果你懂得……
好了,有了这个例子我们就大概知道用户态怎么使用Netlink了,至于我们没有用到的nl_groups等其他信息后面讲到再说,下面看下内核是怎么处理Netlink的。
4.内核Netlinkapi
4.1创建netlinksocket
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>struct sock *netlink_kernel_create(struct net *net,<br /></li><li>int unit,unsigned int groups,<br /></li><li>void (*input)(struct sk_buff *skb),<br /></li><li>struct mutex *cb_mutex,struct module *module);</li></ol>
参数说明:
(1)net:是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量。
(2)unit:表示netlink协议类型,如NETLINK_TEST、NETLINK_SELINUX。
(3)groups:多播地址。
(4)input:为内核模块定义的netlink消息处理函数,当有消息到达这个netlinksocket时,该input函数指针就会被引用,且只有此函数返回时,调用者的sendmsg才能返回。
(5)cb_mutex:为访问数据时的互斥信号量。
(6)module:一般为THIS_MODULE。
4.2发送单播消息netlink_unicast
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)</li></ol>
参数说明:
(1)ssk:为函数netlink_kernel_create()返回的socket。
(2)skb:存放消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,宏NETLINK_CB(skb)就用于方便设置该控制块。
(3)pid:为接收此消息进程的pid,即目标地址,如果目标为组或内核,它设置为0。
(4)nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回;而如果为0,该函数在没有接收缓存可利用定时睡眠。
4.3发送广播消息netlink_broadcast
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)</li></ol>
前面的三个参数与netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。
4.4释放netlinksocket
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>void netlink_kernel_release(struct sock *sk)</li></ol>
5.内核态程序范例一
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>#include <linux/init.h><br /></li><li>#include <linux/module.h><br /></li><li>#include <linux/timer.h><br /></li><li>#include <linux/time.h><br /></li><li>#include <linux/types.h><br /></li><li>#include <net/sock.h><br /></li><li>#include <net/netlink.h><br /></li><li>#define NETLINK_TEST 25<br /></li><li>#define MAX_MSGSIZE 1024<br /></li><li>int stringlength(char *s);<br /></li><li>int err;<br /></li><li>struct sock *nl_sk = NULL;<br /></li><li>int flag = 0;<br /></li><li>//向用户态进程回发消息<br /></li><li>void sendnlmsg(char *message, int pid)<br /></li><li>{<br /></li><li>struct sk_buff *skb_1;<br /></li><li>struct nlmsghdr *nlh;<br /></li><li>int len = NLMSG_SPACE(MAX_MSGSIZE);<br /></li><li>int slen = 0;<br /></li><li>if(!message || !nl_sk)<br /></li><li>{<br /></li><li>return ;<br /></li><li>}<br /></li><li>printk(KERN_ERR "pid:%d\n",pid);<br /></li><li>skb_1 = alloc_skb(len,GFP_KERNEL);<br /></li><li>if(!skb_1)<br /></li><li>{<br /></li><li>printk(KERN_ERR "my_net_link:alloc_skb error\n");<br /></li><li>}<br /></li><li>slen = stringlength(message);<br /></li><li>nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);<br /></li><li>NETLINK_CB(skb_1).pid = 0;<br /></li><li>NETLINK_CB(skb_1).dst_group = 0;<br /></li><li>message[slen]= '\0';<br /></li><li>memcpy(NLMSG_DATA(nlh),message,slen+1);<br /></li><li>printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh));<br /></li><li>netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);<br /></li><li>}<br /></li><li>int stringlength(char *s)<br /></li><li>{<br /></li><li>int slen = 0;<br /></li><li>for(; *s; s++)<br /></li><li>{<br /></li><li>slen++;<br /></li><li>}<br /></li><li>return slen;<br /></li><li>}<br /></li><li>//接收用户态发来的消息<br /></li><li>void nl_data_ready(struct sk_buff *__skb)<br /></li><li>{<br /></li><li>struct sk_buff *skb;<br /></li><li>struct nlmsghdr *nlh;<br /></li><li>char str[100];<br /></li><li>struct completion cmpl;<br /></li><li>printk("begin data_ready\n");<br /></li><li>int i=10;<br /></li><li>int pid;<br /></li><li>skb = skb_get (__skb);<br /></li><li>if(skb->len >= NLMSG_SPACE(0))<br /></li><li>{<br /></li><li>nlh = nlmsg_hdr(skb);<br /></li><li>memcpy(str, NLMSG_DATA(nlh), sizeof(str));<br /></li><li>printk("Message received:%s\n",str) ;<br /></li><li>pid = nlh->nlmsg_pid;<br /></li><li>while(i--)<br /></li><li>{//我们使用completion做延时,每3秒钟向用户态回发一个消息<br /></li><li>init_completion(&cmpl);<br /></li><li>wait_for_completion_timeout(&cmpl,3 * HZ);<br /></li><li>sendnlmsg("I am from kernel!",pid);<br /></li><li>}<br /></li><li>flag = 1;<br /></li><li>kfree_skb(skb);<br /></li><li>}<br /></li><li>}<br /></li><li>// Initialize netlink<br /></li><li>int netlink_init(void)<br /></li><li>{<br /></li><li>nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 1,<br /></li><li>nl_data_ready, NULL, THIS_MODULE);<br /></li><li>if(!nl_sk){<br /></li><li>printk(KERN_ERR "my_net_link: create netlink socket error.\n");<br /></li><li>return 1;<br /></li><li>}<br /></li><li>printk("my_net_link_4: create netlink socket ok.\n");<br /></li><li>return 0;<br /></li><li>}<br /></li><li>static void netlink_exit(void)<br /></li><li>{<br /></li><li>if(nl_sk != NULL){<br /></li><li>sock_release(nl_sk->sk_socket);<br /></li><li>}<br /></li><li>printk("my_net_link: self module exited\n");<br /></li><li>}<br /></li><li>module_init(netlink_init);<br /></li><li>module_exit(netlink_exit);<br /></li><li>MODULE_AUTHOR("yilong");<br /></li><li>MODULE_LICENSE("GPL");</li></ol>
附上内核代码的Makefile文件:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>ifneq ($(KERNELRELEASE),)<br /></li><li>obj-m :=netl.o<br /></li><li>else<br /></li><li>KERNELDIR ?=/lib/modules/$(shell uname -r)/build<br /></li><li>PWD :=$(shell pwd)<br /></li><li>default:<br /></li><li>$(MAKE) -C $(KERNELDIR) M=$(PWD) modules<br /></li><li>endif</li></ol>
6.程序结构分析
我们将内核模块insmod后,运行用户态程序,结果如下:
这个结果复合我们的预期,但是运行过程中打印出“state_smg”卡了好久才输出了后面的结果。这时候查看客户进程是处于D状态的(不了解D状态的同学可以google一下)。这是为什么呢?因为进程使用Netlink向内核发数据是同步,内核向进程发数据是异步。什么意思呢?也就是用户进程调用sendmsg发送消息后,内核会调用相应的接收函数,但是一定到这个接收函数执行完用户态的sendmsg才能够返回。我们在内核态的接收函数中调用了10次回发函数,每次都等待3秒钟,所以内核接收函数30秒后才返回,所以我们用户态程序的sendmsg也要等30秒后才返回。相反,内核回发的数据不用等待用户程序接收,这是因为内核所发的数据会暂时存放在一个队列中。
再来回到之前的一个问题,用户态程序的源地址(pid)可以用0吗?我把上面的用户程序的A和C处pid都改为了0,结果一运行就死机了。为什么呢?我们看一下内核代码的逻辑,收到用户消息后,根据消息中的pid发送回去,而pid为0,内核并不认为这是用户程序,认为是自身,所有又将回发的10个消息发给了自己(内核),这样就陷入了一个死循环,而用户态这时候进程一直处于D。
另外一个问题,如果同时启动两个用户进程会是什么情况?答案是再调用bind时出错:“Addressalreadyinuse”,这个同UDP一样,同一个地址同一个port如果没有设置SO_REUSEADDR两次bind就会出错,之后我用同样的方式再Netlink的socket上设置了SO_REUSEADDR,但是并没有什么效果。
7.用户态范例二
之前我们说过UDP可以使用sendmsg/recvmsg也可以使用sendto/recvfrom,那么Netlink同样也可以使用sendto/recvfrom。具体实现如下:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>#include <sys/stat.h><br /></li><li>#include <unistd.h><br /></li><li>#include <stdio.h><br /></li><li>#include <stdlib.h><br /></li><li>#include <sys/socket.h><br /></li><li>#include <sys/types.h><br /></li><li>#include <string.h><br /></li><li>#include <br /></li><li>#include <linux/netlink.h><br /></li><li>#include <linux/socket.h><br /></li><li>#include <errno.h><br /></li><li>#define MAX_PAYLOAD 1024 // maximum payload size<br /></li><li>#define NETLINK_TEST 25<br /></li><li>int main(int argc, char* argv[])<br /></li><li>{<br /></li><li>struct sockaddr_nl src_addr, dest_addr;<br /></li><li>struct nlmsghdr *nlh = NULL;<br /></li><li>int sock_fd, retval;<br /></li><li>int state,state_smg = 0;<br /></li><li>// Create a socket<br /></li><li>sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);<br /></li><li>if(sock_fd == -1){<br /></li><li>printf("error getting socket: %s", strerror(errno));<br /></li><li>return -1;<br /></li><li>}<br /></li><li>// To prepare binding<br /></li><li>memset(&src_addr, 0, sizeof(src_addr));<br /></li><li>src_addr.nl_family = AF_NETLINK;<br /></li><li>src_addr.nl_pid = 100;<br /></li><li>src_addr.nl_groups = 0;<br /></li><li>//Bind<br /></li><li>retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));<br /></li><li>if(retval < 0){<br /></li><li>printf("bind failed: %s", strerror(errno));<br /></li><li>close(sock_fd);<br /></li><li>return -1;<br /></li><li>}<br /></li><li>// To orepare create mssage head<br /></li><li>nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));<br /></li><li>if(!nlh){<br /></li><li>printf("malloc nlmsghdr error!\n");<br /></li><li>close(sock_fd);<br /></li><li>return -1;<br /></li><li>}<br /></li><li>memset(&dest_addr,0,sizeof(dest_addr));<br /></li><li>dest_addr.nl_family = AF_NETLINK;<br /></li><li>dest_addr.nl_pid = 0;<br /></li><li>dest_addr.nl_groups = 0;<br /></li><li>nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);<br /></li><li>nlh->nlmsg_pid = 100;<br /></li><li>nlh->nlmsg_flags = 0;<br /></li><li>strcpy(NLMSG_DATA(nlh),"Hello you!");<br /></li><li>//send message<br /></li><li>printf("state_smg\n");<br /></li><li>sendto(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,(struct sockaddr*)(&dest_addr),sizeof(dest_addr));<br /></li><li>if(state_smg == -1)<br /></li><li>{<br /></li><li>printf("get error sendmsg = %s\n",strerror(errno));<br /></li><li>}<br /></li><li>memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));<br /></li><li>//receive message<br /></li><li>printf("waiting received!\n");<br /></li><li>while(1){<br /></li><li>printf("In while recvmsg\n");<br /></li><li>state=recvfrom(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,NULL,NULL);<br /></li><li>if(state<0)<br /></li><li>{<br /></li><li>printf("state<1");<br /></li><li>}<br /></li><li>printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));<br /></li><li>memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));<br /></li><li>}<br /></li><li>close(sock_fd);<br /></li><li>return 0;<br /></li><li>}</li></ol>
熟悉UDP编程的同学看到这个程序一定很熟悉,除了多了一个Netlink消息头的设置。但是我们发现程序中调用了bind函数,这个函数再UDP编程中的客户端不是必须的,因为我们不需要把UDPsocket与某个地址关联,同时再发送UDP数据包时内核会为我们分配一个随即的端口。但是对于Netlink必须要有这一步bind,因为Netlink内核可不会为我们分配一个pid。再强调一遍消息头(nlmsghdr)中的pid是告诉内核接收端要回复的地址,但是这个地址存不存在内核并不关心,这个地址只有用户端调用了bind后才存在。
再说一个体外话,我们看到这两个例子都是用户态首先发起的,那Netlink是否支持内核态主动发起的情况呢?当然是可以的,只是内核一般需要事件触发,这里,只要和用户态约定号一个地址(pid),内核直接调用netlink_unicast就可以了。

PHPタイプは、コードの品質と読みやすさを向上させるためのプロンプトがあります。 1)スカラータイプのヒント:php7.0であるため、基本データ型は、int、floatなどの関数パラメーターで指定できます。 3)ユニオンタイプのプロンプト:PHP8.0であるため、関数パラメーターまたは戻り値で複数のタイプを指定することができます。 4)Nullable Typeプロンプト:null値を含めることができ、null値を返す可能性のある機能を処理できます。

PHPでは、クローンキーワードを使用してオブジェクトのコピーを作成し、\ _ \ _クローンマジックメソッドを使用してクローン動作をカスタマイズします。 1.クローンキーワードを使用して浅いコピーを作成し、オブジェクトのプロパティをクローン化しますが、オブジェクトのプロパティはクローニングしません。 2。\ _ \ _クローン法は、浅いコピーの問題を避けるために、ネストされたオブジェクトを深くコピーできます。 3.クローニングにおける円形の参照とパフォーマンスの問題を避けるために注意し、クローニング操作を最適化して効率を向上させます。

PHPはWeb開発およびコンテンツ管理システムに適しており、Pythonはデータサイエンス、機械学習、自動化スクリプトに適しています。 1.PHPは、高速でスケーラブルなWebサイトとアプリケーションの構築においてうまく機能し、WordPressなどのCMSで一般的に使用されます。 2。Pythonは、NumpyやTensorflowなどの豊富なライブラリを使用して、データサイエンスと機械学習の分野で驚くほどパフォーマンスを発揮しています。

HTTPキャッシュヘッダーの主要なプレーヤーには、キャッシュコントロール、ETAG、およびラスト修飾が含まれます。 1.Cache-Controlは、キャッシュポリシーを制御するために使用されます。例:キャッシュコントロール:Max-Age = 3600、public。 2。ETAGは、一意の識別子を介してリソースの変更を検証します。例:ETAG: "686897696A7C876B7E"。 3. Last-Modifiedは、リソースの最後の変更時間を示しています。

PHPでは、Password_hashとpassword_verify関数を使用して安全なパスワードハッシュを実装する必要があり、MD5またはSHA1を使用しないでください。 1)password_hashセキュリティを強化するために、塩値を含むハッシュを生成します。 2)password_verifyハッシュ値を比較して、パスワードを確認し、セキュリティを確保します。 3)MD5とSHA1は脆弱であり、塩の値が不足しており、最新のパスワードセキュリティには適していません。

PHPは、動的なWeb開発およびサーバー側のアプリケーションに使用されるサーバー側のスクリプト言語です。 1.PHPは、編集を必要とせず、迅速な発展に適した解釈言語です。 2。PHPコードはHTMLに組み込まれているため、Webページの開発が簡単になりました。 3。PHPプロセスサーバー側のロジック、HTML出力を生成し、ユーザーの相互作用とデータ処理をサポートします。 4。PHPは、データベースと対話し、プロセスフォームの送信、サーバー側のタスクを実行できます。

PHPは過去数十年にわたってネットワークを形成しており、Web開発において重要な役割を果たし続けます。 1)PHPは1994年に発信され、MySQLとのシームレスな統合により、開発者にとって最初の選択肢となっています。 2)コア関数には、動的なコンテンツの生成とデータベースとの統合が含まれ、ウェブサイトをリアルタイムで更新し、パーソナライズされた方法で表示できるようにします。 3)PHPの幅広いアプリケーションとエコシステムは、長期的な影響を促進していますが、バージョンの更新とセキュリティの課題にも直面しています。 4)PHP7のリリースなど、近年のパフォーマンスの改善により、現代の言語と競合できるようになりました。 5)将来的には、PHPはコンテナ化やマイクロサービスなどの新しい課題に対処する必要がありますが、その柔軟性とアクティブなコミュニティにより適応性があります。

PHPの中心的な利点には、学習の容易さ、強力なWeb開発サポート、豊富なライブラリとフレームワーク、高性能とスケーラビリティ、クロスプラットフォームの互換性、費用対効果が含まれます。 1)初心者に適した学習と使用が簡単。 2)Webサーバーとの適切な統合および複数のデータベースをサポートします。 3)Laravelなどの強力なフレームワークを持っています。 4)最適化を通じて高性能を達成できます。 5)複数のオペレーティングシステムをサポートします。 6)開発コストを削減するためのオープンソース。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター

ドリームウィーバー CS6
ビジュアル Web 開発ツール

Dreamweaver Mac版
ビジュアル Web 開発ツール
