1. 程式人生 > >linux下使用者程式同核心通訊例項一(netlink)

linux下使用者程式同核心通訊例項一(netlink)

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) ? (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:


5. 編譯方式。

其中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\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;
}