Bir yapıyı c olarak paketlemenin standart bir yolu veya standart bir alternatifi var mı?


13

CI'da programlama yaparken, GCCs __attribute__((__packed__))özniteliğini kullanarak yapıları paketlemenin paha biçilmez olduğunu gördüm, böylece yapılandırılmış bir uçucu bellek yığınını bir veri yolu üzerinden iletilecek, depoya kaydedilecek veya bir kayıt bloğuna uygulanacak bir bayt dizisine kolayca dönüştürebilirim. Paketlenmiş yapılar, bir bayt dizisi olarak işlendiğinde, hem savurgan, olası bir güvenlik riski hem de arabirim donanımı ile uyumsuz olan herhangi bir dolgu içermeyeceğini garanti eder.

Tüm C derleyicilerinde çalışan paketleme yapıları için bir standart yok mu? Değilse o zaman bu sistem programlama için kritik bir özellik olduğunu düşünüyorum bir aykırı? C dilinin ilk kullanıcıları paketleme yapılarına ihtiyaç duymadılar mı, yoksa bir çeşit alternatif var mı?


derleme etki alanlarında yapıların kullanılması, özellikle donanıma (başka bir derleme etki alanıdır) işaret etmek için çok kötü bir fikirdir. Paket yapıları bunu yapmak için sadece bir hile, çok kötü yan etkileri var, bu yüzden sorunlarınız için daha az yan etki ile daha birçok çözüm var ve bunlar daha taşınabilir.
old_timer

Yanıtlar:


12

Bir yapıda önemli olan her bir üyenin her bir yapı örneğinin adresinden uzaklığıdır. İşlerin ne kadar sıkı bir şekilde paketlendiği meselesi o kadar da değil.

Ancak bir dizi, "paketlenmiş" olma şekliyle önemlidir. C'deki kural, her dizi öğesinin bir öncekinden tam olarak N bayt olmasıdır; burada N, bu türü depolamak için kullanılan bayt sayısıdır.

Ancak bir yapı ile, tekdüzeliğe böyle bir ihtiyaç yoktur.

İşte garip bir paketleme şemasına bir örnek:

Freescale (otomotiv mikrodenetleyicileri yapan) Zaman İşleme Birimi eş işlemcisine (eTPU veya TPU için google) sahip bir mikro yapar. İki yerel veri boyutu vardır, 8 bit ve 24 bit ve sadece tamsayılarla ilgilenir.

Bu yapı:

struct a
{
  U24 elementA;
  U24 elementB;
};

her U24'ün kendi 32 bit bloğunu sakladığını görecek, ancak yalnızca en yüksek adres alanında.

Bu:

struct b
{
  U24 elementA;
  U24 elementB;
  U8  elementC;
};

bitişik 32 bitlik bloklarda iki U24 saklanacak ve U8, ilk U24'ün önündeki "delikte" depolanacaktır elementA.

Ancak derleyiciye her şeyi kendi 32 bit bloğuna koymasını söyleyebilirsiniz; RAM'de daha pahalıdır, ancak erişim için daha az talimat kullanır.

"Paketleme", "sıkıca paketlemek" anlamına gelmez - sadece bir yapının elemanlarını ofset için düzenlemek için bazı şema anlamına gelir.

Genel bir şema yoktur, derleyiciye + mimariye bağımlıdır.


1
TPU için derleyici diğer elemanlardan herhangi birinden önce struct bhareket elementCederse, bu uygun bir C derleyicisi değildir. C
öğesinde

İlginç ama U24 standart bir C türü değil en.m.wikipedia.org/wiki/C_data_types complier biraz garip bir şekilde idare etmek zorunda olduğunu şaşırtıcı değildir o yüzden.
satur9nine

32 bitlik bir kelime boyutuna sahip ana CPU çekirdeği ile RAM paylaşıyor. Ancak bu işlemcinin sadece 24 bit veya 8 bit ile ilgilenen bir ALU'su var. Yani 32 bit sözcüklerle 24 bit sayıları yerleştirmek için bir şemaya sahiptir. Standart olmayan, ancak paketleme ve hizalamanın harika bir örneği. Anlaşıldı, çok standart değil.
RichColours

6

CI programlama zaman GCCs kullanarak yapıları paketi için çok değerli bulduk __attribute__((__packed__))[...]

Bahsettiğinizden beri __attribute__((__packed__)), niyetinizin bir içindeki tüm dolguları ortadan kaldırmak olduğunu varsayıyorum struct(her üyenin 1 baytlık bir hizalamaya sahip olmasını sağlayın).

Tüm C derleyicilerinde çalışan paketleme yapıları için bir standart yok mu?

... Ve cevap hayır". Bir yapıya (ve yığın veya yığındaki yapıların bitişik dizileri) göre dolgu ve veri hizalama önemli bir nedenden ötürü mevcuttur. Birçok makinede, hizalanmamış bellek erişimi potansiyel olarak önemli bir performans cezasına yol açabilir (ancak bazı yeni donanımlarda daha az olur). Bazı nadir durumlarda, yanlış hizalanmış bellek erişimi, kurtarılamayan bir veri yolu hatasına yol açar (hatta tüm işletim sistemini kilitleyebilir).

C standardı taşınabilirliğe odaklandığından, bir yapıdaki tüm dolguları ortadan kaldırmak ve sadece keyfi alanların yanlış hizalanmasına izin vermek için standart bir yol olması pek mantıklı değildir, çünkü bunu yapmak C kodunu taşınabilir hale getirme riskini doğurur.

