मल्टीकोर असेंबली लैंग्वेज कैसी दिखती है?


243

एक बार, x86 असेंबलर लिखने के लिए, उदाहरण के लिए, आपके पास "ईडीएक्स रजिस्टर को मान 5 के साथ लोड करना", "ईडीएक्स बढ़ाना" रजिस्टर, आदि निर्देश होंगे।

आधुनिक सीपीयू के साथ, जिसमें 4 कोर (या इससे भी अधिक) हैं, मशीन कोड स्तर पर ऐसा लगता है जैसे 4 अलग सीपीयू हैं (यानी सिर्फ 4 अलग "ईडीएक्स" रजिस्टर हैं)? यदि ऐसा है, तो जब आप कहते हैं कि "EDX रजिस्टर बढ़ाएँ", क्या निर्धारित करता है कि CPU का EDX रजिस्टर बढ़ा हुआ है? क्या अब x86 असेंबलर में "सीपीयू संदर्भ" या "थ्रेड" अवधारणा है?

कोर के बीच संचार / सिंक्रनाइज़ेशन कैसे काम करता है?

यदि आप एक ऑपरेटिंग सिस्टम लिख रहे थे, तो विभिन्न तंत्रों पर निष्पादन को निर्धारित करने की अनुमति देने के लिए हार्डवेयर के माध्यम से क्या तंत्र सामने आया है? क्या यह कुछ विशेष निजीकृत निर्देश है?

यदि आप एक मल्टीकोर सीपीयू के लिए एक अनुकूलन कंपाइलर / बायटेकोड वीएम लिख रहे थे, तो आपको विशेष रूप से इसके बारे में क्या कहना होगा, x86 इसे कोड बनाने के लिए जो सभी कोर में कुशलता से चलता है?

मल्टी-कोर कार्यक्षमता का समर्थन करने के लिए x86 मशीन कोड में क्या बदलाव किए गए हैं?


2
यहाँ एक समान (हालांकि समान नहीं) प्रश्न है: stackoverflow.com/questions/714905/…
नाथन फ़ेलमैन

जवाबों:


153

यह सवाल का सीधा जवाब नहीं है, लेकिन यह एक सवाल का जवाब है जो टिप्पणियों में दिखाई देता है। अनिवार्य रूप से, सवाल यह है कि हार्डवेयर बहु-थ्रेडेड ऑपरेशन को क्या समर्थन देता है।

निकोलस फ्लायंट के पास यह अधिकार था , कम से कम x86 के बारे में। बहु थ्रेडेड वातावरण (हाइपर-थ्रेडिंग, मल्टी-कोर या मल्टी-प्रोसेसर) में, बूटस्ट्रैप थ्रेड (आमतौर पर प्रोसेसर 0 में कोर 0 में धागा 0) एड्रेस से कोड लाना शुरू कर देता है 0xfffffff0। अन्य सभी धागे एक विशेष नींद की स्थिति में शुरू होते हैं जिसे वेट-फॉर-एसआईपीआई कहा जाता है । इसके आरंभ के भाग के रूप में, प्राथमिक धागा प्रत्येक थ्रेड में एक विशेष SIPI (स्टार्टअप IPI) नामक APIC पर एक विशेष अंतर-प्रोसेसर-इंटरप्ट (IPI) भेजता है जो WFS में होता है। SIPI में वह पता होता है जिसमें से उस थ्रेड को कोड लाना शुरू करना चाहिए।

यह तंत्र प्रत्येक थ्रेड को एक अलग पते से कोड निष्पादित करने की अनुमति देता है। सभी की जरूरत है कि प्रत्येक थ्रेड के लिए सॉफ़्टवेयर समर्थन अपने स्वयं के तालिकाओं और संदेश कतारों को स्थापित करने के लिए है। ओएस उन लोगों का उपयोग करता है जो वास्तविक बहु-थ्रेडिंग शेड्यूलिंग करते हैं।

जहां तक ​​वास्तविक असेंबली का सवाल है, जैसा कि निकोलस ने लिखा है, एक थ्रेडेड या मल्टी थ्रेडेड एप्लिकेशन के लिए असेंबली के बीच कोई अंतर नहीं है। प्रत्येक तार्किक धागे का अपना रजिस्टर सेट होता है, इसलिए लेखन:

mov edx, 0

केवल वर्तमान में चल रहे थ्रेड केEDX लिए अद्यतन करेगा । किसी एकल असेंबली इंस्ट्रक्शन का उपयोग करके किसी अन्य प्रोसेसर पर संशोधित करने का कोई तरीका नहीं है । आपको ओएस को चलाने के लिए किसी अन्य थ्रेड को कोड पूछने के लिए किसी प्रकार के सिस्टम कॉल की आवश्यकता होगी जो अपने आप अपडेट हो जाएगा ।EDXEDX


2
निकोलस के उत्तर में अंतर को भरने के लिए धन्यवाद। अब आपके पास स्वीकार किए गए उत्तर के रूप में चिह्नित किया गया है .... जो विशिष्ट विवरण मुझे दिलचस्पी थी ... में देता है, हालांकि यह बेहतर होगा अगर एक भी उत्तर था जिसमें आपकी जानकारी और निकोलस का संयोजन था।
पॉल हॉलिंग्सवर्थ

3
यह इस सवाल का जवाब नहीं देता कि धागे कहां से आते हैं। कोर्स और प्रोसेसर एक हार्डवेयर चीज है, लेकिन किसी तरह सॉफ्टवेयर में थ्रेड्स बनाने होंगे। SIPI को भेजने के लिए प्राथमिक थ्रेड कैसे जानता है? या SIPI ही एक नया सूत्र बनाता है?
अमीर रईस

7
@richremer: ऐसा लगता है कि आप एचडब्ल्यू थ्रेड और एसडब्ल्यू थ्रेड को भ्रमित कर रहे हैं। HW सूत्र हमेशा मौजूद होता है। कभी-कभी सो जाता है। SIPI स्वयं HW सूत्र को जगाता है और SW को चलाने की अनुमति देता है। यह OS और BIOS पर निर्भर करता है कि कौन सा HW थ्रेड चलते हैं, और कौन से प्रोसेस और SW थ्रेड्स प्रत्येक HW थ्रेड पर चलते हैं।
नाथन फेलमैन

2
बहुत सारी अच्छी और संक्षिप्त जानकारी यहाँ दी गई है, लेकिन यह एक बड़ा विषय है - इसलिए प्रश्न बहुत अचंभित कर सकते हैं। जंगली में पूरी तरह से "नंगे हड्डियां" गुठली के कुछ उदाहरण हैं जो यूएसबी ड्राइव या "फ्लॉपी" डिस्क से बूट होते हैं - यहां कोडांतरण में x86_32 संस्करण को पुराने टीएसएस विवरणकों का उपयोग करके लिखा गया है जो वास्तव में बहु-थ्रेडेड सी कोड (जीथब) चला सकते हैं । com / duanev / oz-x86-32-asm-003 ) लेकिन कोई मानक पुस्तकालय समर्थन नहीं है। आपके द्वारा पूछे गए से थोड़ा अधिक, लेकिन यह उन सुस्त सवालों में से कुछ का जवाब दे सकता है।
डुआनेव

87

इंटेल x86 न्यूनतम रननीय नंगेमेट उदाहरण

सभी आवश्यक बॉयलरप्लेट के साथ चलने योग्य नंगे धातु का उदाहरण । सभी प्रमुख भाग नीचे दिए गए हैं।

Ubuntu 15.10 QEMU 2.3.0 और लेनोवो थिंकपैड T400 वास्तविक हार्डवेयर अतिथि पर परीक्षण किया गया ।

इंटेल मैनुअल माप 3 सिस्टम प्रोग्रामिंग गाइड - 325384-056US सितंबर 2015 अध्याय 8, 9 और 10 में शामिल हैं SMP।

