linux下用戶程序同內(nèi)核通信詳解(netlink機制)
簡介
linux下用戶程序同內(nèi)核通信的方式一般有ioctl, proc文件系統(tǒng),剩下一個就是Netlink套接字了。 這里先介紹下netlink。
Netlink 是一種在內(nèi)核與用戶應用間進行雙向數(shù)據(jù)傳輸?shù)姆浅:玫姆绞?,用戶態(tài)應用使用標準的 socket API 就可以使用 netlink 提供的強大功能,內(nèi)核態(tài)需要使用專門的內(nèi)核 API 來使用 netlink。
Netlink 相對于系統(tǒng)調(diào)用,ioctl 以及 /proc 文件系統(tǒng)而言具有以下優(yōu)點:
1,為了使用 netlink,用戶僅需要在 include/linux/netlink.h 中增加一個新類型的 netlink 協(xié)議定義即可, 如 #define NETLINK_MYTEST 17 然后,內(nèi)核和用戶態(tài)應用就可以立即通過 socket API 使用該 netlink 協(xié)議類型進行數(shù)據(jù)交換。但系統(tǒng)調(diào)用需要增加新的系統(tǒng)調(diào)用,ioctl 則需要增加設備或文件, 那需要不少代碼,proc 文件系統(tǒng)則需要在 /proc 下添加新的文件或目錄,那將使本來就混亂的 /proc 更加混亂。
2. netlink是一種異步通信機制,在內(nèi)核與用戶態(tài)應用之間傳遞的消息保存在socket緩存隊列中,發(fā)送消息只是把消息保存在接收者的socket的接 收隊列,而不需要等待接收者收到消息,但系統(tǒng)調(diào)用與 ioctl 則是同步通信機制,如果傳遞的數(shù)據(jù)太長,將影響調(diào)度粒度。
3.使用 netlink 的內(nèi)核部分可以采用模塊的方式實現(xiàn),使用 netlink 的應用部分和內(nèi)核部分沒有編譯時依賴,但系統(tǒng)調(diào)用就有依賴,而且新的系統(tǒng)調(diào)用的實現(xiàn)必須靜態(tài)地連接到內(nèi)核中,它無法在模塊中實現(xiàn),使用新系統(tǒng)調(diào)用的應用在編譯時需要依賴內(nèi)核。
4.netlink 支持多播,內(nèi)核模塊或應用可以把消息多播給一個netlink組,屬于該neilink 組的任何內(nèi)核模塊或應用都能接收到該消息,內(nèi)核事件向用戶態(tài)的通知機制就使用了這一特性,任何對內(nèi)核事件感興趣的應用都能收到該子系統(tǒng)發(fā)送的內(nèi)核事件,在 后面的文章中將介紹這一機制的使用。
5.內(nèi)核可以使用 netlink 首先發(fā)起會話,但系統(tǒng)調(diào)用和 ioctl 只能由用戶應用發(fā)起調(diào)用。
6.netlink 使用標準的 socket API,因此很容易使用,但系統(tǒng)調(diào)用和 ioctl則需要專門的培訓才能使用。
下面這兩部分代碼主要的目的是用netlink機制實現(xiàn)用戶程序和內(nèi)核的通信。 具體就是用戶程序執(zhí)行./netlink -S [我是參數(shù)] 或./netlink -G 時,內(nèi)核會返回"S know you!" 和“I know you!” 這兩種字符串, 然后輸出。 內(nèi)核和用戶程序均加有打印。
內(nèi)核模塊
1. Makefile依賴的編譯規(guī)則 ruler.dir
PWD := $(shell pwd) all: modules romfs modules: $(MAKE) -C $(KDIR) M=$(PWD) modules @echo $(OBJ) modules_install: $(MAKE) -C $(KDIR) M=$(PWD) modules_install romfs: cp -rf *.ko $(MODULES_BUILD_DIR) clean: rm *.o *.ko *.mod.* Module.* modules.* rm -rf $(MODULES_BUILD_DIR) obj-m := $(MOD_NAME).o
2.Makefile
KERNEL_MODULES := netlink export MODULES_ROOT_DIR := $(shell pwd) export MODULES_BUILD_DIR := $(MODULES_ROOT_DIR)/build export KDIR := /lib/modules/$(shell uname -r)/build #這行是為了取出系統(tǒng)下內(nèi)核的目錄(ubuntu) all: init modules romfs init: mkdir -p $(MODULES_BUILD_DIR) modules:$(patsubst %, _dir_%, $(KERNEL_MODULES)) $(patsubst %, _dir_%, $(KERNEL_MODULES)): @echo @echo Building $(patsubst _dir_%, %, $@) $(MAKE) -C $(patsubst _dir_%, %, $@) all romfs: $(patsubst %, _romfs_%, $(KERNEL_MODULES)) $(patsubst %, _romfs_%, $(KERNEL_MODULES)): $(MAKE) -C $(patsubst _romfs_%, %, $@) romfs clean: $(patsubst %, _clean_%, $(KERNEL_MODULES)) $(RM) $(BUILD_DIR) $(patsubst %, _clean_%, $(KERNEL_MODULES)): @echo @echo Cleaning $(patsubst _dir_%, %, $@) $(MAKE) -C $(patsubst _clean_%, %, $@) clean .PHONY:
3. ./netlink/netlink.c
/*
* netlink.c
*
* Created on: 2014 * Author: cr
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include "usrlink.h"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MDAXIA");
struct sock *netlink_fd;
static void netlink_to_user(int dest, void *buf, int len)
{
struct nlmsghdr *nl;
struct sk_buff *skb;
int size;
size = NLMSG_SPACE(len);
skb = alloc_skb(size, GFP_ATOMIC);
if(!skb || !buf)
{
printk(KERN_ALERT "netlink_to_user skb of buf null!\n");
return;
}
nl = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(len) - sizeof(struct nlmsghdr), 0);
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_group = 0;
memcpy(NLMSG_DATA(nl), buf, len);
nl->nlmsg_len = (len > 2) ? (len - 2):len;
netlink_unicast(netlink_fd, skb, dest, MSG_DONTWAIT);
printk(KERN_ALERT "K send packet success\n");
}
static int process_hello_get(int dest, void *buf, int len)
{
printk(KERN_ALERT "In process_hello get!\n");
memcpy(buf, "I known you !", 13);
netlink_to_user(dest, buf, 13);
return NET_OK;
}
static int process_hello_set(int dest, void *buf, int len)
{
printk(KERN_ALERT "In process_hello set! %s\n", (char *)buf);
memcpy(buf, "S known you !", 13);
netlink_to_user(dest, buf, 13);
return NET_OK;
}
static void netlink_process_packet(struct nlmsghdr *nl)
{
int ret;
switch(nl->nlmsg_type)
{
case HELLO_GET:
ret = process_hello_get(nl->nlmsg_pid, NLMSG_DATA(nl), nl->nlmsg_len);
break;
case HELLO_SET:
ret = process_hello_set(nl->nlmsg_pid, NLMSG_DATA(nl), nl->nlmsg_len);
break;
default:break;
}
}
static void netlink_recv_packet(struct sk_buff *__skb)
{
struct sk_buff *skb;
struct nlmsghdr *nlhdr;
skb = skb_get(__skb);
if(skb->len >= sizeof(struct nlmsghdr))
{
nlhdr = (struct nlmsghdr *)skb->data;
if(nlhdr->nlmsg_len >= sizeof(struct nlmsghdr) &&
__skb->len >= nlhdr->nlmsg_len)
{
netlink_process_packet(nlhdr);
}
}
else
printk(KERN_ALERT "Kernel receive msg length error!\n");
}
static int __init netlink_init(void)
{
netlink_fd = netlink_kernel_create(&init_net, USER_NETLINK_CMD, 0, netlink_recv_packet, NULL, THIS_MODULE);
if(NULL == netlink_fd)
{
printk(KERN_ALERT "Init netlink!\n");
return -1;
}
printk(KERN_ALERT "Init netlink success!\n");
return 0;
}
static void __exit netlink_exit(void)
{
netlink_kernel_release(netlink_fd);
printk(KERN_ALERT "Exit netlink!\n");
}
module_init(netlink_init);
module_exit(netlink_exit);
4. ./netlink/usrlink.h
/*
* usrlink.h
*
* Created on: 2014騫?鏈?7鏃? * Author: cr
*/
#ifndef USRLINK_H_
#define USRLINK_H_
#define USER_NETLINK_CMD 25
#define MAXMSGLEN 1024
typedef enum error_e {
NET_ERROR,
NET_OK,
NET_PARAM,
NET_MEM,
NET_SOCK,
} netlink_err;
typedef enum module_e {
HELLO_CMD = 1,
} netlink_module;
typedef enum type_e {
HELLO_SET,
HELLO_GET,
} netlink_type;
#endif /* USRLINK_H_ */
5. ./netlink/Makefile
MOD_NAME := netlink $(MOD_NAME)-objs : netlink.o -include $(MODULES_ROOT_DIR)/rules.dir .PHONY:
6.編譯方式。
其中Makefile、rulers.dir 在Knetlink/下, netlink.c 、netlink.h 、Makefile在Knetlink/netlink/目錄下。 編譯時在Knetlink目錄下執(zhí)行Make即可
用戶程序
用戶程序的Makefile 這里就不放出了。 我是直接在eclipse下建的工程 自動編譯的、...
1. netlink.c
/*
* usrlink.c
*
* Created on: 2014騫?鏈?7鏃? * Author: cr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "usrlink.h"
int netlink_sock_init(netlink_sock *netlink_s, int module, int protocol)
{
netlink_s->sock = socket(PF_NETLINK, SOCK_RAW, protocol);
if(netlink_s->sock < 0)
return NET_SOCK;
memset(&netlink_s->src, 0 ,sizeof(netlink_s->src));
netlink_s->src.nl_family = AF_NETLINK;
netlink_s->src.nl_pid = module;
netlink_s->src.nl_groups = 0;
if(bind(netlink_s->sock, (struct sockaddr *)&netlink_s->src, sizeof(netlink_s->src)) < 0)
return NET_SOCK;
netlink_s->dest.nl_family = AF_NETLINK;
netlink_s->dest.nl_pid = 0;
netlink_s->dest.nl_groups = 0;
return NET_OK;
}
int netlink_send(netlink_sock *netlink_s, int type, char *sbuf, int slen, char *rbuf, int *rlen)
{
struct msghdr msg;
struct nlmsghdr *nlhdr = NULL;
struct iovec iov;
int ret;
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAXMSGLEN));
if(NULL == nlhdr)
return NET_MEM;
memcpy(NLMSG_DATA(nlhdr), sbuf, slen);
nlhdr->nlmsg_len = NLMSG_SPACE(slen);
nlhdr->nlmsg_pid = netlink_s->src.nl_pid;
nlhdr->nlmsg_type = type;
nlhdr->nlmsg_flags = 0;
iov.iov_base = (void *)nlhdr;
iov.iov_len = nlhdr->nlmsg_len;
msg.msg_name = (void *)&(netlink_s->dest);
msg.msg_namelen = sizeof(netlink_s->dest);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = sendmsg(netlink_s->sock, &msg, 0);
if(ret < 0)
{
printf("Send fail\n");
goto error;
}
ret = recvmsg(netlink_s->sock, &msg, 0);
if(ret < 0)
{
printf("Read fail\n");
goto error;
}
memcpy(rbuf, NLMSG_DATA(nlhdr), nlhdr->nlmsg_len);
*rlen = nlhdr->nlmsg_len;
return NET_OK;
error:
free(nlhdr);
return NET_SOCK;
}
int netlink_sock_deinit(netlink_sock *netlink_s)
{
close(netlink_s->sock);
memset(netlink_s, 0, sizeof(netlink_sock));
return NET_OK;
}
2. netlink.h
/*
* usrlink.h
*
* Created on: 2014 * Author: cr
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <asm/types.h>
#include <linux/socket.h>
#include <linux/netlink.h>
#ifndef USRLINK_H_
#define USRLINK_H_
#define USER_NETLINK_CMD 25
#define MAXMSGLEN 1024
typedef enum error_e {
NET_ERROR,
NET_OK,
NET_PARAM,
NET_MEM,
NET_SOCK,
} netlink_err;
typedef enum module_e {
HELLO_CMD = 1,
} netlink_module;
typedef enum type_e {
HELLO_SET,
HELLO_GET,
} netlink_type;
typedef struct usr_sock_h {
int sock;
struct sockaddr_nl dest;
struct sockaddr_nl src;
} netlink_sock;
int netlink_sock_init(netlink_sock *netlink_s, int module, int protocol);
int netlink_sock_deinit(netlink_sock *netlink_s);
int netlink_send(netlink_sock *netlink_s, int type, char *sbuf, int slen, char *rbuf, int *rlen);
#endif /* USRLINK_H_ */
3. main.c
/*
* main.c
*
* Created on: 2014騫?鏈?7鏃? * Author: cr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usrlink.h"
int parse_ret(int ret)
{
switch(ret)
{
case NET_OK:
return ret;
case NET_ERROR:
printf("error\n");
goto exit_p;
case NET_MEM:
printf("Memory error\n");
goto exit_p;
case NET_PARAM:
printf("Param error\n");
goto exit_p;
case NET_SOCK:
printf("Socket error\n");
goto exit_p;
default:break;
}
exit_p:
return NET_ERROR;
}
void usage(void)
{
printf("Usage: Netlink -G <param>\n\t-S <param>\n");
}
int main(int argc, char **argv)
{
netlink_sock h_sock;
char rbuf[1024];
char sbuf[1024];
int ret, type, slen = 0, rlen = 0;
ret = netlink_sock_init(&h_sock, HELLO_CMD, USER_NETLINK_CMD);
if(NET_OK != parse_ret(ret))
goto exit_p;
bzero(&rbuf, sizeof(rbuf));
bzero(&sbuf, sizeof(sbuf));
if(argc < 3)
{
usage();
goto exit_p;
}
if(!strncmp("-G", argv[1], 2))
type = HELLO_GET;
else if(!strncmp("-S", argv[1], 2))
type = HELLO_SET;
strcpy(sbuf, argv[2]);
slen = strlen(sbuf);
ret = netlink_send(&h_sock, type, sbuf, slen, rbuf, &rlen);
if(NET_OK != parse_ret(ret))
goto exit_p;
if(rlen > 0)
{
rbuf[rlen] = '\0';
printf("K rep [len = %d]:%s\n", rlen, rbuf);
}
printf("K[len = %d]: %s\n", rlen, rbuf);
exit_p:
netlink_sock_deinit(&h_sock);
return 0;
}
總結
以上就是本文關于linux下用戶程序同內(nèi)核通信詳解(netlink機制)的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
版權聲明:本站文章來源標注為YINGSOO的內(nèi)容版權均為本站所有,歡迎引用、轉載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權,請聯(lián)系alex-e#qq.com處理。
關注官方微信