前言
? ? ? ? netlink是一個內(nèi)核空間與用戶空間通信的機(jī)制,相對ioctl和procfs方式來說,netlink有很多優(yōu)點(diǎn):
- netlink使用簡單,與UDO的socket編程類似,直接使用socket編程的API即可。只需要自定義一個新類型的 netlink 協(xié)議定義即可。
- netlink是一種異步通信機(jī)制,在內(nèi)核與用戶態(tài)應(yīng)用之間傳遞的消息保存在socket緩存隊(duì)列中,發(fā)送消息只是把消息保存在接收者的socket的接收隊(duì)列,而不需要等待接收者收到消息。
- netlink 支持多播,內(nèi)核模塊或應(yīng)用可以把消息多播給一個netlink組。
- 內(nèi)核可以使用 netlink 首先發(fā)起會話。
? ? ? ? netlink通信的用戶程序與平時使用的UDPsocket類似,只是多了幾個結(jié)構(gòu)體,在剛開始學(xué)習(xí)netlink時被那么多結(jié)構(gòu)體間的關(guān)系搞的頭腦爆炸,很重要的一點(diǎn)也是在學(xué)習(xí)UDPsocket時沒有打好基礎(chǔ),就正如sendto、sendmsg的使用。
? ? ? ? 只要一開始能搞清楚整個netlink通信的創(chuàng)建和交互過程就能很容易理解。關(guān)于netlink更全面的理解大家自行百度,本文單純從編程角度入手,只是簡單介紹一下基礎(chǔ)的流程實(shí)現(xiàn)。
netlink編程
應(yīng)用程序
流程圖
?圖1.sendto發(fā)送信息流程圖
圖2.sendmsg發(fā)送信息流程圖?
socket()
/* 函數(shù)原型就是socket編程中的socket()函數(shù),就不介紹函數(shù)原型了,只介紹netlink相關(guān)。
* 創(chuàng)建netlink套接字
* SOCK_RAW表示原始的數(shù)據(jù)流
* NETLINK_TEST是自己定義的一個宏,用來標(biāo)識協(xié)議號,也可以用定義在linux/netlink.h中內(nèi)核預(yù)留的宏NETLINK_GENERIC,實(shí)際上就是定義一個內(nèi)核還沒占用的數(shù)字。
*/
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST)
bind()
/* 函數(shù)原型就是socket編程中的bind()函數(shù),就不介紹函數(shù)原型了,只介紹netlink相關(guān)。
* 用戶程序進(jìn)程綁定sockaddr_nl元素
* saddr是struct sockaddr_nl的一個結(jié)構(gòu)體元素,是netlink專用的地址結(jié)構(gòu)體。
*/
bind(sock_fd, (struct sockaddr*)&saddr, sizeof(saddr))
????????Struct?sockaddr_ln為Netlink的地址,和我們通常socket編程中的sockaddr_in作用一樣
struct sockaddr_nl
{
sa_family_t nl_family; /*該字段總是為AF_NETLINK */
unsigned short nl_pad; /* 目前未用到,填充為0*/
__u32 nl_pid; /* PORT-ID 用來標(biāo)識單播地址,是netlink通信標(biāo)識,類似于ip地址,0表示內(nèi)核*/
__u32 nl_groups; /* 多播使用,0表示不用多播 */
};
創(chuàng)建struct nlmsghdr
/* netlink通信的報(bào)文分為消息頭和消息體。
* netlink與tcp、udp的通信區(qū)別就是netlink的報(bào)文需要自己定義頭部信息。
* 而tcp、udp的socket直接發(fā)送消息內(nèi)容,內(nèi)核協(xié)議棧會根據(jù)源、目的地址(sockaddr_in)自己填充頭部信息。
* netlink的消息頭就是結(jié)構(gòu)體nlmsghdr。
*/
struct nlmsghdr
{
__u32 nlmsg_len; // 整個消息的長度,包括消息頭
__u16 nlmsg_type; // 消息的類型,區(qū)別數(shù)據(jù)消息還是控制消息
__u16 nlmsg_flags; // 附加在消息上的額外說明
__u32 nlmsg_seq; // 消息序列號,防止消息丟失
__u32 nlmsg_pid; // 用來表示自己的進(jìn)程標(biāo)識。為netlink連接通道唯一數(shù)字標(biāo)識符,用于避免內(nèi)核發(fā)送給用戶進(jìn)程時發(fā)錯對象,通常是用戶進(jìn)程的pid。
};
nlmsg_type:
#define NLMSG_NOOP 0x1 //不執(zhí)行任何動作,必須將該消息丟棄;
#define NLMSG_ERROR 0x2 //消息發(fā)生錯誤;
#define NLMSG_DONE 0x3 //標(biāo)識分組消息的末尾;
#define NLMSG_OVERRUN 0x4 //緩沖區(qū)溢出,表示某些消息已經(jīng)丟失。
define NLMSG_MIN_TYPE 0x10 //預(yù)留
nlmsg_flags:
#define NLM_F_REQUEST 1 // It is request message.
#define NLM_F_MULTI 2 // Multipart message, terminated by NLMSG_DONE
#define NLM_F_ACK 4 // Reply with ack, with zero or error code
#define NLM_F_ECHO 8 // Echo this request
#define NLM_F_DUMP_INTR 16 // Dump was inconsistent due to sequence change
// Modifiers to GET request
#define NLM_F_ROOT 0x100 // specify tree root
#define NLM_F_MATCH 0x200 // return all matching
#define NLM_F_ATOMIC 0x400 // atomic GET
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
// Modifiers to NEW request
#define NLM_F_REPLACE 0x100 //Override existing
#define NLM_F_EXCL 0x200 // Do not touch, if it exists
#define NLM_F_CREATE 0x400 // Create, if it does not exist
#define NLM_F_APPEND 0x800 // Add to end of list
注意:struct nlmsghdr結(jié)構(gòu)體中
? ? __u32 ? ? ? ?nlmsg_pid; ? ? ?// 用來表示自己的進(jìn)程標(biāo)識。
????????內(nèi)核通過獲取這個值知道用戶進(jìn)程的標(biāo)識,然后通過netlink_unicast的第三個參數(shù)pid發(fā)送出去。
? ? ? ? 需要注意的是內(nèi)核設(shè)置消息頭的結(jié)構(gòu)體nlmsg_put第四個參數(shù)設(shè)置nlmsg_pid應(yīng)為0,標(biāo)識自己的pid為0級內(nèi)核。
????????我們在創(chuàng)建netlink報(bào)文消息頭nlsmhdr結(jié)構(gòu)體時會一起申請消息體的結(jié)構(gòu)。
struct nlmsghdr *nlh = NULL; //定義netlink消息頭類型的指針
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); // 申請netlink報(bào)文空間:消息頭+消息體。NLMSG_SPACE是系統(tǒng)頭文件定義的宏,后面會介紹netlink通信常用的宏;MAX_PAYLOAD是用戶自己定義的消息負(fù)載大小
netlink常用的系統(tǒng)定義的宏
#define NLMSG_ALIGNTO 4U
/* 宏NLMSG_ALIGN(len)用于得到不小于len且字節(jié)對齊的最小數(shù)值 */
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
/* Netlink 頭部長度 */
#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/* 計(jì)算消息數(shù)據(jù)len的真實(shí)消息長度(消息體 + 消息頭)*/
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
/* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字節(jié)對齊的最小數(shù)值 */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
/* 宏NLMSG_DATA(nlh)用于取得消息的數(shù)據(jù)部分的首地址,設(shè)置和讀取消息數(shù)據(jù)部分時需要使用該宏 */
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
/* 宏NLMSG_NEXT(nlh,len)用于得到下一個消息的首地址, 同時len 變?yōu)槭S嘞⒌拈L度 */
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
/* 判斷消息是否 >len */
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \(nlh)->nlmsg_len <= (len))
/* NLMSG_PAYLOAD(nlh,len) 用于返回payload的長度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
sendto、recvfrom
/* sendto函數(shù)原型 */
int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);
/* netlink使用
* sock_fd : socket函數(shù)創(chuàng)建的fd。
* nlh : nlmsghdr結(jié)構(gòu)體指針
* nlh->nlmsg_le : netlink報(bào)文長度
* 0 : flag
* daddr : 目的地址
* sizeof(struct sockaddr_nl) : daddr元素大小
*/
sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
/* recvfrom函數(shù)原型 */
int recvfrom(socket s,void *buf,int len,unsigned int flags, struct sockaddr *from,int *fromlen);
/* netlink使用 */
recvfrom(sock_fd, nlh, NLMSG_LENGTH(MAX_PAYLOAD), 0, NULL, NULL);
sendmsg、recvmsg
? ? ? ? netlink用戶程序發(fā)送和接收消息可以用sendto/sendmsg、recvfrom/recvmsg。使用sendto和recvfrom則如上所述。如果使用sendmsg和recvmsg就需要用到msghdr結(jié)構(gòu)體和iovec結(jié)構(gòu)體。sendmsg、recvmsg更為復(fù)雜功能更強(qiáng)。msghdr、nlmsghdr、iovec的關(guān)系如下圖所示:
? ? ? ? ?下面我們來介紹一下msghdr與iovec結(jié)構(gòu)體
struct iovec
{ /* Scatter/gather array items */
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
/* iov_base: iov_base指向數(shù)據(jù)包緩沖區(qū),即參數(shù)buff,iov_len是buff的長度。msghdr中允許一次傳遞多個buff,以數(shù)組的形式組織在 msg_iov中,msg_iovlen就記錄數(shù)組的長度 (即有多少個buff)*/
struct msghdr
{
void *msg_name; // 數(shù)據(jù)的目的地址,網(wǎng)絡(luò)包指向sockaddr_in, netlink則指向sockaddr_nl;
socklen_t msg_namelen; // msg_namelen: msg_name 所代表的地址長度
struct iovec *msg_iov; // 指向的是緩沖區(qū)數(shù)組
size_t msg_iovlen; // 緩沖區(qū)數(shù)組長度
void *msg_control; // 輔助數(shù)據(jù),控制信息(發(fā)送任何的控制信息)
size_t msg_controllen; // 輔助信息長度
int msg_flags; // 消息標(biāo)識
};
/* iovec、msghdr結(jié)構(gòu)體的使用 */
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
? ? ? ? sendmsg與recvmsg的使用
/* sendmsg函數(shù)原型 */
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
sendmsg(sock_fd, &msg, 0); //sock_fd為socket創(chuàng)建的fd,msg為msghdr的指針
/* recvmsg函數(shù)原型 */
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
recvmsg(sock_fd, &msg, 0);
用戶程序代碼-usr_netlink_demo.c
使用sendto、recvfrom文章來源:http://www.zghlxwxcb.cn/news/detail-589724.html
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#define MAX_PAYLOAD 1024 /* netlink消息最大負(fù)載為1024B */
#define NETLINK_TEST 30
int main(int argc, char **argv)
{
struct sockaddr_nl daddr;
struct sockaddr_nl saddr; //定義netlink用戶空間地址體
struct nlmsghdr *nlh = NULL; //定義netlink消息頭類型的指針
int sock_fd = -1;
int len;
int ret;
char recv_buf[MAX_PAYLOAD] = {0};
char *umsg = "hello";
/* 創(chuàng)建netlink套接字,SOCK_RAW表示原始的數(shù)據(jù)流,NETLINK_TEST表示用戶自定義協(xié)議 */
if (-1 == (sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST)))
{
perror("can't create netlink socket!");
return -1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = 100;
saddr.nl_groups = 0;
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
if (-1 == bind(sock_fd, (struct sockaddr*)&saddr, sizeof(saddr)))
{
perror("can't bind sockfd with sockaddr_nl!");
return -2;
}
/*
給結(jié)構(gòu)體指針nlh分配地址
NLMSG_SPACE(MAX_PAYLOAD): 該宏用于返回不小于MAX_PAYLOAD且4字節(jié)對齊的最小長度值
一般用于向內(nèi)存系統(tǒng)申請空間是指定所申請的內(nèi)存字節(jié)數(shù)
*/
if (NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD))))
{
perror("malloc mem failed!");
return -3;
}
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = 100;
nlh->nlmsg_type = 0;
nlh->nlmsg_flags = 0;
nlh->nlmsg_seq = 0;
memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
ret = sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
if(0 >= ret)
{
perror("sendto error\n");
ret = -4;
goto CleanUp;
}
memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
len = sizeof(struct sockaddr_nl);
ret = recvfrom(sock_fd, nlh, NLMSG_LENGTH(MAX_PAYLOAD), 0, NULL, NULL);
if(0 >= ret)
{
perror("recvfrom kernel error\n");
ret = -5;
goto CleanUp;
}
printf("usr recv kernel:%s\n",NLMSG_DATA(nlh));
ret = 0;
CleanUp:
close(sock_fd);
free(nlh);
return ret;
}
使用sendmsg、recvmsg文章來源地址http://www.zghlxwxcb.cn/news/detail-589724.html
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#define MAX_PAYLOAD 1024 /* netlink消息最大負(fù)載為1024B */
#define NETLINK_TEST 30
int main(int argc, char **argv)
{
struct sockaddr_nl daddr;
struct sockaddr_nl saddr; //定義netlink用戶空間地址體
struct nlmsghdr *nlh = NULL; //定義netlink消息頭類型的指針
struct iovec iov; //定義一個向量元素,通常用于多元素的數(shù)組
struct msghdr msg; // 用于recvmsg、sendmsg發(fā)送和接受的數(shù)據(jù)存放;
int sock_fd = -1;
int len;
int ret;
char recv_buf[MAX_PAYLOAD] = {0};
char *umsg = "hello";
/* 創(chuàng)建netlink套接字,SOCK_RAW表示原始的數(shù)據(jù)流,NETLINK_TEST表示用戶自定義協(xié)議 */
if (-1 == (sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST)))
{
perror("can't create netlink socket!");
return -1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = 100;
saddr.nl_groups = 0;
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
if (-1 == bind(sock_fd, (struct sockaddr*)&saddr, sizeof(saddr)))
{
perror("can't bind sockfd with sockaddr_nl!");
return -2;
}
/*
給結(jié)構(gòu)體指針nlh分配地址
NLMSG_SPACE(MAX_PAYLOAD): 該宏用于返回不小于MAX_PAYLOAD且4字節(jié)對齊的最小長度值
一般用于向內(nèi)存系統(tǒng)申請空間是指定所申請的內(nèi)存字節(jié)數(shù)
*/
if (NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD))))
{
perror("malloc mem failed!");
return -3;
}
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = 100;
nlh->nlmsg_type = 0;
nlh->nlmsg_flags = 0;
nlh->nlmsg_seq = 0;
memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&(daddr);
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = sendmsg(sock_fd, &msg, 0);
if(0 >= ret)
{
perror("sendto kernel error\n");
ret = -5;
goto CleanUp;
}
memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
recvmsg(sock_fd, &msg, 0);
if(0 >= ret)
{
perror("recvfrom kernel error\n");
ret = -6;
goto CleanUp;
}
printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
ret = 0;
CleanUp:
close(sock_fd);
free(nlh);
return ret;
}
內(nèi)核程序-kernel_netlink_demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <linux/sched.h>
#define NETLINK_TEST 30
#define MSG_LEN 125
#define USER_PORT 100
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lixuezahng");
MODULE_DESCRIPTION("netlink example");
struct sock *nl_sk = NULL;
struct net init_net;
static int netlink_send_msg(char *send_buf, size_t len, long pid)
{
struct sk_buff *psk_buff_t;
struct nlmsghdr *pnlmsghdr_t;
int ret;
psk_buff_t = nlmsg_new(len, GFP_ATOMIC); //創(chuàng)建sk_buff空間
if(NULL == psk_buff_t)
{
printk("netlink alloc failure\n");
return -1;
}
/* 設(shè)置netlink消息頭 */
pnlmsghdr_t = nlmsg_put(psk_buff_t, 0, 0, pid, len, 0);
if(NULL == pnlmsghdr_t)
{
printk("nlmsg_put failure\n");
nlmsg_free(psk_buff_t); //針對nlmsg_new申請空間的釋放問題,若put失敗需要手動釋放,若put成功netlink_unicast發(fā)送后內(nèi)核會自己處理
return -2;
}
memcpy(NLMSG_DATA(pnlmsghdr_t), send_buf, len);
/*
內(nèi)核發(fā)送單播消息
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
ssk: netlink socket
skb: skb buff 指針
portid:通信的端口號
nonblock:表示該函數(shù)是否為非阻塞,如果為1,該函數(shù)將在沒有接收緩存可利用時立即返回,而如果為 0,該函數(shù)在沒有接收緩存可利用 定時睡眠
內(nèi)核發(fā)送多播消息
extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid, __u32 group, gfp_t allocation);
ssk: 同上(對應(yīng) netlink_kernel_create 返回值)、
skb: 內(nèi)核 skb buff
portid:端口id
group: 是所有目標(biāo)多播組對應(yīng)掩碼的"OR"操作的。
allocation: 指定內(nèi)核內(nèi)存分配方式,通常 GFP_ATOMIC 用于中斷上下文,而 GFP_KERNEL 用于其他場合。這個參數(shù)的存在是因?yàn)樵?API 可能需要分配一個或多個緩沖區(qū)來對多播消息進(jìn)行 clone。
*/
ret = netlink_unicast(nl_sk, psk_buff_t, pid, MSG_DONTWAIT);
return ret;
}
static void netlink_rcv_msg(struct sk_buff *psk_buff_t)
{
struct nlmsghdr *pnlmsghdr_t = NULL;
char *umsg = NULL;
char *kmsg = "hello users!!!";
if(psk_buff_t->len >= nlmsg_total_size(0))
{
pnlmsghdr_t = nlmsg_hdr(psk_buff_t);
umsg = NLMSG_DATA(pnlmsghdr_t);
if(umsg)
{
printk("kernel recv from user: %s\n", umsg);
netlink_send_msg(kmsg, strlen(kmsg), pnlmsghdr_t->nlmsg_pid);
}
}
}
struct netlink_kernel_cfg cfg = {
.input = netlink_rcv_msg, /* set recv callback */
};
static int __init nlmoudle_init(void)
{
printk(KERN_ALERT "my netlink_kernel in\n");
/*
static inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
net:指向所在的網(wǎng)絡(luò)命名空間, 默認(rèn)傳入的是 &init_net (不需要定義); 定義在 net_namespace.c(extern struct net init_net);
unit:netlink協(xié)議類型
cfg:cfg 存放的是 netlink 內(nèi)核配置參數(shù)
struct netlink_kernel_cfg {
unsigned int groups;
unsigned int flags;
void (*input)(struct sk_buff *skb); // input 回調(diào)函數(shù)
struct mutex *cb_mutex;
void (*bind)(int group);
bool (*compare)(struct net *net, struct sock *sk);
};
*/
nl_sk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
return 0;
}
static void __exit nlmoudle_exit(void)
{
printk(KERN_ALERT "my netlink_kernel out\n");
netlink_kernel_release(nl_sk);
}
module_init(nlmoudle_init);
module_exit(nlmoudle_exit);
makefile
KERNEL_DIR := /XX/XX/linux-3.10 #指定內(nèi)核的路徑
CURRENT_PATH := $(shell pwd) #當(dāng)前路徑
obj-m += kernel_netlink_demo.o #編譯kernel_ioctl_demo.c生成.ko文件
build:netlink_module #總目標(biāo)
netlink_module:
$(MAKE) -C $(KERNEL_DIR) ARCH=XXX CROSS_COMPILE=XXX M=$(CURRENT_PATH) modules
#-C [CPATH]表示make要跳轉(zhuǎn)到CPATH路徑執(zhí)行make
#-M [MPATH]表示執(zhí)行完CPATH路徑下的make后再回到MPATH繼續(xù)執(zhí)行剩下的
#ARCH 指定處理器架構(gòu)
#CROSS_COMPILE 指定交叉編譯器
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_PATH) clean
sendmsg實(shí)例
/*************************************************************************
* @description : nl_send_msg 函數(shù),通過netlink發(fā)送數(shù)據(jù)到內(nèi)核
* @param -sock_fd : sock描述符
* @param -dst_addr : netlink目的地址
* @param -message : 發(fā)送的數(shù)據(jù)
* @param -len :發(fā)送數(shù)據(jù)的長度
* @return : 失敗返回 -1,成功返回0
*************************************************************************/
static int nl_send_msg(int sock_fd, struct sockaddr_nl *dst_addr, char *message, int len)
{
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
if( !len || !message)
{
printf("message is empty or len is zero\n");
return -1;
}
/* 1、構(gòu)建nlh */
if(NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(len))))
{
printf("nlh malloc failed\n");
return -1;
}
nlh->nlmsg_len = NLMSG_SPACE(len);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
memcpy(NLMSG_DATA(nlh), message, len);
/* 2、構(gòu)建iov */
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
/* 3、構(gòu)建msg */
memset(&msg, 0, sizeof(struct msghdr));
msg.msg_name = (void *)dst_addr;
msg.msg_namelen = sizeof(struct sockaddr_nl);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* 4、發(fā)送消息 */
if(0 > sendmsg(sock_fd, &msg, 0))
{
printf("sendmsg failed\n");
free(nlh);
return -1;
}
free(nlh);
return 0;
}
/*************************************************************************
* @description : nl_recv_msg 函數(shù),接收從內(nèi)核發(fā)送的數(shù)據(jù)
* @param -sock_fd : sock描述符
* @param -dst_addr : netlink源地址
* @param -message : 接收到的數(shù)據(jù)
* @param -len :接收到數(shù)據(jù)的長度
* @return : 失敗返回 -1,成功返回0
*************************************************************************/
static int nl_recv_msg(int sock_fd, struct sockaddr_nl *src_addr, char *message, int len)
{
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
if( !len || !message)
{
printf("message is empty or len is zero\n");
return -1;
}
/* 1、構(gòu)建nlh */
if(NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(RESCVPLAYLOAD))))
{
printf("recv nlh malloc failed\n");
return -1;
}
/* 2、構(gòu)建ivo */
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(RESCVPLAYLOAD);
/* 3、構(gòu)建msg */
memset(src_addr, 0, sizeof(struct sockaddr_nl));
memset(&msg, 0, sizeof(struct msghdr));
msg.msg_name = (void *)src_addr;
msg.msg_namelen = sizeof(struct sockaddr_nl);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* 4、接受信息 */
if(0 > recvmsg(sock_fd, &msg, 0))
{
printf("recv_message failed\n");
free(nlh);
return -1;
}
memcpy(message, (unsigned char *)NLMSG_DATA(nlh), nlh->nlmsg_len - NLMSG_SPACE(0));
free(nlh);
return 0;
}
到了這里,關(guān)于內(nèi)核與用戶空間的通信實(shí)現(xiàn)—netlink的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!