तालिका 8-1। "ब्रॉडकास्ट INIT-SIPI-SIPI सीक्वेंस एंड चॉइस ऑफ टाइमआउट्स" में एक उदाहरण है जो मूल रूप से सिर्फ काम करता है:

MOV ESI, ICR_LOW    ; Load address of ICR low dword into ESI.
MOV EAX, 000C4500H  ; Load ICR encoding for broadcast INIT IPI
                    ; to all APs into EAX.
MOV [ESI], EAX      ; Broadcast INIT IPI to all APs
; 10-millisecond delay loop.
MOV EAX, 000C46XXH  ; Load ICR encoding for broadcast SIPI IP
                    ; to all APs into EAX, where xx is the vector computed in step 10.
MOV [ESI], EAX      ; Broadcast SIPI IPI to all APs
; 200-microsecond delay loop
MOV [ESI], EAX      ; Broadcast second SIPI IPI to all APs
                    ; Waits for the timer interrupt until the timer expires

उस कोड पर:

  1. अधिकांश ऑपरेटिंग सिस्टम रिंग 3 (उपयोगकर्ता कार्यक्रमों) से उन अधिकांश कार्यों को असंभव बना देंगे।

    तो आपको इसके साथ स्वतंत्र रूप से खेलने के लिए अपना कर्नेल लिखने की आवश्यकता है: एक यूजरलैंड लिनक्स प्रोग्राम काम नहीं करेगा।

  2. सबसे पहले, एक एकल प्रोसेसर चलता है, जिसे बूटस्ट्रैप प्रोसेसर (बीएसपी) कहा जाता है।

    इसे इंटर प्रोसेसर्स इंटरप्ट (आईपीआई) नामक विशेष इंटरप्ट के माध्यम से अन्य लोगों (एप्लीकेशन प्रोसेसर (एपी) कहा जाता है) को जगाना चाहिए ।

    इंटरप्ट कमांड रजिस्टर (ICR) के माध्यम से एडवांस प्रोग्रामेबल इंटरप्टेबल कंट्रोलर (APIC) की प्रोग्रामिंग करके उन रुकावटों को दूर किया जा सकता है।

    ICR का प्रारूप प्रलेखित है: 10.6 "ISSUING INTERPROCESSOR INTERRUPTS"

    जैसे ही हम ICR को लिखते हैं, IPI होता है।

  3. ICR_LOW को 8.4.4 "एमपी इनिशियलाइज़ेशन उदाहरण" के रूप में परिभाषित किया गया है:

    ICR_LOW EQU 0FEE00300H
    

    मैजिक वैल्यू 0FEE00300ICR का मेमोरी एड्रेस है, जैसा कि टेबल 10-1 "स्थानीय एपीआईसी रजिस्टर एड्रेस मैप" में प्रलेखित है।

  4. उदाहरण में सबसे सरल संभव विधि का उपयोग किया जाता है: यह प्रसारण आईपीआई भेजने के लिए आईसीआर सेट करता है जो वर्तमान एक को छोड़कर अन्य सभी प्रोसेसर को दिया जाता है।

    लेकिन यह भी संभव है, और कुछ के द्वारा अनुशंसित किया गया है , विशेष डेटा संरचनाओं के माध्यम से प्रोसेसर के बारे में जानकारी प्राप्त करने के लिए BIOS द्वारा एसीपीआई टेबल या इंटेल की एमपी कॉन्फ़िगरेशन तालिका जैसे सेटअप और केवल उन लोगों को जगाएं जिनकी आपको एक-एक जरूरत है।

  5. XX000C46XXHपहले निर्देश का पता सांकेतिक शब्दों में बदलना है कि प्रोसेसर के रूप में निष्पादित करेगा:

    CS = XX * 0x100
    IP = 0
    

    याद रखें कि CS द्वारा पते को गुणा किया जाता है0x10 , इसलिए पहले निर्देश का वास्तविक मेमोरी पता है:

    XX * 0x1000
    

    इसलिए यदि उदाहरण के लिए XX == 1, प्रोसेसर शुरू होगा 0x1000

    फिर हमें यह सुनिश्चित करना चाहिए कि उस मेमोरी लोकेशन पर चलने के लिए 16-बिट रियल मोड कोड है, जैसे:

    cld
    mov $init_len, %ecx
    mov $init, %esi
    mov 0x1000, %edi
    rep movsb
    
    .code16
    init:
        xor %ax, %ax
        mov %ax, %ds
        /* Do stuff. */
        hlt
    .equ init_len, . - init
    

    लिंकर स्क्रिप्ट का उपयोग करना एक और संभावना है।

  6. देरी छोरों काम करने के लिए एक कष्टप्रद हिस्सा हैं: ऐसी नींदों को ठीक से करने के लिए कोई सुपर सरल तरीका नहीं है।

    संभावित तरीकों में शामिल हैं:

    • PIT (मेरे उदाहरण में प्रयुक्त)
    • HPET
    • ऊपर के साथ एक व्यस्त लूप के समय को कैलिब्रेट करें, और इसके बजाय इसका उपयोग करें

    संबंधित: स्क्रीन पर एक संख्या कैसे प्रदर्शित करें और डॉस x86 विधानसभा के साथ एक सेकंड के लिए सोएं?

  7. मुझे लगता है कि प्रारंभिक प्रोसेसर को इसके लिए संरक्षित मोड में होना चाहिए क्योंकि हम पते पर लिखते हैं 0FEE00300Hजो 16-बिट्स के लिए बहुत अधिक है

  8. प्रोसेसर के बीच संवाद करने के लिए, हम मुख्य प्रक्रिया पर एक स्पिनलॉक का उपयोग कर सकते हैं, और दूसरे कोर से लॉक को संशोधित कर सकते हैं।

    हमें यह सुनिश्चित करना चाहिए कि मेमोरी राइट बैक किया गया है, उदाहरण के लिए wbinvd

प्रोसेसर के बीच साझा स्थिति

8.7.1 "लॉजिकल प्रोसेसर्स का राज्य" कहता है:

निम्नलिखित विशेषताएं इंटेल 64 या IA-32 प्रोसेसर के भीतर तार्किक हाइपर-थ्रेडिंग तकनीक का समर्थन करने वाले तार्किक प्रोसेसर की वास्तु स्थिति का हिस्सा हैं। सुविधाओं को तीन समूहों में विभाजित किया जा सकता है:

  • प्रत्येक तार्किक प्रोसेसर के लिए डुप्लिकेट
  • एक भौतिक प्रोसेसर में तार्किक प्रोसेसर द्वारा साझा किया गया
  • कार्यान्वयन के आधार पर साझा या डुप्लिकेट किया गया

