__attribute __ ((constructor)) ทำงานอย่างไร


348

ดูเหมือนจะค่อนข้างชัดเจนว่าควรตั้งค่าสิ่งต่าง ๆ

  1. มันจะทำงานเมื่อใด
  2. เหตุใดจึงมีสองวงเล็บ
  3. คือ__attribute__ฟังก์ชั่น? แมโครหรือไม่? ไวยากรณ์?
  4. ใช้งานได้ใน C หรือไม่ c ++?
  5. ฟังก์ชั่นนี้ใช้งานได้กับต้องเป็นแบบคงที่หรือไม่?
  6. __attribute__((destructor))ทำงานเมื่อไหร่

ตัวอย่างใน Objective-C :

__attribute__((constructor))
static void initialize_navigationBarImages() {
  navigationBarImages = [[NSMutableDictionary alloc] init];
}

__attribute__((destructor))
static void destroy_navigationBarImages() {
  [navigationBarImages release];
}

คำตอบ:


273
  1. มันทำงานเมื่อโหลดไลบรารีที่ใช้ร่วมกันโดยทั่วไปในระหว่างการเริ่มต้นโปรแกรม
  2. นั่นคือลักษณะของ GCC ทั้งหมด สันนิษฐานว่าจะแยกพวกเขาออกจากการเรียกใช้ฟังก์ชัน
  3. ไวยากรณ์เฉพาะของ GCC
  4. ใช่ทำงานได้ใน C และ C ++
  5. ไม่ฟังก์ชั่นไม่จำเป็นต้องเป็นแบบคงที่
  6. destructor รันเมื่อไลบรารีที่แบ่งใช้ถูกยกเลิกการโหลดโดยทั่วไปที่โปรแกรมออก

ดังนั้นวิธีการทำงานของตัวสร้างและ destructors คือไฟล์อ็อบเจ็กต์ที่แบ่งใช้มีส่วนพิเศษ (.ctors and .dtors บน ELF) ซึ่งมีการอ้างอิงถึงฟังก์ชั่นที่ถูกทำเครื่องหมายด้วยแอตทริบิวต์ constructor และ destructor ตามลำดับ เมื่อโหลดไลบรารี / ยกเลิกการโหลดโปรแกรมโหลดเดอร์แบบไดนามิก (ld.so หรือ somesuch) จะตรวจสอบว่ามีส่วนดังกล่าวอยู่หรือไม่และเรียกใช้ฟังก์ชันที่อ้างถึงในนั้นหรือไม่

ลองคิดดูสิอาจมีเวทมนตร์ที่คล้ายกันในตัวเชื่อมโยงแบบคงที่ปกติเพื่อให้รหัสเดียวกันนี้ทำงานเมื่อเริ่มต้น / ปิดเครื่องโดยไม่คำนึงว่าผู้ใช้เลือกการเชื่อมโยงแบบคงที่หรือแบบไดนามิก


