简介
linux下用户程序同内核通信的方式一般有ioctl, proc文件系统,剩下一个就是Netlink套接字了。 这里先介绍下netlink。
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机制实现用户程序和内核的通信。 具体就是用户程序执行./netlink -S [我是参数] 或./netlink -G 时,内核会返回”S know you!” 和“I know you!” 这两种字符串, 然后输出。 内核和用户程序均加有打印。
内核模块
1. Makefile依赖的编译规则 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 #这行是为了取出系统下内核的目录(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! "
);
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 "
);
}
static
int
process_hello_get(
int
dest,
void
*buf,
int
len)
{
printk(KERN_ALERT
"In process_hello get! "
);
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 "
, (
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! "
);
}
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! "
);
return
-1;
}
printk(KERN_ALERT
"Init netlink success! "
);
return
0;
}
static
void
__exit netlink_exit(
void
)
{
netlink_kernel_release(netlink_fd);
printk(KERN_ALERT
"Exit netlink! "
);
}
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目录下执行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 "
);
goto
error;
}
ret = recvmsg(netlink_s->sock, &msg, 0);
if
(ret < 0)
{
printf
(
"Read fail "
);
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 "
);
goto
exit_p;
case
NET_MEM:
printf
(
"Memory error "
);
goto
exit_p;
case
NET_PARAM:
printf
(
"Param error "
);
goto
exit_p;
case
NET_SOCK:
printf
(
"Socket error "
);
goto
exit_p;
default
:
break
;
}
exit_p:
return
NET_ERROR;
}
void
usage(
void
)
{
printf
(
"Usage: Netlink -G <param> -S <param> "
);
}
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] =
''
;
printf
(
"K rep [len = %d]:%s "
, rlen, rbuf);
}
printf
(
"K[len = %d]: %s "
, rlen, rbuf);
exit_p:
netlink_sock_deinit(&h_sock);
return
0;
}
总结
以上就是本文关于linux下用户程序同内核通信详解(netlink机制)的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://blog.csdn.net/daydring/article/details/24000081
原创文章,作者:XUEDD,如若转载,请注明出处:http://www.wangzhanshi.com/n/6470.html