प्रत्येक तार्किक प्रोसेसर के लिए निम्नलिखित विशेषताएं दोहराई गई हैं:

  • सामान्य प्रयोजन रजिस्टर (EAX, EBX, ECX, EDX, ESI, EDI, ESP और EBP)
  • सेगमेंट रजिस्टर (CS, DS, SS, ES, FS और GS)
  • EFLAGS और EIP रजिस्टर। ध्यान दें कि CS और EIP / RIP प्रत्येक लॉजिकल प्रोसेसर के लिए रजिस्टर करता है जो कि थ्रेड के लिए निर्देश स्ट्रीम को तार्किक प्रोसेसर द्वारा निष्पादित करता है।
  • x87 FPU रजिस्टर (ST0 ST7 के माध्यम से, स्थिति शब्द, नियंत्रण शब्द, टैग शब्द, डेटा ऑपरेटर सूचक और अन्य सूचक)
  • MMX रजिस्टर (MM7 MM7 के माध्यम से)
  • एक्सएमएम रजिस्टर (एक्सएमएम 7 के माध्यम से एक्सएमएम 0) और एमएक्ससीएसआर रजिस्टर
  • नियंत्रण रजिस्टर और सिस्टम टेबल पॉइंटर रजिस्टर (GDTR, LDTR, IDTR, कार्य रजिस्टर)
  • डीबग रजिस्टर (DR0, DR1, DR2, DR3, DR6, DR7) और डीबग नियंत्रण MSRs
  • मशीन की जाँच वैश्विक स्थिति (IA32_MCG_STATUS) और मशीन जाँच क्षमता (IA32_MCG_CAP) MSRs
  • थर्मल घड़ी मॉड्यूलेशन और एसीपीआई पावर प्रबंधन MSRs को नियंत्रित करते हैं
  • समय टिकट काउंटर MSRs
  • पृष्ठ विशेषता तालिका (PAT) सहित अन्य MSR रजिस्टरों में से अधिकांश। नीचे अपवाद देखें।
  • स्थानीय APIC रजिस्टर।
  • अतिरिक्त सामान्य प्रयोजन रजिस्टर (R8-R15), एक्सएमएम रजिस्टर (XMM8-XMM15), इंटेल 64 प्रोसेसर पर नियंत्रण रजिस्टर, IA32_EFER।

निम्नलिखित विशेषताएं तार्किक प्रोसेसर द्वारा साझा की जाती हैं:

  • मेमोरी प्रकार श्रेणी रजिस्टर (MTRRs)

क्या निम्नलिखित विशेषताएं साझा या दोहराई गई हैं, क्या कार्यान्वयन-विशिष्ट हैं:

  • IA32_MISC_ENABLE MSR (MSR पता 1A0H)
  • मशीन की जाँच वास्तुकला (MCA) MSRs (IA32_MCG_STATUS और IA32_MCG_CAP MSRs को छोड़कर)
  • प्रदर्शन की निगरानी नियंत्रण और एमएसआर का मुकाबला

कैश शेयरिंग पर चर्चा की जाती है:

इंटेल हाइपरथ्रेड्स में अलग-अलग कोर की तुलना में अधिक कैश और पाइपलाइन साझाकरण है: /superuser/133082/hyper-threading-and-dual-core-whats-the-difference/99585899995858

लिनक्स कर्नेल 4.2

मुख्य प्रारंभिक कार्रवाई पर लगता है arch/x86/kernel/smpboot.c

एआरएम न्यूनतम रननीय नंगेमेट उदाहरण

यहाँ मैं QEMU के लिए एक कम से कम रनवेबल ARMv8 अराजकता 64 उदाहरण प्रदान करता हूँ:

.global mystart
mystart:
    /* Reset spinlock. */
    mov x0, #0
    ldr x1, =spinlock
    str x0, [x1]

    /* Read cpu id into x1.
     * TODO: cores beyond 4th?
     * Mnemonic: Main Processor ID Register
     */
    mrs x1, mpidr_el1
    ands x1, x1, 3
    beq cpu0_only
cpu1_only:
    /* Only CPU 1 reaches this point and sets the spinlock. */
    mov x0, 1
    ldr x1, =spinlock
    str x0, [x1]
    /* Ensure that CPU 0 sees the write right now.
     * Optional, but could save some useless CPU 1 loops.
     */
    dmb sy
    /* Wake up CPU 0 if it is sleeping on wfe.
     * Optional, but could save power on a real system.
     */
    sev
cpu1_sleep_forever:
    /* Hint CPU 1 to enter low power mode.
     * Optional, but could save power on a real system.
     */
    wfe
    b cpu1_sleep_forever
cpu0_only:
    /* Only CPU 0 reaches this point. */

    /* Wake up CPU 1 from initial sleep!
     * See:https://github.com/cirosantilli/linux-kernel-module-cheat#psci
     */
    /* PCSI function identifier: CPU_ON. */
    ldr w0, =0xc4000003
    /* Argument 1: target_cpu */
    mov x1, 1
    /* Argument 2: entry_point_address */
    ldr x2, =cpu1_only
    /* Argument 3: context_id */
    mov x3, 0
    /* Unused hvc args: the Linux kernel zeroes them,
     * but I don't think it is required.
     */
    hvc 0

spinlock_start:
    ldr x0, spinlock
    /* Hint CPU 0 to enter low power mode. */
    wfe
    cbz x0, spinlock_start

    /* Semihost exit. */
    mov x1, 0x26
    movk x1, 2, lsl 16
    str x1, [sp, 0]
    mov x0, 0
    str x0, [sp, 8]
    mov x1, sp
    mov w0, 0x18
    hlt 0xf000

spinlock:
    .skip 8

गिटहब ऊपर

इकट्ठा करें और चलाएं:

aarch64-linux-gnu-gcc \
  -mcpu=cortex-a57 \
  -nostdlib \
  -nostartfiles \
  -Wl,--section-start=.text=0x40000000 \
  -Wl,-N \
  -o aarch64.elf \
  -T link.ld \
  aarch64.S \
;
qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a57 \
  -d in_asm \
  -kernel aarch64.elf \
  -nographic \
  -semihosting \
  -smp 2 \
;

इस उदाहरण में, हम सीपीयू 0 को एक स्पिनलॉक लूप में रखते हैं, और यह केवल सीपीयू 1 के साथ बाहर निकलता है।

स्पिनलॉक के बाद, सीपीयू 0 फिर एक सेमीहोस्ट एग्जिट कॉल करता है जो QEMU को छोड़ देता है।

यदि आप QEMU को केवल एक सीपीयू के साथ शुरू करते हैं -smp 1, तो सिमुलेशन सिर्फ स्पिनलॉक पर हमेशा के लिए लटका रहता है।

CPU 1 को PSCI इंटरफ़ेस के साथ जगाया गया है, ARM पर अधिक विवरण : Start / Wakeup / अन्य सीपीयू कोर / APs को लाएं और निष्पादन प्रारंभ पता पास करें?

नदी के ऊपर संस्करण भी इसलिए आप प्रदर्शन विशेषताओं के साथ साथ ही प्रयोग कर सकते हैं, यह gem5 पर काम करने के लिए कुछ बदलाव किया है।

मैंने इसे वास्तविक हार्डवेयर पर परीक्षण नहीं किया है, इसलिए मुझे यकीन नहीं है कि यह कितना पोर्टेबल है। निम्नलिखित रास्पबेरी पाई ग्रंथ सूची ब्याज की हो सकती है:

यह दस्तावेज़ एआरएम सिंक्रोनाइज़ेशन प्राइमेटिव्स का उपयोग करने पर कुछ मार्गदर्शन प्रदान करता है जिसका उपयोग आप कई कोर के साथ मज़ेदार चीजें करने के लिए कर सकते हैं: http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008AA-arm_synchronization_primatics.pdf

उबंटू 18.10, जीसीसी 8.2.0, बिनुटिल्स 2.31.1, क्यूईएमयू 2.12.0 पर परीक्षण किया गया।

अधिक सुविधाजनक प्रोग्रामबिलिटी के लिए अगला कदम

पिछले उदाहरण माध्यमिक सीपीयू को जागृत करते हैं और समर्पित निर्देशों के साथ बुनियादी मेमोरी सिंक्रनाइज़ेशन करते हैं, जो एक अच्छी शुरुआत है।