49
เครื่องหมายวงเล็บคู่ทำให้ง่ายต่อการ "แมโครออก" ( #define __attribute__(x)) หากคุณมีคุณสมบัติหลายอย่างเช่น__attribute__((noreturn, weak))มันยากที่จะ "แมโครออก" หากมีวงเล็บชุดเดียว
Chris Jester-Young

7
.init/.finiมันไม่ได้ทำด้วย (คุณสามารถมีหลาย constructors และ destructors ในหน่วยการแปลเดียวไม่ต้องกังวลกับหลาย ๆ ตัวในไลบรารีเดียว - มันจะทำงานอย่างไร?) แทนบนแพลตฟอร์มที่ใช้รูปแบบไบนารีของ ELF (Linux เป็นต้น) ตัวอ้างอิงและ destructors จะถูกอ้างอิง ใน.ctorsและ.dtorsส่วนต่าง ๆ ของส่วนหัว True ในวันเก่าฟังก์ชั่นที่มีชื่อinitและfiniจะทำงานบนโหลดไลบรารีแบบไดนามิกและยกเลิกการโหลดถ้ามีอยู่ แต่ที่เลิกใช้แล้วในขณะนี้แทนที่ด้วยกลไกที่ดีกว่านี้
ephemient

7
@jcayzac ไม่เพราะมาโคร variadic เป็นส่วนขยาย gcc และเหตุผลหลักในการกำหนดแมโคร__attribute__คือถ้าคุณไม่ได้ใช้ gcc เนื่องจากเป็นส่วนขยาย gcc เช่นกัน
Chris Jester-Young

9
@ ChrisJester-Young macros variadic เป็นคุณสมบัติ C99 มาตรฐานไม่ใช่ส่วนขยาย GNU
jcayzac

4
"การใช้กาลปัจจุบันของคุณ (" ทำ "แทน" ทำ "- parens คู่ยังคงทำให้มันง่ายต่อการออกมาคุณเห่าต้นไม้ pedantic ผิด
Jim Balter

64

.init/ .finiไม่เลิกใช้ มันยังคงเป็นส่วนหนึ่งของมาตรฐานเอลฟ์และฉันกล้าพูดว่ามันจะเป็นตลอดไป โค้ดใน.init/ .finiถูกรันโดยโหลดเดอร์ / รันไทม์ลิงเกอร์เมื่อโค้ดถูกโหลด / ไม่โหลด เช่นในแต่ละโหลดเอลฟ์ (ตัวอย่างเช่นไลบรารีที่แชร์) .initจะถูกเรียกใช้ ยังคงเป็นไปได้ที่จะใช้กลไกนั้นเพื่อให้บรรลุในสิ่งเดียวกันกับ __attribute__((constructor))/((destructor))ก็ยังคงเป็นไปได้ที่จะใช้กลไกที่ว่าเพื่อให้บรรลุเกี่ยวกับสิ่งเดียวกันเช่นเดียวกับมันเป็นโรงเรียนเก่า แต่ก็มีประโยชน์บางอย่าง

.ctors.dtorsกลไก/ ตัวอย่างเช่นต้องการการสนับสนุนโดย system-rtl / loader / linker-script สิ่งนี้ยังห่างไกลจากความแน่นอนที่จะมีอยู่ในทุกระบบเช่นระบบฝังตัวที่ลึกซึ่งโค้ดทำงานบนโลหะเปลือย เช่นแม้ว่า__attribute__((constructor))/((destructor))GCC จะได้รับการสนับสนุน แต่ก็ไม่แน่ใจว่ามันจะทำงานได้หรือไม่ขึ้นอยู่กับตัวเชื่อมโยงเพื่อจัดระเบียบและโหลดเดอร์ (หรือในบางกรณีรหัสบูต) เพื่อเรียกใช้ หากต้องการใช้.init/ .finiแทนวิธีที่ง่ายที่สุดคือการใช้แฟล็กลิงเกอร์: -init & -fini (เช่นจากบรรทัดคำสั่ง GCC ไวยากรณ์จะเป็น-Wl -init my_init -fini my_fini )

ในระบบที่รองรับทั้งสองวิธีประโยชน์ที่เป็นไปได้ประการหนึ่งคือรหัส.initนั้นทำงานก่อนหน้า.ctorsและรหัส.finiหลังจากนั้น.dtorsนั้น ถ้าคำสั่งซื้อนั้นเกี่ยวข้องอย่างน้อยหนึ่งน้ำมันดิบ แต่เป็นวิธีที่ง่ายในการแยกความแตกต่างระหว่างฟังก์ชั่นเริ่มต้น / ออก

ข้อเสียเปรียบที่สำคัญคือคุณไม่สามารถมีได้มากกว่า_initหนึ่ง_finiฟังก์ชันในแต่ละโมดูลที่สามารถโหลดได้ง่ายและอาจจะต้องมีการแยกส่วนของรหัส.soมากกว่าแรงจูงใจ อีกวิธีหนึ่งคือเมื่อใช้วิธีการเชื่อมโยงที่อธิบายไว้ข้างต้นหนึ่งแทนที่ _init เดิมและ_finiฟังก์ชั่นเริ่มต้น (จัดทำโดยcrti.o) นี่คือที่ทุกประเภทของการเริ่มต้นมักจะเกิดขึ้น (บน Linux นี่คือที่การกำหนดตัวแปรทั่วโลกจะเริ่มต้นได้) วิธีที่อธิบายไว้ที่นี่

สังเกตุในลิงค์ด้านบนว่า_init()ไม่จำเป็นต้องมีการเรียงซ้อนกันไปตามต้นฉบับเนื่องจากยังคงใช้งานได้ callในแบบอินไลน์การชุมนุม แต่เป็น 86-ช่วยในการจำและเรียกฟังก์ชั่นจากการชุมนุมจะมีลักษณะที่แตกต่างอย่างสิ้นเชิงสำหรับสถาปัตยกรรมอื่น ๆ อีกมากมาย (เช่น ARM ตัวอย่าง) รหัส Ie ไม่โปร่งใส

.init/ .finiและ.ctors/ .detorsกลไกมีความคล้ายคลึงกัน แต่ไม่มาก รหัสใน.init / .finiรัน "ตามสภาพ" นั่นคือคุณสามารถมีฟังก์ชั่นได้หลายอย่างใน.init/ .finiแต่มันเป็นเรื่องยาก AFAIK ที่จะทำให้พวกเขาอยู่ที่นั่นอย่างโปร่งใส syntactically บริสุทธิ์ C โดยไม่ทำลายรหัสใน.soไฟล์เล็ก ๆมากมาย

.ctors/.dtorsมีการจัดที่แตกต่างกว่า/.init /.fini.ctors.dtors section เป็นเพียงตารางที่มีตัวชี้ไปยังฟังก์ชั่นและ "ตัวเรียก" เป็นลูปที่ระบบจัดเตรียมไว้ให้ซึ่งจะเรียกแต่ละฟังก์ชั่นทางอ้อม เช่น loop-caller นั้นเป็นสถาปัตยกรรมที่เฉพาะเจาะจง แต่เนื่องจากเป็นส่วนหนึ่งของระบบ (หากมีอยู่นั่นคือทั้งหมด) มันไม่สำคัญ

ตัวอย่างต่อไปนี้เพิ่มพอยน์เตอร์ฟังก์ชั่นใหม่ให้กับ.ctorsฟังก์ชั่นอาเรย์__attribute__((constructor))ทำ (เมธอดสามารถอยู่ร่วมกัน__attribute__((constructor)))ได้

#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
   printf("Hello\n");
}
void (*funcptr)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;

