Nếu bạn thực sự thực sự cần phải có khả năng để làm điều này vì một lý do, bạn nên truy cập vào một biến thread-địa phương từ ngôn ngữ lắp ráp bởi gia tải địa chỉ của nó trong C, như thế này:
__thread unsigned val;
void incval(void)
{
unsigned *vp = &val;
asm ("incl\t%0" : "+m" (*vp));
}
Điều này là do trình tự mã yêu cầu để truy cập biến thread-local khác với mọi hệ điều hành và kết hợp CPU được GCC hỗ trợ, và cũng thay đổi nếu bạn biên dịch cho một thư viện được chia sẻ chứ không phải là thực thi (tức là với -fPIC
). Cấu trúc trên cho phép trình biên dịch phát ra chuỗi mã đúng cho bạn. Trong trường hợp có thể truy cập biến thread-local mà không có bất kỳ hướng dẫn bổ sung nào, việc tạo địa chỉ sẽ được xếp vào hoạt động lắp ráp. Bằng cách minh họa, đây là cách gcc 4.7 cho x86/Linux biên dịch nêu trên trong nhiều chế độ khác nhau có thể (tôi đã tước ra một loạt các chỉ thị lắp ráp trong mọi trường hợp, cho rõ ràng) ...
# -S -O2 -m32 -fomit-frame-pointer
incval:
incl %gs:[email protected]
ret
# -S -O2 -m64
incval:
incl %fs:[email protected]
ret
# -S -O2 -m32 -fomit-frame-pointer -fpic
incval:
pushl %ebx
call __x86.get_pc_thunk.bx
addl $_GLOBAL_OFFSET_TABLE_, %ebx
leal [email protected](,%ebx,1), %eax
call [email protected]
incl (%eax)
popl %ebx
ret
# -S -O2 -m64 -fpic
incval:
.byte 0x66
leaq [email protected](%rip), %rdi
.value 0x6666
rex64
call [email protected]
incl (%rax)
ret
Do nhận ra rằng tất cả bốn ví dụ sẽ khác nếu tôi đã biên dịch cho x86/OSX và khác biệt một lần nữa đối với x86/Windows.
1) Tại sao bạn không thể viết ''val + = 1;'' thay thế? 2) Viết nó, biên dịch nó bằng '-O2 -S', và kiểm tra đầu ra của assembly; bạn sẽ khám phá ra rằng bạn đã nhầm lẫn về cách truy cập các biến '__thread'. – zwol
@Zack bạn có thể viết câu trả lời về điều đó không? – 0x90
val ++ dịch để movl $ 0x1,% gs: 0xfffffffc, nhưng khi tôi làm asm ("movl $ 1,% gs: val") theo cách thủ công, nó chuyển thành movl $ 0x1,% gs: 0x8049f14. Cách lấy địa chỉ 0xfffffffc trong chương trình của tôi. – Yogi