लेकिन मल्टीकोर सिस्टम को प्रोग्राम में आसान बनाने के लिए, जैसे POSIX की तरह pthreads, आपको निम्नलिखित अधिक शामिल विषयों में भी जाना होगा:

  • सेटअप बाधित होता है और एक टाइमर चलता है जो समय-समय पर तय करता है कि कौन सा धागा अब चलेगा। इसे प्रीमिटिव मल्टीथ्रेडिंग के रूप में जाना जाता है

    इस तरह की प्रणाली को थ्रेड रजिस्टरों को बचाने और पुनर्स्थापित करने की भी आवश्यकता है क्योंकि वे शुरू और बंद हो जाते हैं।

    गैर-प्रीमेप्टिव मल्टीटास्किंग सिस्टम होना भी संभव है, लेकिन उन लोगों को आपको अपने कोड को संशोधित करने की आवश्यकता हो सकती है ताकि हर थ्रेड उपज (जैसे pthread_yieldकार्यान्वयन के साथ ) हो, और वर्कलोड को संतुलित करना कठिन हो जाए।

    यहाँ कुछ सरल नंगे धातु टाइमर उदाहरण हैं:

  • स्मृति संघर्ष के साथ सौदा। विशेष रूप से, यदि आप C या अन्य उच्च स्तरीय भाषाओं में कोड करना चाहते हैं , तो प्रत्येक थ्रेड को एक विशिष्ट स्टैक की आवश्यकता होगी ।

    आप केवल एक अधिकतम अधिकतम स्टैक आकार के लिए थ्रेड्स को सीमित कर सकते हैं, लेकिन इससे निपटने का अच्छा तरीका पेजिंग के साथ है जो कुशल "असीमित आकार" के ढेर की अनुमति देता है।

    यहाँ एक भोली अराजकता है 64 नंगेतर उदाहरण है कि अगर ढेर बहुत गहरा हो जाता है तो उड़ा देगा

लिनक्स कर्नेल या कुछ अन्य ऑपरेटिंग सिस्टम का उपयोग करने के कुछ अच्छे कारण हैं :-)

उपयोगकर्ताभूमि मेमोरी सिंक्रनाइज़ेशन प्राइमेटीज़

हालाँकि थ्रेड स्टार्ट / स्टॉप / मैनेजमेंट आमतौर पर उपयोगकर्ता दायरे से परे होता है, लेकिन आप संभावित रूप से अधिक महंगी सिस्टम कॉल के बिना मेमोरी एक्सेस को सिंक्रोनाइज़ करने के लिए यूजरलैंड थ्रेड्स से असेंबली निर्देशों का उपयोग कर सकते हैं।

आपको निश्चित रूप से पुस्तकालयों का उपयोग करना पसंद करना चाहिए जो इन निम्न स्तर की प्राथमिकताओं को आंशिक रूप से लपेटते हैं। सी ++ मानक ने स्वयं <mutex>और <atomic>हेडर पर और विशेष रूप से महान प्रगति की है std::memory_order। मुझे यकीन नहीं है कि यह सभी संभव स्मृति शब्दार्थों को प्राप्त करता है, लेकिन यह सिर्फ हो सकता है।

अधिक सूक्ष्म शब्दार्थ विशेष रूप से लॉक मुक्त डेटा संरचनाओं के संदर्भ में प्रासंगिक हैं , जो कुछ मामलों में प्रदर्शन लाभ प्रदान कर सकते हैं। उन्हें लागू करने के लिए, आपको विभिन्न प्रकार के मेमोरी अवरोधों के बारे में थोड़ा सीखना होगा: https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/

उदाहरण के लिए बूस्ट में कुछ लॉक फ्री कंटेनर कार्यान्वयन हैं: https://www.boost.org/doc/libs/1_63_0/doc/html/lockfree.html

लिनक्स futexसिस्टम कॉल को लागू करने के लिए इस तरह के यूजरलैंड निर्देशों का भी उपयोग किया जाता है , जो लिनक्स में मुख्य सिंक्रोनाइज़ेशन प्राइमेटिव्स में से एक है। man futex4.15 पढ़ता है:

फ़ुटेक्स () सिस्टम कॉल प्रतीक्षा के लिए एक विधि प्रदान करता है जब तक कि एक निश्चित स्थिति सच नहीं हो जाती। यह आमतौर पर साझा-मेमोरी सिंक्रनाइज़ेशन के संदर्भ में एक अवरुद्ध निर्माण के रूप में उपयोग किया जाता है। फ़ुटेक्स का उपयोग करते समय, अधिकांश सिंक्रनाइज़ेशन ऑपरेशन उपयोगकर्ता स्थान में किए जाते हैं। एक उपयोगकर्ता-स्पेस प्रोग्राम फ़ुटेक्स () सिस्टम कॉल को केवल तब ही नियोजित करता है जब यह संभावना होती है कि जब तक स्थिति सही न हो जाए, तब तक प्रोग्राम को अधिक समय तक ब्लॉक करना पड़ता है। अन्य फ़ुटेक्स () संचालन का उपयोग किसी भी प्रक्रिया या किसी विशेष स्थिति की प्रतीक्षा कर रहे थ्रेड्स को जगाने के लिए किया जा सकता है।

Syscall नाम का अर्थ है "फास्ट यूजरस्पेस XXX"।

यहाँ इनलाइन असेंबली के साथ एक न्यूनतम बेकार C ++ x86_64 / anarch64 उदाहरण है जो ऐसे निर्देशों के मूल उपयोग को अधिकांशतः मज़ेदार बनाता है:

main.cpp

#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>
#include <vector>

std::atomic_ulong my_atomic_ulong(0);
unsigned long my_non_atomic_ulong = 0;
#if defined(__x86_64__) || defined(__aarch64__)
unsigned long my_arch_atomic_ulong = 0;
unsigned long my_arch_non_atomic_ulong = 0;
#endif
size_t niters;

void threadMain() {
    for (size_t i = 0; i < niters; ++i) {
        my_atomic_ulong++;
        my_non_atomic_ulong++;
#if defined(__x86_64__)
        __asm__ __volatile__ (
            "incq %0;"
            : "+m" (my_arch_non_atomic_ulong)
            :
            :
        );
        // https://github.com/cirosantilli/linux-kernel-module-cheat#x86-lock-prefix
        __asm__ __volatile__ (
            "lock;"
            "incq %0;"
            : "+m" (my_arch_atomic_ulong)
            :
            :
        );
#elif defined(__aarch64__)
        __asm__ __volatile__ (
            "add %0, %0, 1;"
            : "+r" (my_arch_non_atomic_ulong)
            :
            :
        );
        // https://github.com/cirosantilli/linux-kernel-module-cheat#arm-lse
        __asm__ __volatile__ (
            "ldadd %[inc], xzr, [%[addr]];"
            : "=m" (my_arch_atomic_ulong)
            : [inc] "r" (1),
              [addr] "r" (&my_arch_atomic_ulong)
            :
        );
#endif
    }
}

int main(int argc, char **argv) {
    size_t nthreads;
    if (argc > 1) {
        nthreads = std::stoull(argv[1], NULL, 0);
    } else {
        nthreads = 2;
    }
    if (argc > 2) {
        niters = std::stoull(argv[2], NULL, 0);
    } else {
        niters = 10000;
    }
    std::vector<std::thread> threads(nthreads);
    for (size_t i = 0; i < nthreads; ++i)
        threads[i] = std::thread(threadMain);
    for (size_t i = 0; i < nthreads; ++i)
        threads[i].join();
    assert(my_atomic_ulong.load() == nthreads * niters);
    // We can also use the atomics direclty through `operator T` conversion.
    assert(my_atomic_ulong == my_atomic_ulong.load());
    std::cout << "my_non_atomic_ulong " << my_non_atomic_ulong << std::endl;
#if defined(__x86_64__) || defined(__aarch64__)
    assert(my_arch_atomic_ulong == nthreads * niters);
    std::cout << "my_arch_non_atomic_ulong " << my_arch_non_atomic_ulong << std::endl;
#endif
}

गिटहब ऊपर

संभावित उत्पादन:

my_non_atomic_ulong 15264
my_arch_non_atomic_ulong 15267