หนึ่งยังสามารถเพิ่มตัวชี้ฟังก์ชั่นไปยังส่วนที่คิดค้นขึ้นเองอย่างสมบูรณ์ สคริปต์ลิงเกอร์ที่ถูกแก้ไขและฟังก์ชั่นเพิ่มเติมที่เลียนแบบตัวโหลด.ctors/ .dtorsวนซ้ำนั้นจำเป็นในกรณีดังกล่าว แต่ด้วยสิ่งนี้เราสามารถควบคุมลำดับการดำเนินการได้ดีขึ้นเพิ่มการโต้แย้งและการจัดการโค้ดส่งคืน (ในโครงการ C ++ ตัวอย่างเช่นมันจะมีประโยชน์หากต้องการบางสิ่งที่ทำงานก่อนหรือหลังตัวสร้างส่วนกลาง)

ฉันต้องการ __attribute__((constructor))/((destructor))ที่เป็นไปได้มันเป็นทางออกที่ง่ายและสง่างามแม้ว่ามันจะรู้สึกเหมือนโกง สำหรับตัวแปลงสัญญาณโลหะเปลือยอย่างตัวฉันนี่ไม่ใช่ตัวเลือกเสมอไป

บางอ้างอิงที่ดีในหนังสือเล่มLinkers และรถตัก


ตัวโหลดจะเรียกใช้ฟังก์ชันเหล่านั้นได้อย่างไร? ฟังก์ชั่นเหล่านั้นสามารถใช้ globals และฟังก์ชั่นอื่น ๆ ในพื้นที่ที่อยู่ของกระบวนการ แต่โหลดเดอร์เป็นกระบวนการที่มีพื้นที่ที่อยู่ของตัวเองใช่มั้ย
user2162550

