结构在优化编译中的对齐问题
Q: 我正在写一个流模块,其中用到了#pragma pack(),当使用
gcc -D_KERNEL -c abc.c
ld -r -o abc abc.o
编译链接时,一切正常。为了获得64-bit模块,我必须使用Sun Workshop 5.0,
结果导致系统崩溃。访问
http://docs.sun.com/htmlcoll/coll.32.8/iso-8859-1/CPPPG/Pragmas.html#15434
上面说必须在编译链接应用程序的时候指定"-misalign",所以我用了如下命令编译
/opt/SUNWspro/bin/cc -D_KERNEL -misalign -c abc.c
/usr/ccs/bin/ld -r -o abc abc.o
但是我不知道该如何在链接时指定"-misalign"。使用的是"/usr/ccs/bin/ld"。
A: Casper H.S. Dik - Network Security Engineer <Casper.Dik@Holland.Sun.Com>
"-misalign"仅仅用于应用程序,无法应用到内核编程中。"-misalign"使得编译
获得的代码增加了一些runtime glue,它们将指示内核模拟unaligned load(慢)。
作为内核编程,没有等效技术。
Q: 使用#pragma pack()是因为需要读取来自Windows客户端的报文,对端使用
#pragma pack(1)压缩了所使用的数据结构
#pragma pack(1)
typedef struct pkt_hdr_struct
{
uint8_t pkt_ver;
uint32_t pkt_type;
uint32_t pkt_len;
} pkt_hdr_t;
#pragma pack()
为了采用这个结构读取网络数据,Solaris端的服务程序需要强制转换匹配该结构,
但是一旦企图读取紧接在pkt_ver成员之后的pkt_type成员,崩溃了。尝试过其他
办法,首先用一个字符指针读取第一个字节,然后指针增一,把该指针强制类型
转换成( uint32_t * ),然后读取数据,依然崩溃。
此外,是否意味着无法在内核模块编程中使用#pragma pack()
A: Ed L Cashin <ecashin@coe.uga.edu>
我想你可以单独写一个pkt_header_read()函数,单字节读取然后拼装成相应的数
据类型。如果你想避免函数调用,可以使用"inline"关键字。
A: Casper H.S. Dik - Network Security Engineer <Casper.Dik@Holland.Sun.Com>
你是否意识到pkt_hdr_t结构使得你必须自己转换字节序(对端是x86平台)
我不认为#pragma pack()是最好的解决办法,考虑定义如下结构
struct phs
{
char ver;
char type[4];
char len[4];
}
采用memcpy()读取数据
memcpy( &phs.type[0], &pkt.pkt_type, 4 );
A: Andrew Gabriel <andrew@cucumber.demon.co.uk>
采用字符指针是正确的,但是你犯了个错误,编写如下函数
int read_misaligned_int ( int * iptr )
{
int i;
int value;
char * ptr = ( char * )iptr;
char * vptr = ( char * )&value;
for ( i = 0; i < sizeof( int ); i++ )
{
*vptr++ = *ptr++;
}
return( value );
}
此外,既然你提到对端是x86平台,可能还需要考虑字节序转换的问题
A: W. Richard Stevens <1999年逝世,享年49岁>
/*
* return value:
* 1 big-endian
* 2 little-endian
* 3 unknow
* 4 sizeof( short ) != 2
*/
static int byte_order ( void )
{
union
{
short s;
char c[ sizeof( short ) ];
} un;
un.s = 0x0201;
if ( 2 == sizeof( short ) )
{
if ( ( 2 == un.c[0] ) && ( 1 == un.c[1] ) )
{
puts( "big-endian" );
return( 1 );
}
else if ( ( 1 == un.c[0] ) && ( 2 == un.c[1] ) )
{
puts( "little-endian" );
return( 2 );
}
else
{
puts( "unknow" );
return( 3 );
}
}
else
{
puts( "sizeof( short ) = %d", sizeof( short ) );
return( 4 );
}
return( 3 );
} /* end of byte_order */
D: CERNET 华中地区网络中心 程序设计版 集体讨论汇总
为了解决Unix自定义结构在GCC优化编译中对齐问题,一般解决办法是用如下宏封装
自定义结构
#pragma pack(1)
struct my_arphdr
{
};
#pragma pack()
如果是SPARC/Solaris,还可以这样
struct my_arphdr
{
} __attribute__ ((packed));
两种办法其实都可以用在Unix系统/GCC编译器中。
D: mbuf@smth
关于结构中字节对齐问题,相应编译器选项为
GCC/G++ : -fpack-struct
Sun Workshop cc/CC: -misalign
最好不这样做,会大大降低程序效率,特别在某些架构中。应该尝试用位操作来处理。
D: Unknown@smth
GCC可以这么解决
#ifdef __GCC__
#define PACKED __attribute__((__packed__))
#else
#define PACKED
#endif
struct msg
{
u_int16_t PACKED first;
...
};
还是 VC 简单,#include <pshpack1.h> 就搞定了
A: gfh_nuaa
DEC : #pragma pack(1)
SUN : #pragma pack(1)
AIX : 编译时 -q align=packed
HP-UX : #pragma pack 1
D: Joe Durusau
在 Visual C++ 中,使用 "-ZP1" 就可以让编译器对自定义结构进行单字节对齐,实
际就是取消了对齐优化。
A: law@apue.dhs.org 2001-12-20 13:09
1) 结构内部成员的pack
struct foo
{
char a;
int b __attribute__ ((packed));
};
2) 整个结构的pack
struct foo
{
char a;
int b;
}__attribute__ ((packed));
3) 文件范围的pack
#pragma pack(1)
struct foo
{
char a;
int b;
};
... ...
4) 编译选项的pack
-fpack-struct
但这是最危险的做法,因为这样做可能会使库函数和你的程序对结构内成员的偏移理
解不一致。
Q: 小四 <scz@nsfocus.com>
#pragma pack(push)
#pragma pack(n)
... ...
#pragma pack(pop)
push/pop这个用法都谁支持啊
A: law@apue.dhs.org
这个写法我没见过,VC和GCC都是这样写的
#pragma (push, N) // 把原来align设置压栈,并设新的pack为N
#pragma (pop) // align设置弹栈
8.6 如何得到非局部变量列表
Q: 什么工具可以从目标文件中提取非局部变量列表
A: Donald McLachlan <don@mars.dgrc.crc.ca>
最简单的就是nm,假设你有一个目标文件(或者已链接过的可执行文件),nm -g将显
示所有"全局"变量。下面是一个Solaris的例子:
--------------------------------------------------------------------------
/* gcc -o junk junk.c */
int var1;
static int var2;
int main ( void )
{
int var3;
return( 0 );
} /* end of main */
--------------------------------------------------------------------------
Q: 我正在写一个流模块,其中用到了#pragma pack(),当使用
gcc -D_KERNEL -c abc.c
ld -r -o abc abc.o
编译链接时,一切正常。为了获得64-bit模块,我必须使用Sun Workshop 5.0,
结果导致系统崩溃。访问
http://docs.sun.com/htmlcoll/coll.32.8/iso-8859-1/CPPPG/Pragmas.html#15434
上面说必须在编译链接应用程序的时候指定"-misalign",所以我用了如下命令编译
/opt/SUNWspro/bin/cc -D_KERNEL -misalign -c abc.c
/usr/ccs/bin/ld -r -o abc abc.o
但是我不知道该如何在链接时指定"-misalign"。使用的是"/usr/ccs/bin/ld"。
A: Casper H.S. Dik - Network Security Engineer <Casper.Dik@Holland.Sun.Com>
"-misalign"仅仅用于应用程序,无法应用到内核编程中。"-misalign"使得编译
获得的代码增加了一些runtime glue,它们将指示内核模拟unaligned load(慢)。
作为内核编程,没有等效技术。
Q: 使用#pragma pack()是因为需要读取来自Windows客户端的报文,对端使用
#pragma pack(1)压缩了所使用的数据结构
#pragma pack(1)
typedef struct pkt_hdr_struct
{
uint8_t pkt_ver;
uint32_t pkt_type;
uint32_t pkt_len;
} pkt_hdr_t;
#pragma pack()
为了采用这个结构读取网络数据,Solaris端的服务程序需要强制转换匹配该结构,
但是一旦企图读取紧接在pkt_ver成员之后的pkt_type成员,崩溃了。尝试过其他
办法,首先用一个字符指针读取第一个字节,然后指针增一,把该指针强制类型
转换成( uint32_t * ),然后读取数据,依然崩溃。
此外,是否意味着无法在内核模块编程中使用#pragma pack()
A: Ed L Cashin <ecashin@coe.uga.edu>
我想你可以单独写一个pkt_header_read()函数,单字节读取然后拼装成相应的数
据类型。如果你想避免函数调用,可以使用"inline"关键字。
A: Casper H.S. Dik - Network Security Engineer <Casper.Dik@Holland.Sun.Com>
你是否意识到pkt_hdr_t结构使得你必须自己转换字节序(对端是x86平台)
我不认为#pragma pack()是最好的解决办法,考虑定义如下结构
struct phs
{
char ver;
char type[4];
char len[4];
}
采用memcpy()读取数据
memcpy( &phs.type[0], &pkt.pkt_type, 4 );
A: Andrew Gabriel <andrew@cucumber.demon.co.uk>
采用字符指针是正确的,但是你犯了个错误,编写如下函数
int read_misaligned_int ( int * iptr )
{
int i;
int value;
char * ptr = ( char * )iptr;
char * vptr = ( char * )&value;
for ( i = 0; i < sizeof( int ); i++ )
{
*vptr++ = *ptr++;
}
return( value );
}
此外,既然你提到对端是x86平台,可能还需要考虑字节序转换的问题
A: W. Richard Stevens <1999年逝世,享年49岁>
/*
* return value:
* 1 big-endian
* 2 little-endian
* 3 unknow
* 4 sizeof( short ) != 2
*/
static int byte_order ( void )
{
union
{
short s;
char c[ sizeof( short ) ];
} un;
un.s = 0x0201;
if ( 2 == sizeof( short ) )
{
if ( ( 2 == un.c[0] ) && ( 1 == un.c[1] ) )
{
puts( "big-endian" );
return( 1 );
}
else if ( ( 1 == un.c[0] ) && ( 2 == un.c[1] ) )
{
puts( "little-endian" );
return( 2 );
}
else
{
puts( "unknow" );
return( 3 );
}
}
else
{
puts( "sizeof( short ) = %d", sizeof( short ) );
return( 4 );
}
return( 3 );
} /* end of byte_order */
D: CERNET 华中地区网络中心 程序设计版 集体讨论汇总
为了解决Unix自定义结构在GCC优化编译中对齐问题,一般解决办法是用如下宏封装
自定义结构
#pragma pack(1)
struct my_arphdr
{
};
#pragma pack()
如果是SPARC/Solaris,还可以这样
struct my_arphdr
{
} __attribute__ ((packed));
两种办法其实都可以用在Unix系统/GCC编译器中。
D: mbuf@smth
关于结构中字节对齐问题,相应编译器选项为
GCC/G++ : -fpack-struct
Sun Workshop cc/CC: -misalign
最好不这样做,会大大降低程序效率,特别在某些架构中。应该尝试用位操作来处理。
D: Unknown@smth
GCC可以这么解决
#ifdef __GCC__
#define PACKED __attribute__((__packed__))
#else
#define PACKED
#endif
struct msg
{
u_int16_t PACKED first;
...
};
还是 VC 简单,#include <pshpack1.h> 就搞定了
A: gfh_nuaa
DEC : #pragma pack(1)
SUN : #pragma pack(1)
AIX : 编译时 -q align=packed
HP-UX : #pragma pack 1
D: Joe Durusau
在 Visual C++ 中,使用 "-ZP1" 就可以让编译器对自定义结构进行单字节对齐,实
际就是取消了对齐优化。
A: law@apue.dhs.org 2001-12-20 13:09
1) 结构内部成员的pack
struct foo
{
char a;
int b __attribute__ ((packed));
};
2) 整个结构的pack
struct foo
{
char a;
int b;
}__attribute__ ((packed));
3) 文件范围的pack
#pragma pack(1)
struct foo
{
char a;
int b;
};
... ...
4) 编译选项的pack
-fpack-struct
但这是最危险的做法,因为这样做可能会使库函数和你的程序对结构内成员的偏移理
解不一致。
Q: 小四 <scz@nsfocus.com>
#pragma pack(push)
#pragma pack(n)
... ...
#pragma pack(pop)
push/pop这个用法都谁支持啊
A: law@apue.dhs.org
这个写法我没见过,VC和GCC都是这样写的
#pragma (push, N) // 把原来align设置压栈,并设新的pack为N
#pragma (pop) // align设置弹栈
8.6 如何得到非局部变量列表
Q: 什么工具可以从目标文件中提取非局部变量列表
A: Donald McLachlan <don@mars.dgrc.crc.ca>
最简单的就是nm,假设你有一个目标文件(或者已链接过的可执行文件),nm -g将显
示所有"全局"变量。下面是一个Solaris的例子:
--------------------------------------------------------------------------
/* gcc -o junk junk.c */
int var1;
static int var2;
int main ( void )
{
int var3;
return( 0 );
} /* end of main */
--------------------------------------------------------------------------