简介
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!\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) "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骞"htmlcode">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骞"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骞"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下用户程序同内核通信详解(netlink机制)的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]