@ user2162550 ไม่ ld-linux.so.2 (ล่าม "ปกติโหลดเดอร์สำหรับไลบรารีแบบไดนามิกที่ทำงานบน executables ที่เชื่อมโยงแบบไดนามิกทั้งหมด) ทำงานในพื้นที่ที่อยู่ของตัวปฏิบัติการเอง โดยทั่วไปแล้วตัวโหลดไลบรารีแบบไดนามิกนั้นเป็นสิ่งเฉพาะสำหรับผู้ใช้ซึ่งทำงานในบริบทของเธรดที่พยายามเข้าถึงทรัพยากรไลบรารี
Paul Stelian

เมื่อฉันเรียก execv () จากรหัสที่มี__attribute__((constructor))/((destructor))destructor ไม่ทำงาน ฉันลองบางสิ่งเช่นเพิ่มรายการใน. dtor ตามที่แสดงด้านบน แต่ไม่สำเร็จ ปัญหาง่ายต่อการทำซ้ำโดยการเรียกใช้รหัสด้วย numactl ตัวอย่างเช่นสมมติว่า test_code มี destructor (เพิ่ม printf ให้กับฟังก์ชัน constructor และ desctructor เพื่อดีบักปัญหา) LD_PRELOAD=./test_code numactl -N 0 sleep 1จากนั้นเรียก คุณจะเห็นว่าตัวสร้างถูกเรียกสองครั้ง แต่ตัวทำลายเพียงครั้งเดียว
B Abali

39

หน้านี้ให้ความเข้าใจที่ดีเกี่ยวกับconstructorและdestructorดำเนินงานและส่วนแอตทริบิวต์ภายในภายในเอลฟ์ที่ช่วยให้พวกเขาในการทำงาน หลังจากสรุปข้อมูลที่ให้ไว้ที่นี่ฉันรวบรวมข้อมูลเพิ่มเติมเล็กน้อยและ (ยืมตัวอย่างหมวดจาก Michael Ambrus ด้านบน) สร้างตัวอย่างเพื่อแสดงแนวคิดและช่วยการเรียนรู้ของฉัน ผลลัพธ์เหล่านั้นมีให้ด้านล่างพร้อมกับตัวอย่างแหล่งที่มา

ตามที่อธิบายไว้ในชุดข้อความนี้แอตทริบิวต์constructorและdestructorสร้างรายการใน.ctorsและ.dtorsส่วนของไฟล์วัตถุ คุณสามารถทำการอ้างอิงไปยังฟังก์ชั่นในส่วนใดส่วนหนึ่งในสามวิธี (1) ใช้ทั้งsectionแอตทริบิวต์ (2) constructorและdestructorคุณสมบัติหรือ (3) ด้วยการโทรแบบอินไลน์แอสเซมบลี (ตามที่อ้างถึงลิงก์ในคำตอบของ Ambrus)

การใช้constructorและdestructorคุณลักษณะช่วยให้คุณสามารถกำหนดลำดับความสำคัญให้กับตัวสร้าง / destructor เพื่อควบคุมลำดับของการดำเนินการก่อนที่จะmain()ถูกเรียกหรือหลังจากที่มันกลับมา ยิ่งให้ค่าลำดับความสำคัญต่ำลงเท่าใดลำดับความสำคัญในการเรียกใช้งานก็จะยิ่งสูงขึ้น (ลำดับความสำคัญที่ต่ำกว่าจะเรียกใช้ก่อนที่จะมีลำดับความสำคัญสูงกว่าก่อนหน้า main () - และลำดับความสำคัญสูงกว่า ค่าระดับความสำคัญที่คุณให้จะต้องมากกว่า100ค่าที่คอมไพเลอร์สงวนค่าระดับความสำคัญระหว่าง 0-100 สำหรับการนำไปใช้ constructorหรือdestructorระบุไว้กับรันลำดับความสำคัญก่อนconstructorหรือdestructorระบุไว้โดยไม่ต้องจัดลำดับความสำคัญ

ด้วยแอททริบิวต์ 'section' หรือ inline-assembly คุณยังสามารถทำการอ้างอิงฟังก์ชั่นในส่วน.initและ.finiรหัสเอลฟ์ที่จะทำงานก่อนคอนสตรัคเตอร์ใด ๆ และหลังจากตัวทำลายระบบตามลำดับ ฟังก์ชั่นใด ๆ ที่เรียกโดยการอ้างอิงฟังก์ชั่นที่วางไว้ใน.initส่วนจะดำเนินการก่อนการอ้างอิงฟังก์ชั่นของตัวเอง (ตามปกติ)

ฉันพยายามอธิบายแต่ละตัวอย่างในตัวอย่างด้านล่าง:

#include <stdio.h>
#include <stdlib.h>

/*  test function utilizing attribute 'section' ".ctors"/".dtors"
    to create constuctors/destructors without assigned priority.
    (provided by Michael Ambrus in earlier answer)
*/

#define SECTION( S ) __attribute__ ((section ( S )))

void test (void) {
printf("\n\ttest() utilizing -- (.section .ctors/.dtors) w/o priority\n");
}

void (*funcptr1)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;

/*  functions constructX, destructX use attributes 'constructor' and
    'destructor' to create prioritized entries in the .ctors, .dtors
    ELF sections, respectively.

    NOTE: priorities 0-100 are reserved
*/
void construct1 () __attribute__ ((constructor (101)));
void construct2 () __attribute__ ((constructor (102)));
void destruct1 () __attribute__ ((destructor (101)));
void destruct2 () __attribute__ ((destructor (102)));

/*  init_some_function() - called by elf_init()
*/
int init_some_function () {
    printf ("\n  init_some_function() called by elf_init()\n");
    return 1;
}

/*  elf_init uses inline-assembly to place itself in the ELF .init section.
*/
int elf_init (void)
{
    __asm__ (".section .init \n call elf_init \n .section .text\n");

    if(!init_some_function ())
    {
        exit (1);
    }

    printf ("\n    elf_init() -- (.section .init)\n");

    return 1;
}

/*
    function definitions for constructX and destructX
*/
void construct1 () {
    printf ("\n      construct1() constructor -- (.section .ctors) priority 101\n");
}

void construct2 () {
    printf ("\n      construct2() constructor -- (.section .ctors) priority 102\n");
}

void destruct1 () {
    printf ("\n      destruct1() destructor -- (.section .dtors) priority 101\n\n");
}

void destruct2 () {
    printf ("\n      destruct2() destructor -- (.section .dtors) priority 102\n");
}

/* main makes no function call to any of the functions declared above
*/
int
main (int argc, char *argv[]) {

    printf ("\n\t  [ main body of program ]\n");

    return 0;
}

เอาท์พุท:

init_some_function() called by elf_init()

    elf_init() -- (.section .init)

    construct1() constructor -- (.section .ctors) priority 101

    construct2() constructor -- (.section .ctors) priority 102

        test() utilizing -- (.section .ctors/.dtors) w/o priority

        test() utilizing -- (.section .ctors/.dtors) w/o priority

        [ main body of program ]

        test() utilizing -- (.section .ctors/.dtors) w/o priority

    destruct2() destructor -- (.section .dtors) priority 102

    destruct1() destructor -- (.section .dtors) priority 101

ตัวอย่างช่วยประสานพฤติกรรมคอนสตรัคเตอร์ / destructor หวังว่ามันจะเป็นประโยชน์กับผู้อื่นเช่นกัน


คุณพบว่า "ค่าลำดับความสำคัญที่คุณให้ต้องมากกว่า 100" ไม่มีข้อมูลดังกล่าวในเอกสารประกอบคุณลักษณะของฟังก์ชัน GCC
Justin

4
IIRC มีคู่ของการอ้างอิงเป็นPATCH: การสนับสนุนการโต้แย้งความสำคัญสำหรับสร้างอาร์กิวเมนต์ / destructor ( MAX_RESERVED_INIT_PRIORITY) และว่าพวกเขาเช่นเดียวกับC ++ ( init_priority) 7.7 c ++ - ตัวแปรเฉพาะคุณสมบัติฟังก์ชั่นและประเภท จากนั้นฉันก็ลองด้วย99: warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));.
David C. Rankin