इससे हम देखते हैं कि x86 LOCK उपसर्ग / anarch64 LDADDनिर्देश ने अतिरिक्त परमाणु बना दिया है: इसके बिना हमारे पास कई जोड़ पर दौड़ की स्थिति है, और अंत में कुल गणना सिंक्रनाइज़ 20000 से कम है।

यह सभी देखें:

Ubuntu 19.04 amd64 में और QEMU अराजकता 64 उपयोगकर्ता मोड के साथ परीक्षण किया गया।


अपने उदाहरण को संकलित करने के लिए आप किस कोडांतरक का उपयोग करते हैं? GAS को आपकी पसंद नहीं लगती है #include(इसे टिप्पणी के रूप में लिया जाता है), NASM, FASM, YASM को AT & T सिंटैक्स नहीं पता है इसलिए यह उन्हें नहीं हो सकता ... तो यह क्या है?
रुस्लान

@Ruslan gcc, #includeC प्रीप्रोसेसर से आता है। Makefileआरंभ किए गए अनुभाग में बताए अनुसार दिए गए उपयोग करें : github.com/cirosantilli/x86-bare-metal-examples/blob/… यदि वह काम नहीं करता है, तो एक GitHub समस्या खोलें।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '29

x86 पर, क्या होगा यदि कोई कोर महसूस करता है कि कतार में चलने के लिए कोई और प्रक्रिया तैयार नहीं है? (जो एक निष्क्रिय प्रणाली पर समय-समय पर हो सकता है)। क्या कोई नया कार्य होने तक साझा मेमोरी संरचना पर कोर स्पिनलॉक है? (शायद यह अच्छा नहीं है कि यह बहुत शक्ति का उपयोग करेगा) क्या यह एचएलटी की तरह कुछ कहता है जब तक कोई बाधा नहीं है? (उस स्थिति में कौन उस कोर को जगाने के लिए ज़िम्मेदार है?)
tigrou

@tigrou यकीन नहीं है, लेकिन मुझे यह बहुत संभावना है कि लिनक्स कार्यान्वयन इसे अगले (संभावित टाइमर) रुकावट तक पावर स्टेट में डाल देगा, विशेष रूप से एआरएम पर जहां पावर कुंजी है। मैं यह देखने के लिए जल्दी से कोशिश करूंगा कि क्या लिनक्स पर चलने वाले सिम्युलेटर के इंस्ट्रक्शन ट्रेस के साथ इसे आसानी से देखा जा सकता है, यह हो सकता है: github.com/cirosantilli/linux-kernel-module-cheat/tree/…
Ciro Santilli see see 冠状法轮功 24

1
कुछ जानकारी (x86 / Windows के लिए विशिष्ट) यहां पाई जा सकती है ("आइडल थ्रेड" देखें)। टीएल; डीआर: जब सीपीयू पर कोई रन करने योग्य धागा मौजूद नहीं होता है, तो सीपीयू को निष्क्रिय थ्रेड में भेज दिया जाता है। कुछ अन्य कार्यों के साथ, यह अंततः पंजीकृत बिजली प्रबंधन प्रोसेसर निष्क्रिय दिनचर्या (सीपीयू विक्रेता द्वारा प्रदान किए गए ड्राइवर के माध्यम से, जैसे: इंटेल) को कॉल करेगा। यह सीपीयू को कुछ गहरी सी-अवस्था (जैसे: C0 -> C3) को बिजली की खपत को कम करने के लिए बदल सकता है।
टाइगरो डे

43

जैसा कि मैं इसे समझता हूं, प्रत्येक "कोर" एक पूर्ण प्रोसेसर है, जिसका अपना रजिस्टर सेट है। मूल रूप से, BIOS आपको एक कोर चलाने के साथ शुरू करता है, और फिर ऑपरेटिंग सिस्टम अन्य कोर को "प्रारंभ" कर सकता है और उन्हें चलाने के लिए कोड पर इंगित कर सकता है, आदि।

सिंक्रनाइज़ेशन ओएस द्वारा किया जाता है। आमतौर पर, प्रत्येक प्रोसेसर ओएस के लिए एक अलग प्रक्रिया चला रहा है, इसलिए ऑपरेटिंग सिस्टम की बहु-थ्रेडिंग कार्यक्षमता यह तय करने के लिए है कि कौन सी प्रक्रिया किस मेमोरी को छूने के लिए मिलती है, और मेमोरी टकराव के मामले में क्या करना है।


28
हालांकि यह भीख मांगता है: ऑपरेटिंग सिस्टम को ऐसा करने के लिए क्या निर्देश उपलब्ध हैं?
पॉल हॉलिंगवर्थ

4
उसके लिए निजीकरण के निर्देशों का एक सेट है, लेकिन यह ऑपरेटिंग सिस्टम की समस्या है, न कि एप्लिकेशन कोड की। यदि एप्लिकेशन कोड को मल्टीथ्रेड किया जाना है तो उसे "मैजिक" करने के लिए ऑपरेटिंग सिस्टम फ़ंक्शन को कॉल करना होगा।
शार्प्यूट

2
BIOS आमतौर पर पहचान करेगा कि कितने कोर उपलब्ध हैं और पूछे जाने पर यह जानकारी ओएस को पास कर देगा। ऐसे मानक हैं जो BIOS (और हार्डवेयर) को इस तरह के अनुरूप होना चाहिए कि विभिन्न पीसी के लिए हार्डवेयर बारीकियों (प्रोसेसर, कोर, पीसीआई बस, पीसीआई कार्ड, माउस, कीबोर्ड, ग्राफिक्स, आईएसए, पीसीआई-ई / एक्स, मेमोरी आदि) तक पहुंच हो। OS के दृष्टिकोण से समान दिखता है। यदि BIOS रिपोर्ट नहीं करता है कि चार कोर हैं ओएस आमतौर पर मान लेगा कि केवल एक ही है। यहां तक ​​कि प्रयोग करने के लिए एक BIOS सेटिंग भी हो सकती है।
ओलोफ फोर्सशेल

1
यह अच्छा है और अगर आप नंगे धातु कार्यक्रम लिख रहे हैं तो क्या होगा?
अलेक्जेंडर रयान बैजट

3
@AlexanderRyanBaggett,? वह भी क्या? दोहराते हुए, जब हम कहते हैं कि "इसे ओएस पर छोड़ दें", तो हम सवाल से बच रहे हैं क्योंकि सवाल यह है कि ओएस इसे कैसे करता है? किस विधानसभा निर्देश का उपयोग करता है?
पचेरियर

39

अनौपचारिक एसएमपी अकसर किये गए सवाल स्टैक ओवरफ्लो लोगो


एक बार, x86 असेंबलर लिखने के लिए, उदाहरण के लिए, आपके पास निर्देश होगा कि "EDX रजिस्टर को मान 5 के साथ लोड करें", "वेतन वृद्धि EDX" रजिस्टर, आदि आधुनिक सीपीयू के साथ जिसमें 4 कोर (या अधिक) हैं। मशीन कोड स्तर पर यह सिर्फ 4 अलग CPUs हैं (जैसे कि वहाँ 4 अलग "EDX" रजिस्टर हैं) लगता है?

बिल्कुल सही। रजिस्टरों के 4 सेट हैं, जिसमें 4 अलग-अलग अनुदेश बिंदु शामिल हैं।

यदि ऐसा है, तो जब आप कहते हैं कि "EDX रजिस्टर बढ़ाएँ", क्या निर्धारित करता है कि CPU का EDX रजिस्टर बढ़ा हुआ है?

सीपीयू ने उस निर्देश को निष्पादित किया, स्वाभाविक रूप से। इसे 4 पूरी तरह से अलग माइक्रोप्रोसेसरों के रूप में सोचें जो केवल एक ही स्मृति साझा कर रहे हैं।

क्या अब x86 असेंबलर में "सीपीयू संदर्भ" या "थ्रेड" अवधारणा है?

