Rumah  >  Artikel  >  Tutorial sistem  >  Perkara tentang penjajaran bait Linux

Perkara tentang penjajaran bait Linux

WBOY
WBOYke hadapan
2024-02-05 11:06:101048semak imbas

Baru-baru ini, saya sedang mengusahakan projek dan menghadapi masalah. Apabila ThreadX berjalan pada ARM berkomunikasi dengan DSP, ia menggunakan baris gilir mesej untuk menghantar mesej (pelaksanaan akhir menggunakan kaedah gangguan dan memori dikongsi). Walau bagaimanapun, semasa operasi sebenar, didapati ThreadX sering terhempas. Selepas penyiasatan, didapati bahawa masalahnya terletak pada fakta bahawa struktur yang menghantar mesej tidak mempertimbangkan penjajaran bait.

Saya ingin menyelesaikan isu tentang penjajaran bait dalam bahasa C dan berkongsi dengan anda.

1. Konsep

Penjajaran bait berkaitan dengan lokasi data dalam ingatan. Jika alamat memori pembolehubah adalah tepat gandaan integer panjangnya, maka ia dikatakan sejajar secara semula jadi. Sebagai contoh, di bawah CPU 32-bit, dengan mengandaikan alamat pembolehubah integer ialah 0x00000004, maka ia diselaraskan secara semula jadi.

Mula-mula faham apa itu bit, bait dan perkataan

Nama Nama Inggeris Maksudnya
bit bit 1 digit binari dipanggil 1 bit
bait Byte 8 bit binari dipanggil 1 Byte
perkataan perkataan Panjang tetap yang digunakan oleh komputer untuk memproses transaksi pada satu masa

Panjang

Bilangan bit dalam perkataan, panjang perkataan komputer moden biasanya 16, 32 atau 64 bit. (Secara amnya, panjang perkataan sistem N-bit ialah N/8 bait.)

CPU yang berbeza boleh memproses bilangan bit data yang berbeza pada satu masa CPU 32-bit boleh memproses data 32-bit pada satu masa, dan CPU 64-bit boleh memproses data 64-bit pada satu-satu masa panjang perkataan.

Panjang perkataan yang dipanggil kadangkala dipanggil perkataan. Dalam CPU 16-bit, perkataan adalah tepat dua bait, manakala dalam CPU 32-bit, perkataan adalah empat bait. Jika kita mengambil aksara sebagai unit, terdapat dua aksara (dua aksara) dan empat aksara (empat aksara) ke atas.

2. Peraturan penjajaran

Untuk jenis data standard, alamatnya hanya perlu gandaan integer daripada jenis data bukan standard dijajarkan mengikut prinsip berikut: Tatasusunan: Dijajarkan mengikut jenis data asas, yang berikut yang secara semula jadi akan diselaraskan. Kesatuan: Dijajarkan mengikut jenis data yang terkandung di dalamnya dengan panjang terbesar. Struktur: Setiap jenis data dalam struktur mesti diselaraskan.

3. Bagaimana untuk mengehadkan bilangan bit penjajaran bait tetap?

1. Lalai

Secara lalai, pengkompil C memperuntukkan ruang untuk setiap pembolehubah atau unit data mengikut keadaan sempadan semula jadinya. Secara amnya, syarat sempadan lalai boleh diubah dengan kaedah berikut:

2. #pragma pack(n)

· Menggunakan pek #pragma arahan (n), pengkompil C akan menjajarkan sebanyak n bait. · Gunakan pek #pragma arahan () untuk membatalkan penjajaran bait tersuai.

#pragma pack(n) digunakan untuk menetapkan pembolehubah kepada penjajaran n-bait. penjajaran n-bait bermakna terdapat dua situasi untuk mengimbangi alamat permulaan tempat pembolehubah disimpan:

  1. Jika n lebih besar daripada atau sama dengan bilangan bait yang diduduki oleh pembolehubah, maka offset mesti memenuhi penjajaran lalai
  2. Jika n adalah kurang daripada bilangan bait yang diduduki oleh jenis pembolehubah, offset ialah gandaan n dan tidak perlu memenuhi penjajaran lalai.

Jumlah saiz struktur juga mempunyai kekangan Jika n lebih besar daripada atau sama dengan bilangan bait yang diduduki oleh semua jenis pembolehubah ahli, maka jumlah saiz struktur mestilah gandaan ruang yang diduduki oleh. pembolehubah dengan ruang terbesar; jika tidak, ia mestilah n gandaan.

3. __atribut

Selain itu, terdapat kaedah berikut: · __attribute((aligned (n))), yang membolehkan ahli struktur diselaraskan pada sempadan semula jadi n bait. Jika panjang mana-mana anggota dalam struktur lebih besar daripada n, ia dijajarkan mengikut panjang anggota terbesar. · atribut ((dikemaskan)), membatalkan penjajaran dioptimumkan struktur semasa proses penyusunan, dan menjajarkannya mengikut bilangan bait sebenar yang diduduki.