1
อา. ฉันพยายามจัดลำดับความสำคัญ <100 ห้องพร้อมด้วยเสียงดังกราวและดูเหมือนว่ามันจะทำงาน แต่กรณีทดสอบอย่างง่ายของฉัน (เดียวรวบรวมหน่วย) ได้ง่ายเกินไป
Justin

1
ลำดับความสำคัญของตัวแปรโกลบอลแบบสแตติกคืออะไร
dashesy

2
ผลกระทบและการมองเห็นของโกลบอลสแตติกจะขึ้นอยู่กับว่าโปรแกรมของคุณมีโครงสร้างอย่างไร (เช่นไฟล์เดียว, หลายไฟล์ ( หน่วยการแปล )) และการประกาศแบบโกลบอลดูที่: สแตติก (คำหลัก)โดยเฉพาะคำอธิบายตัวแปรโกลบอลแบบคงที่
David C. Rankin

7

นี่คือตัวอย่าง "คอนกรีต" (และอาจเป็นประโยชน์ ) ของวิธีการทำไมและเมื่อใดที่จะใช้ประโยชน์เหล่านี้ แต่ยังสร้างขึ้นไม่น่าดู ...

Xcode ใช้ "ทั่วโลก" "เริ่มต้นผู้ใช้" การตัดสินใจที่XCTestObserverระดับspews หัวใจมันออกไปชั่วโมงที่ผ่านมาคอนโซล