नहीं। असेंबलर ने निर्देशों का अनुवाद वैसे ही किया है जैसे हमेशा किया था। वहां कोई बदलाव नहीं।

कोर के बीच संचार / सिंक्रनाइज़ेशन कैसे काम करता है?

चूंकि वे एक ही मेमोरी साझा करते हैं, यह ज्यादातर प्रोग्राम लॉजिक की बात है। हालांकि अब एक इंटर-प्रोसेसर बाधा है तंत्र है, यह आवश्यक नहीं है और मूल रूप से पहले दोहरे-सीपीयू x86 सिस्टम में मौजूद नहीं था।

यदि आप एक ऑपरेटिंग सिस्टम लिख रहे थे, तो विभिन्न तंत्रों पर निष्पादन को निर्धारित करने की अनुमति देने के लिए हार्डवेयर के माध्यम से क्या तंत्र सामने आया है?

शेड्यूलर वास्तव में नहीं बदलता है, सिवाय इसके कि यह महत्वपूर्ण वर्गों और इस्तेमाल किए गए तालों के प्रकारों के बारे में थोड़ा अधिक ध्यान से है। एसएमपी से पहले, कर्नेल कोड अंत में शेड्यूलर को कॉल करेगा, जो रन कतार को देखेगा और अगले थ्रेड के रूप में चलाने के लिए एक प्रक्रिया चुन लेगा। (कर्नेल की प्रक्रियाएं थ्रेड्स की तरह दिखती हैं।) एसएमपी कर्नेल एक ही समय में एक ही कोड, एक थ्रेड को चलाता है, यह सिर्फ इतना है कि अब महत्वपूर्ण सेक्शन लॉकिंग को एसएमपी-सुरक्षित होने की आवश्यकता है सुनिश्चित करें कि दो कोर गलती से नहीं उठा सकते हैं वही पीआईडी।

क्या यह कुछ विशेष विशेषाधिकार प्राप्त निर्देश हैं?

नहीं। कोर बस एक ही पुराने निर्देशों के साथ एक ही मेमोरी में चल रहे हैं।

यदि आप एक मल्टीकोर सीपीयू के लिए एक अनुकूलन कंपाइलर / बायटेकोड वीएम लिख रहे थे, तो आपको विशेष रूप से इसके बारे में क्या कहना होगा, x86 इसे कोड बनाने के लिए जो सभी कोर में कुशलता से चलता है?

आप पहले जैसा ही कोड चलाते हैं। यह यूनिक्स या विंडोज कर्नेल है जिसे बदलने की आवश्यकता है।

आप मेरे प्रश्न को संक्षेप में बता सकते हैं कि "मल्टी-कोर कार्यक्षमता का समर्थन करने के लिए x86 मशीन कोड में क्या बदलाव किए गए हैं?"

कुछ भी आवश्यक नहीं था। पहले एसएमपी सिस्टम ने यूनिप्रोसेसर्स के रूप में सटीक एक ही निर्देश सेट का उपयोग किया। अब, x86 वास्तुकला विकास और चीजों को तेजी से आगे बढ़ाने के लिए नए निर्देशों के zillions का एक बड़ा सौदा हुआ है, लेकिन कोई भी नहीं था एसएमपी के लिए आवश्यक

अधिक जानकारी के लिए, देखें इंटेल मल्टीप्रोसेसर विनिर्देश देखें


अपडेट: सभी फॉलो-अप प्रश्नों का उत्तर केवल पूरी तरह से स्वीकार करते हुए कहा जा सकता है कि एक एन- मल्टी मल्टीकोर सीपीयू लगभग 1 बिलकुल वैसा ही है जैसे n अलग-अलग प्रोसेसर जो बस एक ही मेमोरी साझा करते हैं। 2 एक महत्वपूर्ण सवाल यह नहीं पूछा गया था: एक कार्यक्रम को अधिक प्रदर्शन के लिए एक से अधिक कोर पर चलाने के लिए कैसे लिखा जाता है? और इसका उत्तर है: यह एक थ्रेड लाइब्रेरी जैसे Pthreads का उपयोग करके लिखा गया है कुछ थ्रेड लाइब्रेरी "ग्रीन थ्रेड्स" का उपयोग करती हैं जो OS के लिए दृश्यमान नहीं होती हैं, और उन्हें अलग-अलग कोर नहीं मिलेंगे, लेकिन जब तक थ्रेड लाइब्रेरी कर्नेल थ्रेड सुविधाओं का उपयोग करती है, तब तक आपका थ्रेडेड प्रोग्राम स्वचालित रूप से मल्टीकोर हो जाएगा।
1. पीछे की संगतता के लिए, केवल पहला कोर रीसेट पर शुरू होता है, और कुछ ड्राइवर-प्रकार की चीजों को शेष लोगों को आग लगाने के लिए किया जाना चाहिए।
2. वे सभी परिधीयों को भी स्वाभाविक रूप से साझा करते हैं।


3
मुझे हमेशा लगता है कि "थ्रेड" एक सॉफ्टवेयर अवधारणा है, जिससे मुझे मल्टी-कोर प्रोसेसर को समझना मुश्किल हो जाता है, समस्या यह है कि कोड एक कोर को कैसे बता सकता है "मैं कोर 2 में चलने वाला एक धागा बनाने जा रहा हूं"? क्या ऐसा करने के लिए कोई विशेष विधानसभा कोड है?
डेमोन्ग

2
@demonguy: नहीं, ऐसा कुछ भी विशेष निर्देश नहीं है। आप ओएस को एक विशिष्ट कोर पर एक आत्मीयता मुखौटा स्थापित करके पूछते हैं (जो कहता है "यह धागा तार्किक कोर के इस सेट पर चल सकता है")। यह पूरी तरह से एक सॉफ्टवेयर समस्या है। प्रत्येक सीपीयू कोर (हार्डवेयर धागा) स्वतंत्र रूप से लिनक्स (या विंडोज) चल रहा है। अन्य हार्डवेयर थ्रेड्स के साथ काम करने के लिए, वे साझा डेटा संरचनाओं का उपयोग करते हैं। लेकिन आप कभी भी "सीधे" एक अलग सीपीयू पर एक धागा शुरू नहीं करते हैं। आप ओएस को एक नया सूत्र बताना चाहेंगे, और यह एक डेटा संरचना में एक नोट बनाता है जिसे ओएस दूसरे कोर पर देखता है।
पीटर कॉर्ड्स

2
मैं इसे ओएस बता सकता हूं, लेकिन ओएस विशिष्ट कोर पर कोड कैसे डाल सकता है?
डिमॉन्ग्यूई

4
@demonguy ... (सरलीकृत) ... प्रत्येक कोर ओएस छवि को साझा करता है और इसे उसी स्थान पर चलाना शुरू करता है। तो, 8 कोर के लिए, यह 8 "हार्डवेयर प्रोसेस" है जो कर्नेल में चल रहा है। प्रत्येक एक ही शेड्यूलर फ़ंक्शन को कॉल करता है जो रन करने योग्य प्रक्रिया या थ्रेड के लिए प्रक्रिया तालिका की जांच करता है। (यह रन कतार है। ) इस बीच, थ्रेड वाले प्रोग्राम अंतर्निहित SMP प्रकृति के बारे में जागरूकता के बिना काम करते हैं। वे बस कांटा (2) या कुछ और कर्नेल को यह बताना चाहते हैं कि वे चलाना चाहते हैं। मूल रूप से, कोर को खोजने की प्रक्रिया के बजाय, कोर प्रक्रिया को ढूंढता है।
डिजिटलरॉस

