@@ -662,6 +662,10 @@ include/linux/rcupdate.h 의 rcu_assign_pointer() 와 rcu_dereference() 를
662
662
컨트롤 의존성
663
663
-------------
664
664
665
+ 현재의 컴파일러들은 컨트롤 의존성을 이해하고 있지 않기 때문에 컨트롤 의존성은
666
+ 약간 다루기 어려울 수 있습니다. 이 섹션의 목적은 여러분이 컴파일러의 무시로
667
+ 인해 여러분의 코드가 망가지는 걸 막을 수 있도록 돕는겁니다.
668
+
665
669
로드-로드 컨트롤 의존성은 데이터 의존성 배리어만으로는 정확히 동작할 수가
666
670
없어서 읽기 메모리 배리어를 필요로 합니다. 아래의 코드를 봅시다:
667
671
@@ -689,20 +693,21 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
689
693
690
694
q = READ_ONCE(a);
691
695
if (q) {
692
- WRITE_ONCE(b, p );
696
+ WRITE_ONCE(b, 1 );
693
697
}
694
698
695
699
컨트롤 의존성은 보통 다른 타입의 배리어들과 짝을 맞춰 사용됩니다. 그렇다곤
696
- 하나, READ_ONCE() 는 반드시 사용해야 함을 부디 명심하세요! READ_ONCE() 가
697
- 없다면, 컴파일러가 'a' 로부터의 로드를 'a' 로부터의 또다른 로드와, 'b' 로의
698
- 스토어를 'b' 로의 또다른 스토어와 조합해 버려 매우 비직관적인 결과를 초래할 수
699
- 있습니다.
700
+ 하나, READ_ONCE() 도 WRITE_ONCE() 도 선택사항이 아니라 필수사항임을 부디
701
+ 명심하세요! READ_ONCE() 가 없다면, 컴파일러는 'a' 로부터의 로드를 'a' 로부터의
702
+ 또다른 로드와 조합할 수 있습니다. WRITE_ONCE() 가 없다면, 컴파일러는 'b' 로의
703
+ 스토어를 'b' 로의 또라느 스토어들과 조합할 수 있습니다. 두 경우 모두 순서에
704
+ 있어 상당히 비직관적인 결과를 초래할 수 있습니다.
700
705
701
706
이걸로 끝이 아닌게, 컴파일러가 변수 'a' 의 값이 항상 0이 아니라고 증명할 수
702
707
있다면, 앞의 예에서 "if" 문을 없애서 다음과 같이 최적화 할 수도 있습니다:
703
708
704
709
q = a;
705
- b = p ; /* BUG: Compiler and CPU can both reorder!!! */
710
+ b = 1 ; /* BUG: Compiler and CPU can both reorder!!! */
706
711
707
712
그러니 READ_ONCE() 를 반드시 사용하세요.
708
713
@@ -712,11 +717,11 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
712
717
q = READ_ONCE(a);
713
718
if (q) {
714
719
barrier();
715
- WRITE_ONCE(b, p );
720
+ WRITE_ONCE(b, 1 );
716
721
do_something();
717
722
} else {
718
723
barrier();
719
- WRITE_ONCE(b, p );
724
+ WRITE_ONCE(b, 1 );
720
725
do_something_else();
721
726
}
722
727
@@ -725,12 +730,12 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
725
730
726
731
q = READ_ONCE(a);
727
732
barrier();
728
- WRITE_ONCE(b, p ); /* BUG: No ordering vs. load from a!!! */
733
+ WRITE_ONCE(b, 1 ); /* BUG: No ordering vs. load from a!!! */
729
734
if (q) {
730
- /* WRITE_ONCE(b, p ); -- moved up, BUG!!! */
735
+ /* WRITE_ONCE(b, 1 ); -- moved up, BUG!!! */
731
736
do_something();
732
737
} else {
733
- /* WRITE_ONCE(b, p ); -- moved up, BUG!!! */
738
+ /* WRITE_ONCE(b, 1 ); -- moved up, BUG!!! */
734
739
do_something_else();
735
740
}
736
741
@@ -742,10 +747,10 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
742
747
743
748
q = READ_ONCE(a);
744
749
if (q) {
745
- smp_store_release(&b, p );
750
+ smp_store_release(&b, 1 );
746
751
do_something();
747
752
} else {
748
- smp_store_release(&b, p );
753
+ smp_store_release(&b, 1 );
749
754
do_something_else();
750
755
}
751
756
@@ -754,10 +759,10 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
754
759
755
760
q = READ_ONCE(a);
756
761
if (q) {
757
- WRITE_ONCE(b, p );
762
+ WRITE_ONCE(b, 1 );
758
763
do_something();
759
764
} else {
760
- WRITE_ONCE(b, r );
765
+ WRITE_ONCE(b, 2 );
761
766
do_something_else();
762
767
}
763
768
@@ -770,18 +775,18 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
770
775
771
776
q = READ_ONCE(a);
772
777
if (q % MAX) {
773
- WRITE_ONCE(b, p );
778
+ WRITE_ONCE(b, 1 );
774
779
do_something();
775
780
} else {
776
- WRITE_ONCE(b, r );
781
+ WRITE_ONCE(b, 2 );
777
782
do_something_else();
778
783
}
779
784
780
785
만약 MAX 가 1 로 정의된 상수라면, 컴파일러는 (q % MAX) 는 0이란 것을 알아채고,
781
786
위의 코드를 아래와 같이 바꿔버릴 수 있습니다:
782
787
783
788
q = READ_ONCE(a);
784
- WRITE_ONCE(b, p );
789
+ WRITE_ONCE(b, 1 );
785
790
do_something_else();
786
791
787
792
이렇게 되면, CPU 는 변수 'a' 로부터의 로드와 변수 'b' 로의 스토어 사이의 순서를
@@ -793,10 +798,10 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
793
798
q = READ_ONCE(a);
794
799
BUILD_BUG_ON(MAX <= 1); /* Order load from a with store to b. */
795
800
if (q % MAX) {
796
- WRITE_ONCE(b, p );
801
+ WRITE_ONCE(b, 1 );
797
802
do_something();
798
803
} else {
799
- WRITE_ONCE(b, r );
804
+ WRITE_ONCE(b, 2 );
800
805
do_something_else();
801
806
}
802
807
@@ -828,35 +833,33 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
828
833
829
834
q = READ_ONCE(a);
830
835
if (q) {
831
- WRITE_ONCE(b, p );
836
+ WRITE_ONCE(b, 1 );
832
837
} else {
833
- WRITE_ONCE(b, r );
838
+ WRITE_ONCE(b, 2 );
834
839
}
835
- WRITE_ONCE(c, 1); /* BUG: No ordering against the read from "a" . */
840
+ WRITE_ONCE(c, 1); /* BUG: No ordering against the read from 'a' . */
836
841
837
- 컴파일러는 volatile 타입에 대한 액세스를 재배치 할 수 없고 이 조건 하의 "b"
842
+ 컴파일러는 volatile 타입에 대한 액세스를 재배치 할 수 없고 이 조건 하의 'b'
838
843
로의 쓰기를 재배치 할 수 없기 때문에 여기에 순서 규칙이 존재한다고 주장하고
839
844
싶을 겁니다. 불행히도 이 경우에, 컴파일러는 다음의 가상의 pseudo-assembly 언어
840
- 코드처럼 "b" 로의 두개의 쓰기 오퍼레이션을 conditional-move 인스트럭션으로
845
+ 코드처럼 'b' 로의 두개의 쓰기 오퍼레이션을 conditional-move 인스트럭션으로
841
846
번역할 수 있습니다:
842
847
843
848
ld r1,a
844
- ld r2,p
845
- ld r3,r
846
849
cmp r1,$0
847
- cmov,ne r4,r2
848
- cmov,eq r4,r3
850
+ cmov,ne r4,$1
851
+ cmov,eq r4,$2
849
852
st r4,b
850
853
st $1,c
851
854
852
- 완화된 순서 규칙의 CPU 는 "a" 로부터의 로드와 "c" 로의 스토어 사이에 어떤
855
+ 완화된 순서 규칙의 CPU 는 'a' 로부터의 로드와 'c' 로의 스토어 사이에 어떤
853
856
종류의 의존성도 갖지 않을 겁니다. 이 컨트롤 의존성은 두개의 cmov 인스트럭션과
854
857
거기에 의존하는 스토어 에게만 적용될 겁니다. 짧게 말하자면, 컨트롤 의존성은
855
858
주어진 if 문의 then 절과 else 절에게만 (그리고 이 두 절 내에서 호출되는
856
859
함수들에게까지) 적용되지, 이 if 문을 뒤따르는 코드에는 적용되지 않습니다.
857
860
858
861
마지막으로, 컨트롤 의존성은 이행성 (transitivity) 을 제공하지 -않습니다-. 이건
859
- x 와 y 가 둘 다 0 이라는 초기값을 가졌다는 가정 하의 두개의 예제로
862
+ 'x' 와 'y' 가 둘 다 0 이라는 초기값을 가졌다는 가정 하의 두개의 예제로
860
863
보이겠습니다:
861
864
862
865
CPU 0 CPU 1
@@ -924,6 +927,9 @@ http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf 와
924
927
(*) 컨트롤 의존성은 이행성을 제공하지 -않습니다-. 이행성이 필요하다면,
925
928
smp_mb() 를 사용하세요.
926
929
930
+ (*) 컴파일러는 컨트롤 의존성을 이해하고 있지 않습니다. 따라서 컴파일러가
931
+ 여러분의 코드를 망가뜨리지 않도록 하는건 여러분이 해야 하는 일입니다.
932
+
927
933
928
934
SMP 배리어 짝맞추기
929
935
--------------------
0 commit comments