linux程式設計獲取本機網路相關引數
在LINUX下獲取網絡卡資訊需要用到IOCTL或者getifaddrs
而我在用getifaddrs的時候遇到了記憶體方面的問題
先看相關定義:
==========
函式定義:
/* Create a linked list of `struct ifaddrs' structures, one for each
network interface on the host machine. If successful, store the
list in *IFAP and return 0. On errors, return -1 and set `errno'.
The storage returned in *IFAP is allocated dynamically and can
only be properly freed by passing it to `freeifaddrs'. */
extern int getifaddrs (struct ifaddrs **__ifap) __THROW;
/* Reclaim the storage allocated by a previous `getifaddrs' call. */
extern void freeifaddrs (struct ifaddrs *__ifa) __THROW;
==============
此函式需要的結構體定義:
struct ifaddrs
{
struct ifaddrs *ifa_next; /* Pointer to the next structure. */
char *ifa_name; /* Name of this network interface. */
unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */
struct sockaddr *ifa_addr; /* Network address of this interface. */
struct sockaddr *ifa_netmask; /* Netmask of this interface. */
union
{
/* At most one of the following two is valid. If the IFF_BROADCAST
bit is set in `ifa_flags', then `ifa_broadaddr' is valid. If the
IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
It is never the case that both these bits are set at once. */
struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */
struct sockaddr *ifu_dstaddr; /* Point-to-point destination address. */
} ifa_ifu;
/* These very same macros are defined by <net/if.h> for `struct ifaddr'.
So if they are defined already, the existing definitions will be fine. */
# ifndef ifa_broadaddr
# define ifa_broadaddr ifa_ifu.ifu_broadaddr
# endif
# ifndef ifa_dstaddr
# define ifa_dstaddr ifa_ifu.ifu_dstaddr
# endif
void *ifa_data; /* Address-specific data (may be unused). */
};
=============
我在呼叫了getifaddrs()之後,正常地完成了需要的工作
但是最後如果用freeifaddrs,則出現執行時錯誤
*** glibc detected *** d: free(): invalid pointer: 0x0804a4d4 ***
======= Backtrace: =========
/lib/libc.so.6[0xb7eda911]
/lib/libc.so.6(__libc_free+0x84)[0xb7edbf84]
/lib/libc.so.6(freeifaddrs+0x1d)[0xb7f512dd]
d[0x8048989]
d[0x80486a5]
/lib/libc.so.6(__libc_start_main+0xdc)[0xb7e8c87c]
d[0x8048491]
======= Memory map: ========
08048000-08049000 r-xp 00000000 03:07 48637 /home/souldump/bin/d
08049000-0804a000 rw-p 00000000 03:07 48637 /home/souldump/bin/d
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
b7d00000-b7d21000 rw-p b7d00000 00:00 0
b7d21000-b7e00000 ---p b7d21000 00:00 0
b7e76000-b7e77000 rw-p b7e76000 00:00 0
b7e77000-b7f90000 r-xp 00000000 03:05 16184 /lib/libc-2.4.so
b7f90000-b7f92000 r--p 00118000 03:05 16184 /lib/libc-2.4.so
b7f92000-b7f94000 rw-p 0011a000 03:05 16184 /lib/libc-2.4.so
b7f94000-b7f98000 rw-p b7f94000 00:00 0
b7fab000-b7fb5000 r-xp 00000000 03:05 20108 /lib/libgcc_s.so.1
b7fb5000-b7fb6000 rw-p 00009000 03:05 20108 /lib/libgcc_s.so.1
b7fb6000-b7fb7000 rw-p b7fb6000 00:00 0
b7fb7000-b7fd1000 r-xp 00000000 03:05 16177 /lib/ld-2.4.so
b7fd1000-b7fd3000 rw-p 00019000 03:05 16177 /lib/ld-2.4.so
bfb2b000-bfb41000 rw-p bfb2b000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]
實際上也有人出現相同問題:
http://p.g.yupoo.com/nph-proxy.cgi/000110A/http/www.linuxdby.com/bbs/viewthread.php=3ftid=3d10756
此人說:"這說明不是真正的連結串列,指標非法"
但是又沒有進一步說明怎麼解決
他乾脆沒有呼叫freeifaddrs,自然會記憶體洩漏.....
我去看了afaddrs.c
freeifaddrs的定義居然是:
void
freeifaddrs (struct ifaddrs *ifa)
{
free (ifa);
}
怎麼樣,很囧吧,明明在標頭檔案裡說"必須用freeifaddrs才能正確free..."
然後我看了一下getifaddrs的函式體
他在getifaddrs內部定義了一個結構
struct ifaddrs_storage
{
struct ifaddrs ifa;
union
{
/* Save space for the biggest of the four used sockaddr types and
avoid a lot of casts. */
struct sockaddr sa;
struct sockaddr_ll sl;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
} addr, netmask, broadaddr;
char name[IF_NAMESIZE + 1];
};
然後把獲取的各網絡卡資訊一個個填充到此結構的struct ifaddrs ifa中,ifa的next值手動設定為下一個struct ifaddrs_storage中的ifa的地址...
這酒是所謂的"偽連結串列"吧?
這就是我無法正確free掉它的原因?
我究竟要怎麼把它free掉?freeifaddrs一執行就執行時錯誤
LINUX取得本機IP的簡單C程式
注意這裡用了兩個struct ifaddrs
//程式碼根據UNP和man手冊編寫
//適用於LINUX/BSD(FreeBSD, MacOS X)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
int main(void)
{
struct ifaddrs *ifc, *ifc1;
char ip[64];
char nm[64];
if (0 != getifaddrs(&ifc)) return(-1);
ifc1 = ifc;
printf("Iface\tIP address\tNetmask\n");
for(; NULL != ifc; ifc = (*ifc).ifa_next) {
printf("%s", (*ifc).ifa_name);
if (NULL != (*ifc).ifa_addr) {
inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_addr))->sin_addr), ip, 64);
printf("\t%s", ip);
} else {
printf("\t\t");
}
if (NULL != (*ifc).ifa_netmask) {
inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_netmask))->sin_addr), nm, 64);
printf("\t%s", nm);
} else {
printf("\t\t");
}
printf("\n");
}
freeifaddrs(ifc1);
return(0);
}
--------------------------------------------------------------------------------
============原來的============
struct ifaddrs *ifap, *ifaphead, *ifaTmp;
getifaddrs(&ifap);
ifaphead = ifap;
while((ifapTmp = ifap) != NULL)
{
//實際任務程式碼
ifap = ifapTmp->ifa_next;
}
freeifaddrs(ifaphead);
=========修改後========
struct ifaddrs *ifap, *ifaphead;
getifaddrs(&ifap);
ifaphead = ifap;
while(ifap != NULL)
{
//實際任務程式碼
ifap = ifa_next;
}
freeifaddrs(ifaphead);
==================
僅僅是用了一個ifapTmp來代替ifap做事,區別僅此而已(而且我也忘了一開始為什麼要用ifapTmp....)
但是最後都是用了freeifaddrs(ifaphead)啊,並沒有傳錯指標啊????
中間的程式碼並沒有對這段資料做任何修改啊.....
請指教一下,這唯一的區別為什麼會造成我原先的程式碼freeifaddrs失敗?謝謝!
ifaphead = ifap; //這裡用ifaphead儲存ifap指標地址
while((ifapTmp = ifap) != NULL)
{
ifap = ifapTmp->ifa_next; //這裡修改了ifap的地址
}
freeifaddrs(ifaphead); //由於ifap的地址修改,所以ifaphead已經是無效指標。