1
आपको वास्तव में एक कोर को दूसरे से बाधित करने की आवश्यकता नहीं है। इसके बारे में इस तरह से सोचें: पहले जो कुछ आपको संवाद करने की जरूरत थी, वह सॉफ्टवेयर तंत्र के साथ ठीक था । एक ही सॉफ्टवेयर तंत्र काम करना जारी रखता है। तो, पाइप, कर्नेल कॉल, स्लीप / वेकअप, वह सब सामान ... वे अभी भी पहले की तरह काम करते हैं। प्रत्येक प्रक्रिया एक ही सीपीयू पर नहीं चल रही है, लेकिन उनके पास संचार के लिए समान डेटा संरचनाएं हैं जैसा कि पहले था। एसएमपी जाने का प्रयास ज्यादातर पुराने तालों को अधिक समानांतर वातावरण में काम करने के लिए सीमित किया गया है।
डिजिटलरॉस

10

यदि आप एक मल्टीकोर सीपीयू के लिए एक अनुकूलन कंपाइलर / बायटेकोड वीएम लिख रहे थे, तो आपको विशेष रूप से इसके बारे में क्या कहना होगा, x86 इसे कोड बनाने के लिए जो सभी कोर में कुशलता से चलता है?

जैसा कि कोई है जो संकलक / बायटेकोड वीएमएस का अनुकूलन लिखता है, मैं यहां आपकी मदद करने में सक्षम हो सकता हूं।

आपको विशेष रूप से x86 के बारे में कुछ भी जानने की जरूरत नहीं है, ताकि यह सभी कोर को कुशलतापूर्वक चलाने वाला कोड उत्पन्न कर सके।

हालाँकि, आपको सही तरीके से चलने वाले कोड लिखने के लिए cmpxchg और दोस्तों के बारे में जानना होगा सभी कोर पर । मल्टीकोर प्रोग्रामिंग को निष्पादन के थ्रेड्स के बीच सिंक्रनाइज़ेशन और संचार के उपयोग की आवश्यकता होती है।

सामान्य रूप से x86 पर कुशलतापूर्वक चलने वाले कोड को उत्पन्न करने के लिए आपको x86 के बारे में कुछ जानना होगा।

आपके लिए यह सीखना उपयोगी होगा:

आपको उन सुविधाओं के बारे में सीखना चाहिए जो ओएस (लिनक्स या विंडोज या ओएसएक्स) आपको कई धागे चलाने की अनुमति देती है। आपको ओपन एंड थ्रेडिंग बिल्डिंग ब्लॉक्स या OSX 10.6 "स्नो लेपर्ड" के आगामी "ग्रैंड सेंट्रल" जैसे समानांतरकरण एपीआई के बारे में सीखना चाहिए।

आपको यह विचार करना चाहिए कि क्या आपका संकलक ऑटो-समांतर होना चाहिए, या यदि आपके संकलक द्वारा संकलित अनुप्रयोगों के लेखक को कई कोर का लाभ उठाने के लिए अपने प्रोग्राम में विशेष वाक्यविन्यास या एपीआई कॉल को जोड़ने की आवश्यकता है।


.NET और Java जैसे कई लोकप्रिय VMs के पास समस्या नहीं है कि उनकी मुख्य GC प्रक्रिया ताले और मूलभूत रूप से सिंगलथ्रेड में कवर हो?
मार्को वैन डे वोर्ट

9

प्रत्येक कोर एक अलग मेमोरी क्षेत्र से निष्पादित होता है। आपका ऑपरेटिंग सिस्टम आपके प्रोग्राम में एक कोर इंगित करेगा और कोर आपके प्रोग्राम को निष्पादित करेगा। आपके कार्यक्रम से यह पता नहीं चलेगा कि एक से अधिक कोर हैं या किस कोर पर यह निष्पादित हो रहा है।

केवल ऑपरेटिंग सिस्टम के लिए कोई अतिरिक्त निर्देश उपलब्ध नहीं है। ये कोर सिंगल कोर चिप्स के समान हैं। प्रत्येक कोर ऑपरेटिंग सिस्टम का एक हिस्सा चलाता है जो सूचना मेमोरी के लिए उपयोग किए जाने वाले आम मेमोरी क्षेत्रों में संचार का संचालन करेगा ताकि अगले मेमोरी क्षेत्र को निष्पादित किया जा सके।

यह एक सरलीकरण है लेकिन यह आपको मूल विचार देता है कि यह कैसे किया जाता है। एंबेडेड.कॉम पर मल्टीकोर्स और मल्टीप्रोसेसर के बारे में इस विषय में बहुत सारी जानकारी है ... यह विषय बहुत जल्दी जटिल हो जाता है!


मुझे लगता है कि किसी को यहां अधिक सावधानी से अंतर करना चाहिए कि मल्टीकोर सामान्य रूप से कैसे काम करता है, और ओएस कितना प्रभावित करता है। "प्रत्येक कोर एक अलग स्मृति क्षेत्र से निष्पादित करता है" मेरी राय में बहुत भ्रामक है। पहले और सबसे महत्वपूर्ण, सिद्धांतों में कई कोर का उपयोग करने के लिए इसकी आवश्यकता नहीं है, और आप आसानी से देख सकते हैं कि एक थ्रेडेड प्रोग्राम के लिए आप एक ही पाठ और डेटा सेगमेंट पर दो काम करना चाहते हैं (जबकि प्रत्येक कोर को स्टैक जैसे व्यक्तिगत संसाधनों की भी आवश्यकता होती है) ।
वोल्कर स्टोलज़

@ShiDoiSi यही कारण है कि मेरे उत्तर में "यह एक सरलीकरण है" पाठ शामिल है
गेरहार्ड

5

असेंबली कोड मशीन कोड में अनुवाद करेगा जिसे एक कोर पर निष्पादित किया जाएगा। यदि आप चाहते हैं कि इसे मल्टीथ्रेड किया जाए तो आपको कई बार अलग-अलग प्रोसेसर पर इस कोड को शुरू करने के लिए ऑपरेटिंग सिस्टम प्रिमिटिव का उपयोग करना होगा या अलग-अलग कोर पर कोड के अलग-अलग टुकड़े - प्रत्येक कोर एक अलग थ्रेड निष्पादित करेगा। प्रत्येक थ्रेड को केवल एक कोर दिखाई देगा, जो वर्तमान में निष्पादित हो रहा है।


4
मैं इस तरह से कुछ कहने जा रहा था, लेकिन फिर ओएस कोर को धागे कैसे आवंटित करता है? मुझे लगता है कि कुछ विशेषाधिकार प्राप्त विधानसभा निर्देश हैं जो इसे पूरा करते हैं। यदि हां, तो मुझे लगता है कि वह उत्तर है जिसकी लेखक को तलाश है।
ए। लेवी

इसके लिए कोई निर्देश नहीं है, यह ऑपरेटिंग सिस्टम अनुसूचक का कर्तव्य है। Win32 में SetThreadAffinityMask जैसे ऑपरेटिंग सिस्टम फ़ंक्शन हैं और कोड उन्हें कॉल कर सकता है, लेकिन यह ऑपरेटिंग सिस्टम सामान है और शेड्यूलर को प्रभावित करता है, यह एक प्रोसेसर निर्देश नहीं है।
शार्प जुथ 11'09

2
एक OpCode होना चाहिए अन्यथा ऑपरेटिंग सिस्टम इसे करने में सक्षम नहीं होगा।
मैथ्यू व्हाइट

1
शेड्यूलिंग के लिए वास्तव में एक ओपकोड नहीं है - यह अधिक है जैसे कि आप प्रति प्रोसेसर ओएस की एक प्रति प्राप्त करते हैं, मेमोरी स्पेस साझा करते हैं; जब भी कोई कोर कर्नेल (syscall या इंटरप्ट) में फिर से प्रवेश करता है, तो यह मेमोरी में समान डेटा संरचनाओं को देखता है ताकि यह तय किया जा सके कि आगे क्या धागा है।
pjc50

