新年快到了,先祝大家新年快樂,牛年發大財,快到了年關,也該好好輕鬆一下,回頭看看自己這一年是不是做了些足以令你自傲的豐功偉業,也希望我能繼續保持對coding的熱誠在新的一年繼續奮鬥
話說回來,有了ioctl,和proc,我們是不是還有其它可以與kernel交換資料的界面呢?沒錯,就是netlink,那什麼時候會用到netlink呢?當你設routing table或防火牆時就會用到了,kernel就是用netlink界面與route指令溝通,scsi驅動也可以看到netlink的影子,netlink有什麼好處呢?
優點:
1.它沒有ioctl等ret回來的kernel round trip time
2.可發多個訊息給不同的user process(如果user processes都在同一group)
缺點:
1.因為只有UDP封包,所以你送資料後也不曉得該資料到底是不是去到對的地方
2.用了一堆socket API,程式碼寫起來並不簡潔
簡單描述我現在做的測試,以下面的kernel udp echo server為例,它會接收使用者的訊息並echo回來,所以整個程式分為兩部份,udp echo server在kernel space,使用者程式在user space
udp echo server重點列如下
1. netlink_kernel_create: create kernel socket並且註冊你的callback函式處理netlink封包,請特別注意第二個參數,我用了NETLINK_USERSOCK這個linux保留給使用者測試的netlink type.
2. sock_release:移除module時,一定要release kernel socket
3. udp_receive:這個函式就是我們處理netlink傳入封包的地方,在這裡你可以取得user process id和data
4. udp_reply:做一個新的封包並呼叫函式netlink_unicast echo回去
[kudp echo server]
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <net/sock.h>
- #include <net/netlink.h>
- #include <linux/skbuff.h>
-
-
- static struct sock *netlink_sock;
-
- static void udp_reply(int pid,int seq,void *payload)
- {
- struct sk_buff *skb;
- struct nlmsghdr *nlh;
- int size=strlen(payload)+1;
- int len = NLMSG_SPACE(size);
- void *data;
- int ret;
-
- skb = alloc_skb(len, GFP_ATOMIC);
- if (!skb)
- return;
- nlh= NLMSG_PUT(skb, pid, seq, 0, size);
- nlh->nlmsg_flags = 0;
- data=NLMSG_DATA(nlh);
- memcpy(data, payload, size);
- NETLINK_CB(skb).pid = 0;
- NETLINK_CB(skb).dst_group = 0;
- ret=netlink_unicast(netlink_sock, skb, pid, MSG_DONTWAIT);
- if (ret <0)
- {
- printk("send failed\n");
- return;
- }
- return;
-
- nlmsg_failure:
- if (skb)
- kfree_skb(skb);
- }
-
-
-
- static void udp_receive(struct sk_buff *skb)
- {
- u_int uid, pid, seq, sid;
- void *data;
- struct nlmsghdr *nlh;
-
- nlh = (struct nlmsghdr *)skb->data;
- pid = NETLINK_CREDS(skb)->pid;
- uid = NETLINK_CREDS(skb)->uid;
- sid = NETLINK_CB(skb).sid;
- seq = nlh->nlmsg_seq;
- data = NLMSG_DATA(nlh);
- printk("recv skb from user space uid:%d pid:%d seq:%d,sid:%d\n",uid,pid,seq,sid);
- printk("data is :%s\n",(char *)data);
- udp_reply(pid,seq,data);
- return ;
- }
-
- static int __init kudp_init(void)
- {
- netlink_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,udp_receive, NULL, THIS_MODULE);
- return 0;
- }
-
- static void __exit kudp_exit(void)
- {
- sock_release(netlink_sock->sk_socket);
- printk("netlink driver remove successfully\n");
- }
- module_init(kudp_init);
- module_exit(kudp_exit);
-
- MODULE_DESCRIPTION("kudp echo server module");
- MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("QT2410:kudp echo server module");
user space client重點列如下
1.宣告socket fd請用sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_USERSOCK),第三個傳入參數為netlink type
2.注意pid與group的使用方式,如果只是unicast,要把group設為0
3.小心struct msghdr msg,要先把裡面的iov,nlmsghdr等欄位宣告並初始化
4.簡單的用sendmsg和recvmsg就大功告成啦
[user space client]
- #include <stdio.h>
- #include <string.h>
- #include <malloc.h>
- #include <sys/socket.h>
- #include <linux/netlink.h>
-
- #define MAX_PAYLOAD 1024
-
- int main(int argc,char *argv[])
- {
- struct sockaddr_nl src_addr, dest_addr;
- struct nlmsghdr *nlh = NULL;
- struct iovec iov;
- int sock_fd;
- struct msghdr msg;
-
- sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_USERSOCK);
- memset(&src_addr, 0, sizeof(src_addr));
- src_addr.nl_family = AF_NETLINK;
- src_addr.nl_pid = getpid();
- src_addr.nl_groups = 0;
- bind(sock_fd, (struct sockaddr*)&src_addr,sizeof(src_addr));
-
- memset(&dest_addr, 0, sizeof(dest_addr));
- dest_addr.nl_family = AF_NETLINK;
- dest_addr.nl_pid = 0;
- dest_addr.nl_groups = 0;
-
- nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
-
- nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
- nlh->nlmsg_pid = getpid();
- nlh->nlmsg_flags = 0;
-
- strcpy(NLMSG_DATA(nlh), "Hello World!");
-
- iov.iov_base = (void *)nlh;
- iov.iov_len = nlh->nlmsg_len;
- msg.msg_name = (void *)&dest_addr;
- msg.msg_namelen = sizeof(dest_addr);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- sendmsg(sock_fd, &msg, 0);
-
- printf("Waiting for message from kernel\n");
-
-
- recvmsg(sock_fd, &msg, 0);
-
- printf("Received message payload: %s\n",NLMSG_DATA(msg.msg_iov->iov_base));
- close(sock_fd);
- return 0;
- }
全部的程式請在這邊下載,如果要嘗試multicast server,那麼請參考kdupb.c這支程式,和它的user space client mc1.c和mc2.c,只要group設定正確mc1和mc2都可以收到kudpb的multicast訊息,kudpb利用netlink_broadcast廣播給mc1和mc2,執行畫面如下

Regular expression-跟brainfuck差不多的東西 (2009-11-13 15:37)
Reading file in kernel-簡單但實用 (2009-10-13 15:18)
Linux file system for dummies-只花你45分鐘 (2009-08-19 15:40)
OPENSSL-TCP SSL初心者之路 (2009-07-16 15:16)
NAPI與pure interrupt driver的效能比較 (2009-04-29 19:06)
usermode helper-來自kernel的呼喚 (2009-04-21 16:19)
kernel module memory detector-抓出有害的kernel module (2009-03-31 13:50)
kernel space coding-如履薄冰 (2009-03-26 09:52)
readahead與posix_advise-預讀取是萬能靈丹? (2009-03-06 15:54)