Bu tür verileri tüm dolguyu ortadan kaldıracak şekilde harici bir kaynağa çıkarmanın en güvenli ve en taşınabilir yolu, yalnızca ham bellek içeriğini göndermeye çalışmak yerine bayt akışlarına / dizisinden serileştirmektir structs. Bu ayrıca programınızın bu serileştirme bağlamının dışında performans cezalarına maruz kalmasını önler ve ayrıca structtüm yazılımı atmadan ve hata yapmadan yeni alanlara özgürce yeni alanlar eklemenizi sağlar . Endianlık ve bunun gibi şeyler ile başa çıkmak için biraz endişe duyuyorsanız size de yer açacaktır.

Derleyiciye özgü direktiflere ulaşmadan tüm dolguları ortadan kaldırmanın bir yolu vardır, ancak yalnızca alanlar arasındaki göreceli sıralama önemli değilse uygulanabilir. Böyle bir şey verildiğinde:

struct Foo
{
    double x;  // assume 8-byte alignment
    char y;    // assume 1-byte alignment
               // 7 bytes of padding for first field
};

... bu alanları içeren yapının adresine göre hizalanmış bellek erişimi için dolguya ihtiyacımız var, şöyle:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......

... .dolguyu gösterir. Her xbiri performans için 8 baytlık bir sınıra (ve hatta bazen doğru davranışa) hizalanmalıdır.

Bunun gibi bir SoA (dizi yapısı) gösterimi kullanarak dolguyu taşınabilir bir şekilde ortadan kaldırabilirsiniz (8 Fooörneğe ihtiyacımız olduğunu varsayalım ):

struct Foos
{
   double x[8];
   char y[8];
};

Yapıyı etkili bir şekilde yıktık. Bu durumda, bellek temsili şu şekilde olur:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______

... ve bu:

01234567
yyyyyyyy

... artık doldurma yükü yok ve bu veri alanlarına artık bir yapı adresinin ofseti olarak değil, etkili bir şekilde bir dizi için temel adresin ofseti olarak eriştiğimizden, yanlış hizalanmış bellek erişimi içermeden.

Bu aynı zamanda, hem tüketilecek daha az verinin (makinenin ilgili veri tüketim oranını yavaşlatmak için karışımda daha fazla alakasız dolgu olmaması) hem de derleyicinin işlemi çok önemsiz bir şekilde vektörelleştirmesinin bir sonucu olarak sıralı erişim için daha hızlı olma bonusu taşır. .

Dezavantajı kodlamak için bir PITA olmasıdır. Ayrıca, genellikle AoS veya AoSoA temsilcilerinin daha iyi olacağı alanlar arasındaki daha büyük adımlarla rastgele erişim için potansiyel olarak daha az verimlidir. Ancak bu, yastığı ortadan kaldırmanın ve her şeyin hizalanmasıyla vidalanmadan işleri mümkün olduğunca sıkı bir şekilde paketlemenin standart bir yoludur.


2
Yapı düzenini açıkça belirtmek için bir araca sahip olmanın taşınabilirliği büyük ölçüde artıracağını savunuyorum . Bazı mizanpajlar bazı makinelerde çok verimli koda ve diğerlerinde çok verimsiz koda yol açarken, kod tüm makinelerde çalışır ve en azından bazılarında etkili olur. Aksine, böyle bir özelliğin yokluğunda, tüm makinelerde kod çalışması yapmanın tek yolu ya tüm makinelerde verimsiz hale getirmek ya da hızlı taşınabilir olmayan bir araya getirmek için bir grup makro ve koşullu derleme kullanmaktır. program ve aynı kaynakta yavaş taşınabilir bir program.
supercat

Kavramsal olarak evet, her şeyi bitlere ve bayt temsillerine, hizalama gereksinimlerine, endianiteye vb. Kadar belirleyebilir ve isteğe bağlı olarak altta yatan mimariden boşanırken C'de bu tür açık kontrole izin veren bir özelliğe sahip olsaydık ... ATM - şu anda bir serileştirici için en taşınabilir çözüm, onu tam bitlere ve bayt gösterimlerine ve veri türlerinin hizalanmasına bağlı olmayacak şekilde yazmaktır. Maalesef ATM'nin başka türlü etkili bir şekilde yapacağı araçlardan yoksun (C cinsinden).

5

Tüm mimariler aynı değildir, sadece bir modülde 32 bit seçeneğini açın ve aynı kaynak kodunu ve aynı derleyiciyi kullanırken ne olduğunu görün. Bayt sırası bir diğer iyi bilinen sınırlamadır. Kayan nokta gösterimi atın ve sorunlar daha da kötüleşir. İkili veri göndermek için Paketleme'yi kullanmak taşınabilir değildir. Bu standardı pratik olarak kullanılabilir hale getirmek için, C Dili spesifikasyonunu yeniden tanımlamanız gerekir.

Her ne kadar yaygın olsa da, verilerin güvenliğini, taşınabilirliğini veya uzun ömürlü olmasını istiyorsanız, ikili verileri göndermek için Pack'i kullanmak kötü bir fikirdir. Bir kaynağından programınıza ikili bir bloğu ne sıklıkla okursunuz. Bir bilgisayar korsanının veya program değişikliğinin verilere 'ulaşmadığını' tüm değerlerin ne kadar aklı başında olduğunu kontrol edersiniz? Bir kontrol rutini kodladığınız zaman, içe ve dışa aktarma rutinlerini de kullanıyor olabilirsiniz.


0

Çok yaygın bir alternatif "adlandırılmış dolgu":

struct s {
  short s1;
  char  c2;
  char  reserved; // Padding
};

Bu does yapı 8 bayt yastıklı olmayacak varsayalım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.