1
@ ए.लव: जब आप एक आत्मीयता के साथ एक धागा शुरू करते हैं जो केवल इसे एक अलग कोर पर चलने देता है, तो यह तुरंत दूसरे कोर में नहीं जाता है। इसका संदर्भ सामान्य मेमोरी स्विच की तरह ही मेमोरी में सेव होता है। अन्य हार्डवेयर थ्रेड्स शेड्यूलर डेटा संरचनाओं में इसकी प्रविष्टि देखते हैं, और उनमें से एक अंततः तय करेगा कि यह थ्रेड चलाएगा। तो पहले कोर के दृष्टिकोण से: आप एक साझा डेटा संरचना में लिखते हैं और अंततः एक और कोर (हार्डवेयर थ्रेड) पर ओएस कोड इसे नोटिस करेंगे और इसे चलाएंगे।
पीटर कॉर्ड्स

3

यह मशीन के निर्देशों में बिल्कुल नहीं किया गया है; कोर अलग-अलग सीपीयू होने का दिखावा करते हैं और एक दूसरे से बात करने की कोई विशेष क्षमता नहीं रखते हैं। उनके संवाद करने के दो तरीके हैं:

  • वे भौतिक पता स्थान साझा करते हैं। हार्डवेयर कैश सुसंगतता को संभालता है, इसलिए एक सीपीयू एक मेमोरी पते पर लिखता है जो दूसरा पढ़ता है।

  • वे एक एपीआईसी (प्रोग्रामेबल इंटरप्ट कंट्रोलर) साझा करते हैं। यह भौतिक पता स्थान में मैप की गई मेमोरी है, और इसका उपयोग एक प्रोसेसर द्वारा दूसरों को नियंत्रित करने, उन्हें चालू या बंद करने, व्यवधान भेजने आदि के लिए किया जा सकता है।

http://www.cheesecake.org/sac/smp.html मूर्खतापूर्ण यूआरएल के साथ एक अच्छा संदर्भ है।


2
वे वास्तव में एक APIC साझा नहीं करते हैं। प्रत्येक तार्किक CPU का अपना एक है। APIC आपस में संवाद करते हैं, लेकिन वे अलग-अलग हैं।
नाथन फेलमैन

वे एक बुनियादी तरीके से (संचार के बजाय) सिंक्रनाइज़ करते हैं और यह LOCK उपसर्ग के माध्यम से होता है (निर्देश "xchg मेम, reg" में एक निहित लॉक अनुरोध होता है) जो लॉक पिन तक चलता है जो सभी बसों को प्रभावी ढंग से बताता है कि सीपीयू (वास्तव में कोई भी बस-स्वामी उपकरण) बस में विशेष पहुंच चाहता है। अंततः एक संकेत LOCKA (स्वीकार करें) पिन सीपीयू को बताएगा कि अब उसके पास बस तक अनन्य पहुंच है। चूंकि बाहरी डिवाइस सीपीयू के आंतरिक कामकाज की तुलना में बहुत धीमी हैं, इसलिए LOCK / LOCKA अनुक्रम को पूरा करने के लिए कई सैकड़ों CPU चक्रों की आवश्यकता हो सकती है।
ओलफ फोर्सशेल

1

एकल और बहु-थ्रेडेड अनुप्रयोग के बीच मुख्य अंतर यह है कि पूर्व में एक स्टैक होता है और बाद में प्रत्येक थ्रेड के लिए एक होता है। कोड कुछ अलग तरीके से उत्पन्न होता है क्योंकि कंपाइलर यह मान लेगा कि डेटा और स्टैक सेगमेंट रजिस्टर (ds और ss) बराबर नहीं हैं। इसका मतलब यह है कि ईबीपी और एस्प रजिस्टर के माध्यम से इनडायरेक्ट जो ss रजिस्टर में डिफ़ॉल्ट है वह भी ds (क्योंकि ds! = Ss) के लिए डिफ़ॉल्ट नहीं होगा। इसके विपरीत, अन्य रजिस्टरों के माध्यम से अप्रत्यक्ष जो डीएस के लिए डिफ़ॉल्ट एस.एस.

धागे डेटा और कोड क्षेत्रों सहित अन्य सब कुछ साझा करते हैं। वे काम-काज रूटीन भी साझा करते हैं ताकि यह सुनिश्चित हो सके कि वे थ्रेड-सुरक्षित हैं। एक प्रक्रिया जो रैम में एक क्षेत्र को सॉर्ट करती है, चीजों को गति देने के लिए बहु-थ्रेडेड हो सकती है। इसके बाद थ्रेड्स उसी भौतिक मेमोरी क्षेत्र में डेटा तक पहुंच, तुलना और ऑर्डर करने और समान कोड निष्पादित करने के लिए होंगे, लेकिन विभिन्न प्रकार के स्थानीय चर का उपयोग करके उनके संबंधित हिस्से को नियंत्रित करने के लिए। यह निश्चित रूप से है क्योंकि थ्रेड्स के अलग-अलग स्टैक हैं जहां स्थानीय चर समाहित हैं। इस प्रकार की प्रोग्रामिंग के लिए कोड की सावधानीपूर्वक ट्यूनिंग की आवश्यकता होती है ताकि अंतर-कोर डेटा टकराव (कैश और रैम में) कम हो जाएं, जिसके परिणामस्वरूप एक कोड होता है जो दो या दो से अधिक थ्रेड्स के साथ तेज होता है, यह सिर्फ एक के साथ होता है। बेशक, एक अन-ट्यून्ड कोड दो या अधिक के साथ एक प्रोसेसर के साथ अक्सर तेज होगा। डीबग करना अधिक चुनौतीपूर्ण है क्योंकि मानक "int 3" ब्रेकपॉइंट लागू नहीं होगा क्योंकि आप एक विशिष्ट थ्रेड को बाधित करना चाहते हैं और उन सभी को नहीं। डिबग रजिस्टर ब्रेकपॉइंट्स इस समस्या को हल नहीं करते हैं, जब तक कि आप उन्हें विशिष्ट प्रोसेसर पर सेट नहीं कर सकते हैं जो उस विशिष्ट थ्रेड को निष्पादित करते हैं जिसे आप बाधित करना चाहते हैं।

अन्य बहु-थ्रेडेड कोड में प्रोग्राम के विभिन्न हिस्सों में चलने वाले अलग-अलग धागे शामिल हो सकते हैं। इस प्रकार की प्रोग्रामिंग के लिए एक ही तरह की ट्यूनिंग की आवश्यकता नहीं होती है और इसलिए इसे सीखना बहुत आसान है।


0

प्रत्येक प्रोसेसर-वेरिएंट-सक्षम आर्किटेक्चर पर जो एकल प्रोसेसर वेरिएंट की तुलना में जोड़ा गया है, उससे पहले उन्हें कोर के साथ सिंक्रनाइज़ करने के निर्देश हैं। इसके अलावा, आपके पास कैश सुसंगतता, निस्तब्धता बफ़र्स, और इसी तरह के निम्न-स्तरीय संचालन के साथ एक ओएस से निपटने के निर्देश हैं। आईबीएम पावर 6, आईबीएम सेल, सन नियाग्रा, और इंटेल "हाइपरथ्रेडिंग" जैसे एक साथ बहु-आयामी आर्किटेक्चर के मामले में, आप थ्रेड्स के बीच प्राथमिकता देने के लिए नए निर्देश देखना पसंद करते हैं (जैसे प्राथमिकताएँ सेट करना और प्रोसेसर को स्पष्ट रूप से योदग करना जब कुछ नहीं करना है) ।

लेकिन मूल सिंगल-थ्रेड शब्दार्थ एक ही हैं, आप बस अन्य कोर के साथ सिंक्रनाइज़ेशन और संचार को संभालने के लिए अतिरिक्त सुविधाएं जोड़ते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.