4
  1. アラインされたintの読み取りと書き込みはアトミックで安全だと聞きましたが、パックされた構造とキャスト/ポインター算術バイトバッファー以外の非mallocされたグローバルをいつシステムがアラインされないようにするのでしょうか?

  2. [X86-64 linux]私の通常のケースではすべて、システムは常にワードが破損しない整数の場所を選択します。たとえば、一方のワードで2バイト、もう一方のワードで他の2バイトです。整数が引き裂かれ、システムが1つの整数値をロードするために2つの読み取りを使用する必要があるように、グローバル変数を非整列アドレスに強制するプログラム/スニップ(Cまたはアセンブリ)を投稿できますか?

    以下のプログラムを印刷すると、複数の変数が64ビット以内にあるようにアドレスが互いに接近していますが、単語のティアリングが一度も見られません(システムまたはコンパイラのスマートさ?)

    #include <stdio.h>
    int a;
    char  b;
    char c;
    int      d;
    int e = 0;
    
    
    int isaligned(void *p, int N)
    {
        if (((int)p % N) == 0)
            return 1;
        else
            return 0;
    }
    
    int main()
    {
    
        printf("processor is %d byte mode \n", sizeof(int *));
        printf ( "a=%p/b=%p/c=%p/d=%p/f=%p\n", &a, &b, &c, &d, &e );
    
        printf ( " check for 64bit alignment of test result of 0x80 = %d \n", isaligned( 0x80, 64 ));
        printf ( " check for 64bit alignment of a result = %d \n", isaligned( &a, 64 ));
        printf ( " check for 64bit alignment of d  result = %d \n", isaligned( &e, 64 ));
    
    return 0;}
    

    出力:

    processor is 8 byte mode 
    a=0x601038/b=0x60103c/c=0x60103d/d=0x601034/f=0x601030
     check for 64bit alignment of test result of 0x80 = 1 
     check for 64bit alignment of a result = 0 
     check for 64bit alignment of d  result = 0 
    
  3. 上記の場合、charの読み取りはどのように行われますか?8バイトに整列された境界(私の場合は0x601030)から読み取り、0x60103cに移動しますか?

  4. メモリアクセスの粒度は常にワードサイズですよね?

どうも。

4

5 に答える 5

4

1)はい、アラインされていないアクセスがアトミックであるという保証はありません。[少なくとも時々、特定のタイプのプロセッサでは]データが2つの別々の書き込みとして書き込まれる可能性があるためです。たとえば、メモリページの境界を越えた場合[私は仮想メモリの4KBページについて話しているのではなく、DDR2 / 3/4ページについて話しているのです。これは、合計メモリサイズの一部であり、通常、実際のメモリチップの幅の16Kビット倍です。これは、メモリスティック自体]。同様に、x86以外のプロセッサでは、アラインされていないメモリを読み取るためのトラップが発生します。これにより、プログラムが中止されるか、ソフトウェアで読み取りが複数の読み取りとしてエミュレートされ、アラインされていない読み取りが「修正」されます。

2)次のような方法で、常に非整列メモリ領域を作成できます。

char *ptr = malloc(sizeof(long long) * number+1);
long long *unaligned = (long long *)&ptr[2];

for(i = 0; i < number; i++)
   temp = unaligned[i]; 

ちなみに、アライメントチェックでは、アドレスが64ビットではなく64バイトにアライメントされているかどうかがチェックされます。64ビットにアラインされていることを確認するには、8で除算する必要があります。

3)charは1バイトの読み取りであり、アドレスはバイト自体の実際のアドレスになります。実行される実際のメモリ読み取りは、おそらく完全なキャッシュラインに対して行われ、ターゲットアドレスから始まり、次に循環します。たとえば、次のようになります。

0x60103dはターゲットアドレスであるため、プロセッサは必要な64ビットワードから始まる32バイトのキャッシュラインを読み取ります:0x601038(そして、それが完了するとすぐに、プロセッサは次の命令に進みます-その間、次の読み取りはキャッシュラインを埋めるために実行されます)、次にキャッシュラインは0x601020、0x601028、0x601030で埋められます。ただし、キャッシュをオフにした場合[3GHzの最新のx86プロセッサを66MHz 486よりもわずかに遅くしたい場合は、キャッシュを無効にするのが良い方法です]、プロセッサは0x60103dで1バイトを読み取るだけです。

4)x86プロセッサではなく、バイトアドレス指定がありますが、通常のメモリの場合、上記で説明したように、読み取りはキャッシュラインベースで行われます。

また、「アトミックではない可能性がある」は「アトミックではない」とはまったく同じではないことにも注意してください。したがって、意志で失敗させるのは難しいでしょう。実際には、2つの異なるタイミングをすべて取得する必要があります。スレッドが適切で、キャッシュラインにまたがったり、メモリページの境界にまたがったりするなどして、問題が発生します。これは、発生させたくない場合に発生しますが、問題を発生させようとすると、非常に困難になる可能性があります[信頼してください。私はそこに行ったことがあります]。