3. Kompilasi.selaraskan

Kod pemasangan biasanya menggunakan .align untuk menentukan bilangan bit penjajaran bait.

.align: digunakan untuk menentukan penjajaran data, formatnya adalah seperti berikut:

.align [absexpr1, absexpr2]

Isi kawasan storan yang tidak digunakan dengan nilai dalam penjajaran tertentu Nilai pertama mewakili penjajaran, 4, 8, 16 atau 32. Nilai ungkapan kedua mewakili nilai yang diisi.

四、为什么要对齐?

操作系统并非一个字节一个字节访问内存,而是按2,4,8这样的字长来访问。因此,当CPU从存储器读数据到寄存器,IO的数据长度通常是字长。如32位系统访问粒度是4字节(bytes), 64位系统的是8字节。当被访问的数据长度为n字节且该数据地址为n字节对齐时,那么操作系统就可以高效地一次定位到数据, 无需多次读取,处理对齐运算等额外操作。数据结构应该尽可能地在自然边界上对齐。如果访问未对齐的内存,CPU需要做两次内存访问。

字节对齐可能带来的隐患:

代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:

unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;

p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;

最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐.

五、举例

例1:os基本数据类型占用的字节数

首先查看操作系统的位数Perkara tentang penjajaran bait Linux

在64位操作系统下查看基本数据类型占用的字节数:

#include 

int main()
{
    printf("sizeof(char) = %ld\n", sizeof(char));
    printf("sizeof(int) = %ld\n", sizeof(int));
    printf("sizeof(float) = %ld\n", sizeof(float));
    printf("sizeof(long) = %ld\n", sizeof(long));                                      
    printf("sizeof(long long) = %ld\n", sizeof(long long));
    printf("sizeof(double) = %ld\n", sizeof(double));
    return 0;
}
Perkara tentang penjajaran bait Linux

例2:结构体占用的内存大小–默认规则

考虑下面的结构体占用的位数

struct yikou_s
{
    double d;
    char c;
    int i;
} yikou_t;

执行结果

sizeof(yikou_t) = 16

在内容中各变量位置关系如下:

其中成员C的位置Perkara tentang penjajaran bait Linux还受字节序的影响,有的可能在位置8

编译器给我们进行了内存对齐,各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量类型所占用的字节数的倍数, 且结构的大小为该结构中占用最大空间的类型所占用的字节数的倍数。

对于偏移量:变量type n起始地址相对于结构体起始地址的偏移量必须为sizeof(type(n))的倍数结构体大小:必须为成员最大类型字节的倍数

char: 偏移量必须为sizeof(char) 即1的倍数
int: 偏移量必须为sizeof(int) 即4的倍数
float: 偏移量必须为sizeof(float) 即4的倍数
double: 偏移量必须为sizeof(double) 即8的倍数

例3:调整结构体大小

我们将结构体中变量的位置做以下调整:

struct yikou_s
{
    char c;
    double d;
    int i;
} yikou_t;

执行结果

sizeof(yikou_t) = 24

各变量在内存中布局如下:

Perkara tentang penjajaran bait Linux

当结构体中有嵌套符合成员时,复合成员相对于结构体首地址偏移量是复合成员最宽基本类型大小的整数倍。

例4:#pragma pack(4)

#pragma pack(4)

struct yikou_s
{
    char c;
    double d;
    int i;
} yikou_t;
sizeof(yikou_t) = 16

例5:#pragma pack(8)

#pragma pack(8)

struct yikou_s
{
    char c;
    double d;
    int i;
} yikou_t;
sizeof(yikou_t) = 24

例6:汇编代码

举例:以下是截取的uboot代码中异常向量irq、fiq的入口位置代码:Perkara tentang penjajaran bait Linux

六、汇总实力

有手懒的同学,直接贴一个完整的例子给你们:

#include 
main()
{
struct A {
    int a;
    char b;
    short c;
};
 
struct B {
    char b;
    int a;
    short c;
};
struct AA {
   // int a;
    char b;
    short c;
};

struct BB {
    char b;
   // int a;
    short c;
}; 
#pragma pack (2) /*指定按2字节对齐*/
struct C {
    char b;
    int a;
    short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
 
 
 
#pragma pack (1) /*指定按1字节对齐*/
struct D {
    char b;
    int a;
    short c;
};
#pragma pack ()/*取消指定对齐,恢复缺省对齐*/
 
int s1=sizeof(struct A);
int s2=sizeof(struct AA);
int s3=sizeof(struct B);
int s4=sizeof(struct BB);
int s5=sizeof(struct C);
int s6=sizeof(struct D);
printf("%d\n",s1);
printf("%d\n",s2);
printf("%d\n",s3);
printf("%d\n",s4);
printf("%d\n",s5);
printf("%d\n",s6);
}

Atas ialah kandungan terperinci Perkara tentang penjajaran bait Linux. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam