- 論壇徽章:
- 169
|
使用netlink通訊時需要注意的一些問題 原作者:duanjigang
之前發(fā)過一個用戶態(tài)通過netlink從內(nèi)核中獲取網(wǎng)絡(luò)卡列表以及每個網(wǎng)卡狀態(tài)信息的例子
大概的原理就是內(nèi)核創(chuàng)建netlink socket,然后用戶態(tài)調(diào)用應用程序發(fā)送查詢命令,或者獲取所有網(wǎng)卡列表,或者獲取某一個網(wǎng)卡的狀態(tài)信息。
當時做的比較簡單,也就過去了,最近要用到這個通訊,傳輸比較大量數(shù)據(jù),遇到了一些問題,今天剛剛解決,稍微小結(jié)下,發(fā)上來。
希望能對大家有點用(估計很多高手早都注意這個問題了^_^)
首先列舉下問題:
其一,內(nèi)核多次發(fā)送數(shù)據(jù)的問題。
在上篇文章中,我們看到,kernel是收到一個命令,就獲取數(shù)據(jù),然后簡單的完成一次發(fā)送,代碼片段如下:
- nlhdr->nlmsg_pid = 0;
- nlhdr->nlmsg_flags = 0;
- NETLINK_CB(skb).pid = 0;
- NETLINK_CB(skb).dst_pid = pid;
- NETLINK_CB(skb).dst_group = 0;
- memset(nlhdr, 0, NLMSG_SPACE(nlhdr->nlmsg_len));
- strcpy(NLMSG_DATA(nlhdr), szBuff);
- netlink_unicast(netlink_exam_sock, skb, pid, MSG_DONTWAIT);
復制代碼
當時沒太注意,后來遇到情況是,數(shù)據(jù)有多條,內(nèi)核需要多次發(fā)送,怎么辦??結(jié)果我嘗試用netlink_unicast多次發(fā)送,比如
- for (int i = 0; i < n ;i++)
- {
- //make data for record i
- netlink_unicast(netlink_exam_sock, skb, pid, MSG_DONTWAIT);
- }
復制代碼
結(jié)果一運行,就崩潰,后來知道netlink_unicast發(fā)送后會把skb釋放掉,所以第二次調(diào)用是無效的了,這才會崩潰。
大體上感覺在每次發(fā)送的時候,可能需要clone一個或者自己構(gòu)造一個包發(fā)送,上文的例子中的是直接利用從隊列中拿出來的
skb做為負載發(fā)送的,所以沒問題,但是還不能偷懶。就在網(wǎng)上找資料。
終于還是找到說法了,居然也是在CU的帖子,就是另外寫一個函數(shù),自己構(gòu)造包,填數(shù)據(jù),然后發(fā)送,就能多次發(fā)送了。
參考“執(zhí)一”的博文:
通過Netlink與TC進行通信
(讓我們再次對九賤兄和執(zhí)一表示感謝!)修改了下,寫(基本上是copy,只不過修改了參數(shù))了個發(fā)送的函數(shù),如下:
- static int send_to_user(struct sock * ps, int pid, const char* szdata, unsigned int len)
- {
- int ret;
- int size;
- unsigned char *old_tail;
- struct sk_buff *skb;
- struct nlmsghdr *nlhdr;
- struct cha *packet;
- /*計算消息總長:消息首部加上數(shù)據(jù)加度*/
- size = NLMSG_SPACE(len);
- /*分配一個新的套接字緩存*/
- skb = alloc_skb(size, GFP_ATOMIC);
- old_tail = skb->tail;
- /*初始化一個netlink消息首部*/
- nlhdr = NLMSG_PUT(skb, 0, 0, NETLINK_CME, size - sizeof(*nlhdr));
- /*跳過消息首部,指向數(shù)據(jù)區(qū)*/
- packet = NLMSG_DATA(nlhdr);
- /*初始化數(shù)據(jù)區(qū)*/
- memset(packet, 0, len);
- memcpy(packet, szdata, len);
- nlhdr->nlmsg_len = skb->tail - old_tail;
- /*設(shè)置控制字段*/
- nlhdr->nlmsg_pid = 0;
- nlhdr->nlmsg_flags = 0;
- NETLINK_CB(skb).pid = 0;
- NETLINK_CB(skb).dst_pid = pid;
- NETLINK_CB(skb).dst_group = 0;
- /*發(fā)送數(shù)據(jù)*/
- ret = netlink_unicast(ps, skb, pid, MSG_DONTWAIT);
- nlmsg_failure:
- return ret;
- ;
- }
復制代碼
這樣,把原來的代碼稍作修改
改成這樣就能多次發(fā)送了。
- if(strncmp(data, "all", 3) == 0)
- {
- get_dev_info(0, NULL);
- }
- else
- {
- get_dev_info(1, data);
- }
- pid = nlhdr->nlmsg_pid;
- for(i = 0; i < time; i++)
- {
- send_to_user(netlink_exam_sock, pid, szBuff, strlen(szBuff));
- }
- }
復制代碼
其二:skb釋放問題。(問題解決按照輕重緩急來說^_^)
剛解決了多次發(fā)送的問題,我就有些得意忘形,結(jié)果dmesg時看到一個很2的信息,是在rmmod時報告的
KERNEL: assertion (!atomic_read(&sk->sk_rmem_alloc)) failed at net/netlink/af_netlink.c (145)
于是再想是不是一樓了什么東西,哦,從隊列中拿出來的skb沒有釋放,這下好解決了。兩種途徑。
A:既然netlink_unicast發(fā)送完后會把skb釋放掉,那我們?yōu)樯恫坏谝淮伟l(fā)送時用從隊列中拿出來的skb做載體,這樣既發(fā)送數(shù)據(jù)包,又
釋放了skb,果然,報錯沒了。。真是得了便宜還賣乖啊![]()
B:最簡單的,直接釋放掉從隊列拿出來的skb,從一而終的構(gòu)造包發(fā)送,不再腳踩兩只船。
復制代碼
好了,第二個問題解決了。
其三:也是最末的。應用層的阻塞讀問題。
以前我都是一次sendto,然后內(nèi)核一個回復,應用層再一個recvfrm就了事了。
結(jié)果后來改成
- while(1)
- {
- recvfrom();
- //把數(shù)據(jù)入庫
- }
復制代碼
的方式,發(fā)現(xiàn)后面的語句沒執(zhí)行,發(fā)現(xiàn)是recvfrom阻塞住了。。這個好辦,要讓while循環(huán)跳出,內(nèi)核通知應用層:“我沒數(shù)據(jù)了,別再再苦苦追尋了,不要浪費你時間”。。這似乎聽起來有些悲哀啊,呵呵![]()
這種人來的語言用程序來寫就是IP報文的分片標志吧,那就自己做個標志吧。
可以這樣做,自己定一個消息頭,放在netlink消息的開始位置,大小固定,或者直接放一個整數(shù)都行,反正就是用來標識是否還有數(shù)據(jù)的。
當內(nèi)核中還有數(shù)據(jù)要發(fā)送時,每次發(fā)送消息中,這個標志位為1,告訴他:“你還有希望,繼續(xù)追”![]()
如果沒有數(shù)據(jù)了,發(fā)送一個空包或者帶數(shù)據(jù)的報文,其中標志位為0,告訴他:“我已經(jīng)領(lǐng)證了,終止吧”,用戶態(tài)讀到這個
標志位,跳出循環(huán),后續(xù)工作繼續(xù)。。
就以上這三個問題,是我實際中遇到的,希望對大家有用。
|
|