ในตัวอย่างนี้ ... เมื่อฉันโหลดไลบรารี psuedo นี้โดยปริยายเราจะเรียกมันว่า ... libdemure.aผ่านธงในเป้าหมายทดสอบของฉันá la ..

OTHER_LDFLAGS = -ldemure

ฉันต้องการ ..

  1. ที่ load (เช่นเมื่อXCTestโหลดชุดทดสอบ) ให้แทนที่XCTestคลาส"default" "ผู้สังเกตการณ์" ... (ผ่านconstructorฟังก์ชั่น) PS: เท่าที่ฉันสามารถบอกได้ .. สิ่งที่ทำที่นี่สามารถทำได้โดยมีผลเทียบเท่าภายในของฉัน คลาส+ (void) load { ... }วิธีการ

  2. รันการทดสอบของฉัน .... ในกรณีนี้โดยไม่มีการใช้คำฟุ่มเฟือยน้อยลงในบันทึก (การนำไปใช้ตามคำขอ)

  3. ส่งคืนXCTestObserverคลาส"ส่วนกลาง" ไปยังสถานะดั้งเดิม .. เพื่อไม่ให้เหม็นในการXCTestวิ่งอื่น ๆที่ไม่ได้รับใน bandwagon (aka. เชื่อมโยงกับlibdemure.a) ฉันเดาว่านี่เป็นเรื่องทางประวัติศาสตร์แล้วdeallocแต่ฉันไม่ได้ยุ่งกับแม่มดตัวเก่า

ดังนั้น...

#define USER_DEFS NSUserDefaults.standardUserDefaults

@interface      DemureTestObserver : XCTestObserver @end
@implementation DemureTestObserver

__attribute__((constructor)) static void hijack_observer() {

/*! here I totally hijack the default logging, but you CAN
    use multiple observers, just CSV them, 
    i.e. "@"DemureTestObserverm,XCTestLog"
*/
  [USER_DEFS setObject:@"DemureTestObserver" 
                forKey:@"XCTestObserverClass"];
  [USER_DEFS synchronize];
}

__attribute__((destructor)) static void reset_observer()  {

  // Clean up, and it's as if we had never been here.
  [USER_DEFS setObject:@"XCTestLog" 
                forKey:@"XCTestObserverClass"];
  [USER_DEFS synchronize];
}

...
@end

โดยไม่ต้องธงลิงเกอร์ ... (แฟชั่นตำรวจจับกลุ่มคูเปอร์ติโนเรียกร้องการลงโทษแต่แอปเปิ้ล prevails ค่าเริ่มต้นเป็นที่ต้องการที่นี่ )

ป้อนคำอธิบายรูปภาพที่นี่

ด้วย-ldemure.aธงลิงเกอร์ ... (ผลลัพธ์ที่เข้าใจได้อ้าปากค้าง ... "ขอบคุณconstructor/ destructor" ... ไชโยฝูงชน ) ป้อนคำอธิบายรูปภาพที่นี่


1

นี่เป็นอีกตัวอย่างที่ชัดเจนมันมีไว้สำหรับห้องสมุดสาธารณะ หน้าที่หลักของห้องสมุดสาธารณะคือการสื่อสารกับเครื่องอ่านสมาร์ทการ์ด แต่ยังสามารถรับ 'ข้อมูลการกำหนดค่า' ที่รันไทม์บน udp udp ถูกจัดการโดยเธรดที่ต้องเริ่มต้นในเวลาเริ่มต้น

__attribute__((constructor))  static void startUdpReceiveThread (void) {
    pthread_create( &tid_udpthread, NULL, __feigh_udp_receive_loop, NULL );
    return;

  }

ห้องสมุดถูกเขียนในค


1
ตัวเลือกแปลก ๆ ถ้าไลบรารีถูกเขียนใน C ++ เนื่องจากตัวสร้างตัวแปรโกลบอลสามัญเป็นวิธีที่ใช้สำนวนในการรันโค้ด pre-main ใน C ++
Nicholas Wilson

@NicholasWilson จริง ๆ แล้วไลบรารีเขียนด้วย c ไม่ทราบว่าฉันพิมพ์ c ++ ได้อย่างไรแทนที่จะเป็น c
drlolly
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.