在LINUX下获取网卡信息&&getifaddrs的时候遇到了内存方面的问题&&struct socketaddr和struct socketaddr_in的区...

在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已经是无效指针。

********************************************************
struct socketaddr和struct socketaddr_in的区别

struct sockaddr,该类型是用来保存socket信息的:
struct sockaddr
{
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字节的协议地址 */
};
sa_family一般为AF_INET;
sa_data则包含该socket的IP地址和端口号。

另外还有一种结构类型:
struct sockaddr_in
{
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */
};
sin_zero(它用来将sockaddr_in结构填充到与struct sockaddr同样的长度)
应该用bzero()或memset()函数将其置为零。
指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时

,你可以在函数调用的时候将一个指向 sockaddr_in的指针转换为指向sockaddr的指针;或者相反。
sin_family通常被赋AF_INET;sin_port和 sin_addr应该转换成为网络字节优先顺序;而sin_addr则不需要转换。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值