于 2013-02-01T00:01:49.447 に答える
2
  1. それらの場合を除いて、おそらくそうではありません。

  2. 組み立てでは、それは些細なことです。何かのようなもの:

         .org 0x2
    myglobal:
         .word SOME_NUMBER
    

    しかし、Intelでは、プロセッサはアラインされていないメモリを安全に読み取ることができます。アトミックではないかもしれませんが、生成されたコードからは明らかではないかもしれません。

  3. インテル、そうですか?Intel ISAには、シングルバイトの読み取り/書き込みオペコードがあります。プログラムを分解して、何が使用されているかを確認します。

  4. 必ずしもそうとは限りません。メモリワードサイズとプロセッサワードサイズの間に不一致がある可能性があります。

于 2013-01-31T23:03:08.670 に答える
2

1)この回答はプラットフォーム固有です。ただし、一般的に、コンパイラーは、他の方法で強制しない限り、変数を整列させます。

2)以下は、32ビットCPUで実行する場合、1つの変数をロードするために2回の読み取りが必要になります。

uint64_t huge_variable;

変数はレジスタよりも大きいため、アクセスするには複数の操作が必要になります。パック構造を使用して、同様のことを行うこともできます。

struct unaligned __attribute__ ((packed))
{
    char buffer[2];
    int  unaligned;
    char buffer2[2];
} sample_struct;

3)この回答はプラットフォーム固有です。一部のプラットフォームは、説明したように動作する場合があります。一部のプラットフォームには、データのハーフレジスタまたはクォーターレジスタをフェッチできる命令があります。詳細については、コンパイラーによって発行されたアセンブリーを調べることをお勧めします(最初にすべてのコンパイラー最適化をオフにしてください)。

4)C言語を使用すると、バイトサイズの粒度でメモリにアクセスできます。これが内部でどのように実装されるか、および1バイトを読み取るためにCPUがフェッチするデータの量は、プラットフォームによって異なります。多くのCPUの場合、これは汎用レジスタのサイズと同じです。

于 2013-01-31T23:05:38.550 に答える
0
  1. malloc(3)C規格は、最も厳しいアライメント要件に準拠するメモリ領域を返すことを保証しているため、この場合はこれは発生しません。整列されていないデータがある場合、それはおそらく断片ごとに読み取り/書き込みされます(これはアーキテクチャが提供する正確な保証に依存します)。
  2. 一部のアーキテクチャでは、整列されていないアクセスが許可されますが、他のアーキテクチャでは致命的なエラーになります。許可されている場合、通常、整列アクセスよりもはるかに低速です。許可されていない場合、コンパイラーは断片を取得してそれらをつなぎ合わせる必要があり、それはさらに遅くなります。
  3. 文字(実際にはバイト)は通常、任意のバイトアドレスを持つことができます。その場合、バイトを処理する命令は、個々のバイトを取得/格納するだけです。
  4. いいえ、メモリアクセスはデータの幅に応じて異なります。ただし、実際のメモリアクセスは、キャッシュラインの観点から行われます(これについては、 CPUキャッシュを参照してください)。
于 2013-02-01T00:06:34.060 に答える
0

未定義の動作を呼び出さないと、整列されていないオブジェクトが存在することはありません。言い換えると、一連のアクションはなく、すべてが明確に定義された動作を持ち、プログラムが実行できるため、整列されていないポインターが存在します。特に、コンパイラに位置がずれたオブジェクトを提供させる移植可能な方法はありません。最も近いのは、多くのコンパイラが持つ「パックされた構造」ですが、これは構造体のメンバーにのみ適用され、独立したオブジェクトには適用されません。

さらに、ポータブルCで整列性をテストする方法はありません。実装定義のポインターから整数への変換を使用して下位ビットを検査できますが、「整列された」ポインターの下位ビットがゼロであるという基本的な要件はありません。整数に変換された後の下位ビットは、それが何を意味するかにかかわらず、ポインタの「最下位」ビットにさえ対応します。言い換えると、算術演算で通勤するために、ポインターと整数の間の変換は必要ありません。

本当に位置がずれたポインタを作成したい場合、それを行う最も簡単な方法は、と仮定するとalignof(int)>1、次のようになります。

char buf[2*sizeof(int)+1];
int *p1 = (int *)buf, *p2 = (int *)(buf+sizeof(int)+1);

が1より大きい場合、両方bufbuf+sizeof(int)+1同時に整列させることはできません。したがって、2つのキャストの少なくとも1つが不整列のポインターに適用され、未定義の動作が呼び出され、通常の結果は不整列のポインターになります。intalignof(int)(int *)

于 2013-02-01T00:56:51.533 に答える