2016-10-11 12 views
2

私は現在、ロバート・愛のLinuxカーネル開発て読んでいる、第3版、私はclear_bit()アトミック関数とその非、set_bit()を説明するセクションの後に奇数の文に遭遇しました-atomic兄弟、__set_bit()__clear_bit():アトミック整数演算とは異なりプラットフォームに依存しない方法

、コードは、典型的には、特定のビットを設定する唯一の移植可能な方法であるビット演算—を使用するかどうか選択の余地を有していません。

-p。 183(自分の強調)

Iは、これらの操作は、これらのインライン関数が存在する理由である単一のプラットフォーム固有のアセンブリ命令で実装できることを理解します。しかし、私は、著者はこれらがこれらの事を行うにはだけポータブルな方法であることを言った理由として興味があります。これを行うことにより、unsigned long x

x |= 1UL << nr; 

同様に私ができる非アトミッククリアビットnr

x &= ~(1UL << nr); 
たとえば、私は非アトミックプレーンなCでこれを行うことにより、 unsigned long xにビット nrを設定することができます信じています

もちろん、コンパイラの洗練度に応じて、これらはいくつかの命令にコンパイルされる可能性がありますので、__set_bit()__clear_bit()の関数ほど良くないかもしれません。

ここに何か不足していますか?このフレーズは、わずかに怠惰な簡素化した、または私はビットをセットしてクリアする上で提示しまし方法について移植何かがあるのでしょうか?

編集:それはGCCはかなり洗練されているが、それはまださえ-O3(バージョン6.2.1)に、代わりに__set_bit()機能が行うよう、単一の命令を使用してのビットシフトを行い、ことが表示されます。例として:原子整数演算の文脈において

stephen at greed in ~/code 
$ gcc -g -c set.c -O3   

stephen at greed in ~/code 
$ objdump -d -M intel -S set.o 

set.o:  file format elf64-x86-64 


Disassembly of section .text.startup: 

0000000000000000 <main>: 
#include<stdio.h> 
int main(int argc, char *argv) 
{ 
    unsigned long x = 0; 
    x |= (1UL << argc); 
    0: 89 f9     mov ecx,edi 
    2: be 01 00 00 00   mov esi,0x1 
    7: 48 83 ec 08    sub rsp,0x8 
    b: 48 d3 e6    shl rsi,cl 
    e: bf 00 00 00 00   mov edi,0x0 
    13: 31 c0     xor eax,eax 
    15: e8 00 00 00 00   call 1a <main+0x1a> 
{ 
    1a: 31 c0     xor eax,eax 
    x |= (1UL << argc); 
    1c: 48 83 c4 08    add rsp,0x8 
    printf("x=%x\n", x); 
    20: c3      ret 
+0

あなたは( 'signed')' long'の符号ビットにシフトする場合、これはUBです。 – EOF

+0

フェアポイント - カーネルの操作は 'ボイド*'で定義されているが、私は簡単にするために整数を使用することを決めています。私はそれらを 'unsigned long'に変更します。 – brenns10

+1

編集内容は変更されません。 '1L << nr'は関係なく、あなたは結果を割り当てるどのようなタイプの、署名long'タイプ'で行われます。 – EOF

答えて

1

、「ビット単位操作」は、原子のものを意味します。

非原子ビット演算では特別なことはありません(BITS_PER_LONGより大きい数値をサポートしている点を除いて)。generic implementationは常に正しいので、アーキテクチャ特有の実装はパフォーマンスを最適化するためにのみ必要です。

+0

これらの一般的な実装が存在していた。 – brenns10

関連する問題