การจำลอง Linus Torvalds: สร้างระบบปฏิบัติการของคุณเองตั้งแต่เริ่มต้น (VI)

หลังจากวงเล็บเล็กน้อยเราจะดำเนินการต่อด้วยชุดบทเรียนของเรา หากเรากลับไปที่รหัสก่อนหน้าเราต้องมี ISR ของการหารด้วยศูนย์ ตอนนี้เราต้องกรอก ISR ที่เหลือซึ่งเราได้โพสต์ข้อความ (32 รายการแรก) ตอนนี้เรากำลังจะหยุดการเขียนโปรแกรมต่อไปเราจะทำ IRQ หรือที่เรียกว่า ขัดขวางคำขอ IRQ เหล่านี้สร้างขึ้นโดยอุปกรณ์ฮาร์ดแวร์เช่นคีย์บอร์ดเมาส์เครื่องพิมพ์ ฯลฯ เริ่มแรก IRQ 8 ตัวแรกจะถูกแมปโดยอัตโนมัติกับตำแหน่ง IDT 8 ถึง 15 เนื่องจากเราใช้ 32 ตัวแรกสำหรับข้อยกเว้นในตอนนี้เราจึงต้องทำการแมปใหม่ เราจะใส่ IRQ จาก 32 เป็น 45 สำหรับสิ่งนี้เราต้องทำการแมป IRQ ใหม่ก่อน:

void ND::IRQ::Remap(int pic1, int pic2)
{
#define PIC1 0x20
#define PIC2 0xA0
#define ICW1 0x11
#define ICW4 0x01
/* send ICW1 */
ND::Ports::OutputB(PIC1, ICW1);
ND::Ports::OutputB(PIC2, ICW1);
/* send ICW2 */
ND::Ports::OutputB(PIC1 + 1, pic1); /* remap */
ND::Ports::OutputB(PIC2 + 1, pic2); /* pics */
/* send ICW3 */
ND::Ports::OutputB(PIC1 + 1, 4); /* IRQ2 -> connection to slave */
ND::Ports::OutputB(PIC2 + 1, 2);
/* send ICW4 */
ND::Ports::OutputB(PIC1 + 1, ICW4);
ND::Ports::OutputB(PIC2 + 1, ICW4);
/* disable all IRQs */
ND::Ports::OutputB(PIC1 + 1, 0xFF);
}

ตอนนี้เราสร้างฟังก์ชั่นเพื่อติดตั้ง IRQ:

void ND::IRQ::Install()
{
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);
ND::Screen::PutString("\nInstalling IRQ...");
ND::IRQ::Remap(0x20,0x28);
ND::IDT::SetGate(32,(unsigned)ND::IRQ::IRQ1,0x08,0x8E);
ND::IDT::SetGate(33,(unsigned)ND::IRQ::IRQ2,0x08,0x8E);
ND::IDT::SetGate(34,(unsigned)ND::IRQ::IRQ3,0x08,0x8E);
ND::IDT::SetGate(35,(unsigned)ND::IRQ::IRQ4,0x08,0x8E);
ND::IDT::SetGate(36,(unsigned)ND::IRQ::IRQ5,0x08,0x8E);
ND::IDT::SetGate(37,(unsigned)ND::IRQ::IRQ6,0x08,0x8E);
ND::IDT::SetGate(38,(unsigned)ND::IRQ::IRQ7,0x08,0x8E);
ND::IDT::SetGate(39,(unsigned)ND::IRQ::IRQ8,0x08,0x8E);
ND::IDT::SetGate(40,(unsigned)ND::IRQ::IRQ9,0x08,0x8E);
ND::IDT::SetGate(41,(unsigned)ND::IRQ::IRQ10,0x08,0x8E);
ND::IDT::SetGate(42,(unsigned)ND::IRQ::IRQ11,0x08,0x8E);
ND::IDT::SetGate(43,(unsigned)ND::IRQ::IRQ12,0x08,0x8E);
ND::IDT::SetGate(44,(unsigned)ND::IRQ::IRQ13,0x08,0x8E);
ND::IDT::SetGate(45,(unsigned)ND::IRQ::IRQ14,0x08,0x8E);
ND::IDT::SetGate(46,(unsigned)ND::IRQ::IRQ15,0x08,0x8E);
ND::IDT::SetGate(47,(unsigned)ND::IRQ::IRQ16,0x08,0x8E);
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);
ND::Screen::PutString("done");
asm volatile("sti");
}

ประโยคของ asm STI เราเปิดใช้งาน IRQ ตอนนี้เราไปกับสิ่งที่คล้ายกับ ISR หน้าที่ของ IRQ พื้นฐาน:

void ND::IRQ::IRQ1()
{
asm volatile(
"cli \n"
"pushl 0\n"
"pushl 32\n"
"jmp ND_IRQ_Common"
);
}

ส่วนทั่วไป (เช่นเดียวกับ ISR):

extern "C"
void ND_IRQ_Common()
{
asm volatile(
"pusha \n"
"push %ds\n"
"push %es\n"
"push %fs\n"
"push %gs\n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_IRQ_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}

และตัวจัดการพื้นฐาน:

extern "C"
void ND_IRQ_Handler(struct regs* r)
{
void (*handler)(struct regs *r);
if(r->int_no >= 40)
{
ND::Ports::OutputB(0xA0,0x20);
}
ND::Ports::OutputB(0x20,0x20);
}

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

ถัดไป


และด้วยสิ่งนี้จะสิ้นสุดลง อย่างที่คุณเห็นตอนนี้ฉันเขียนน้อยลงเป็นประจำเนื่องจากปัญหาอื่น ๆ อย่างไรก็ตามฉันจะดำเนินการต่อไปจนกว่าฉันจะมีระบบปฏิบัติการที่สมบูรณ์ยิ่งขึ้น


แสดงความคิดเห็นของคุณ

อีเมล์ของคุณจะไม่ถูกเผยแพร่ ช่องที่ต้องการถูกทำเครื่องหมายด้วย *

*

*

  1. ผู้รับผิดชอบข้อมูล: Miguel ÁngelGatón
  2. วัตถุประสงค์ของข้อมูล: ควบคุมสแปมการจัดการความคิดเห็น
  3. ถูกต้องตามกฎหมาย: ความยินยอมของคุณ
  4. การสื่อสารข้อมูล: ข้อมูลจะไม่ถูกสื่อสารไปยังบุคคลที่สามยกเว้นตามข้อผูกพันทางกฎหมาย
  5. การจัดเก็บข้อมูล: ฐานข้อมูลที่โฮสต์โดย Occentus Networks (EU)
  6. สิทธิ์: คุณสามารถ จำกัด กู้คืนและลบข้อมูลของคุณได้ตลอดเวลา

  1.   aitor_cz dijo

    ขอบคุณมากเอเดรียนทันทีที่ฉันมีเวลา (ตอนนี้ฉันค่อนข้างยุ่งเหนือสิ่งอื่นใดรวมถึงระบบปฏิบัติการด้วย) ฉันจะเริ่มทดสอบบทช่วยสอนทีละขั้นตอน

  2.   ทับทิม dijo

    ขอบคุณมากสำหรับ tute

  3.   Sasuke dijo

    คำถามหนึ่งที่ฉันกำลังทำโครงการโดยใช้ภาษาโปรแกรมเหล่านี้
    * Html5
    * Css3
    * Java
    คำถามของฉันคือฉันต้องการให้โปรเจ็กต์นี้สามารถใช้งานได้ดังนั้นผู้ใช้จึงใช้มันในระบบปฏิบัติการเช่น linux และ windows คุณช่วยบอกฉันได้ไหมว่าฉันทำอย่างไร

    1.    เอเดรียน ArroyoStreet dijo

      ไม่มีอะไรเกี่ยวข้องกับเรื่องนี้ แต่ฉันตอบคุณอย่างไรก็ตาม ฉันคิดว่ามันจะเป็น HTML5, CSS3 และ JavaScript ไม่ใช่ Java เนื่องจาก HTML5 และ Java เข้ากันได้ดี ด้วย HTML5 คุณสามารถสร้างเว็บไซต์เหมือนเดิมที่เข้าถึงได้จากอินเทอร์เน็ต หากคุณต้องการทำให้เป็นแบบท้องถิ่นคุณสามารถจัดแพคเกจเป็นแอป Firefox OS และ Chrome OS หากสิ่งที่คุณต้องการคือในแต่ละระบบปฏิบัติการจะมีไฟล์ปฏิบัติการให้ดูที่ XUL Runner ซึ่งเป็นเครื่องมือในการรัน XUL (ดังนั้น HTML5 ภายในองค์ประกอบ) ด้วยเอ็นจิ้น Gecko

    2.    ดินเปเรซ dijo

      เฟรมหรือพาเนล Java เป็นตัวเลือกที่ดีมากฉันได้สร้างแอพปฏิบัติการบางตัวในหน้าต่างโดยใช้คลาส java frame เป็นเว็บเบราว์เซอร์ แต่แทนที่จะใช้กับเพจใด ๆ ฉันให้พา ธ โดยตรงในโค้ดและด้วย php ฉันรันประโยค java ผ่าน ของ. โถที่ดูแลเหล็ก แม้ว่าฉันจะแนะนำให้ใช้ HTML5, CSS3 และ JavaScript เนื่องจาก Adrian Java กล่าวว่าใช้ Fatal กับ Html5 และทำให้ฉันปวดหัวมากมาย

  4.   ในเมือง dijo

    คำแนะนำเกี่ยวกับการสร้างภาษาโปรแกรมของคุณเองก็น่าจะดี

  5.   อีวาน dijo

    บทความชุดนี้ดีมากเกี่ยวกับวิธีสร้างระบบปฏิบัติการคุณได้เรียนรู้หลายสิ่งหลายอย่าง ฉันรอคอยรายการถัดไปฉันต้องการมีแป้นพิมพ์ใน OS อยู่แล้ว ฉันยุ่งกับรหัส git และฉันไม่สามารถทำให้มันทำงานกับพอร์ต 0x60 และ 0x64 ได้ แม้ว่าฉันจะคิดว่าสำหรับแป้นพิมพ์มีการขัดจังหวะที่ทำให้คุณกดแป้น

    1.    เอเดรียน ArroyoStreet dijo

      คุณสามารถรับอินพุตคีย์บอร์ดได้โดยไม่หยุดชะงัก แต่คุณต้องอ่านด้วย ND :: Ports :: InputB บนพอร์ต 0x60 อย่างไรก็ตามวิธีที่ดีที่สุดในการทำเช่นนี้คือการขัดจังหวะ IRQ ตอนนี้ฉันกำลังพยายามทำอยู่และใช้เวลานานกว่าจะดำเนินการต่อเนื่องจากสิ่งนั้น

      1.    Carlosorta dijo

        สวัสดีเอเดรียนฉันกำลังตรวจสอบโค้ดและฉันประทับใจกับสิ่งที่ทำและมันช่วยให้ฉันเข้าใจบางสิ่งได้ดีเพียงใด

        ฉันมีคำถามเล็ก ๆ น้อย ๆ เกี่ยวกับวิธีการทำงานและแหล่งที่มาคือคำถามที่ฉันได้รับจาก GIT ของคุณ:

        1.- ในส่วนของ IRQ คุณได้กล่าวว่าตำแหน่งตั้งแต่ 0 ถึง 32 ของ IDT ถูกใช้สำหรับข้อยกเว้นและตั้งแต่ 32 (0x20) ถึง 45 (0x2D) สำหรับ IRQ แต่ IRQ มีทั้งหมด 16 การรีแมปจะไม่อยู่ที่ 0x20 ถึง 0x30?

        2.- ในส่วน IRQ โปรดทราบว่า setgates ถูกส่งไปยังส่วน IDT เมื่อคุณแยกพวกเขาโปรดสังเกตว่าไม่มีการสร้างข้อยกเว้นของการหารด้วย 0 อีกต่อไปดังนั้นจึงจำเป็นต้องเพิ่ม IDT Flush () สำหรับการแก้ไขแต่ละครั้ง ทำไม IDT ถึงเปลี่ยนไปเมื่อตั้ง Timer และการหารระหว่าง 0 หยุดทำงาน

        3.- ฉันพยายามติดตามรหัสด้วยการพิมพ์บางอย่างเพื่อใช้เป็นตัวบ่งชี้ว่ามันกำลังดำเนินการอะไรอยู่ (เพื่อที่จะไม่ทำการดีบั๊กทีละขั้นตอน) และฉันตระหนักว่าไม่มี IRQ กำลังทำงานอยู่ฉันต้องลงทะเบียนอย่างอื่นเพื่อ IRQ ขัดจังหวะสร้าง?

        1.    Carlosorta dijo

          ฉันลืมบอกว่าฉันพบลิงค์เหล่านี้พร้อมข้อมูล:
          http://arstechnica.com/civis/viewtopic.php?f=20&t=899001
          http://www.superfrink.net/athenaeum/OS-FAQ/os-faq-pics.html
          http://orga2.exp.dc.uba.ar/data/downloads/clasespracticas/interrupciones2_clase_17.pdf
          http://www.intel-assembler.it/PORTALE/4/231468_8259A_PIC.pdf

          เห็นได้ชัดว่าสำหรับการจัดการ IRQ จำเป็นต้องคำนึงถึงประเภทของตัวจัดการที่ใช้ถ้า, PIC, APIC, IOAPIC . .etc. มีวิธีทำให้การจัดการ IRQ แบบไดนามิกหรือคุณต้องลองเสี่ยงโชค?

  6.   Carlosorta dijo

    สวัสดีตอนบ่ายเอเดรียน

    ฉันเห็นว่าฉันมีปัญหากับ IRQ และนั่นคือสาเหตุที่รหัสไม่สามารถเป็นขั้นสูงได้ฉันจึงถ่ายสำเนาของโครงการและเริ่มวิเคราะห์ ฉันเพิ่มฟังก์ชั่นในการพิมพ์หน้าจอเพื่อพิมพ์เร็กคอร์ด reg ของ struct reg ในขณะที่ทำการขัดจังหวะ ฉันพบหลายสิ่งหลายอย่างในหมู่พวกเขาที่รีจิสทรีกำลังทำงานอยู่และฉันยังหาสาเหตุไม่พบ เปลี่ยนการขัดจังหวะตัวตั้งเวลาเพื่อให้แป้นพิมพ์ขัดจังหวะเพื่อทดสอบและทำในสิ่งที่ควร แต่ไม่พบปัญหาคุณช่วยฉันและดำเนินการต่อด้วยโพสต์ดีๆนี้ได้ไหม 😀

    ฉันออกจากลิงค์ (มันมีการปรับเปลี่ยนบางอย่างเพราะฉันใช้ Mageia และฉันใช้ Grub2 ฉันใช้ VirtualBox เพื่อทดสอบ)
    https://www.mediafire.com/?93psrgaoozatse8

    รอการตอบกลับอย่างเอาใจใส่ของคุณและหากคุณมีคำถามหรือต้องการสิ่งที่ฉันต้องการให้ช่วย🙂

    1.    Carlosorta dijo

      ฉันลืมบอกว่าฉันตรวจสอบ KernelPanic ด้วยเพราะ ISR ไม่ทำงานและฉันมีปัญหาเดียวกันที่ด้านบนของสแต็กค่ากำลังถูกกรองและฉันไม่รู้ว่ามันเป็นคอมไพเลอร์ของฉันหรือมีปัญหาฉันใช้ GCC 4.8.2 กับ Mageia 4

    2.    เอเดรียน ArroyoStreet dijo

      ฉันชอบที่คุณช่วยฉันในโครงการ ฉันติดอยู่ในตัวจับเวลาจริงๆและฉันไม่เข้าใจว่าทำไมมันไม่ทำงาน ฉันทำการทดสอบการปรับเปลี่ยนบางสิ่งหลายอย่าง แต่ก็ไม่ได้ผล ตอนนี้ฉันไม่สามารถแก้ไขโค้ดได้ (ฉันอยู่ระหว่างการพักผ่อน) แต่ฉันจะตรวจสอบให้ดีโดยเร็วที่สุด ฉันให้ลิงค์พร้อมข้อมูลเกี่ยวกับปัญหานี้ซึ่งดูเหมือนจะเป็นเรื่องธรรมดา: http://wiki.osdev.org/I_Cant_Get_Interrupts_Working

      เกี่ยวกับข้อยกเว้นฉันคิดว่าฉันจำได้ว่าคุณต้องโทรไปที่ "sti" ใน ASM เพื่อเปิดใช้งานแม้ว่าจะชัดเจนว่ามีบางอย่างผิดปกติ

      1.    Carlosorta dijo

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

      2.    Carlosorta dijo

        จะไม่มีทางเห็นรหัสที่ประกอบหรือไม่มีอะไรแปลก ๆ เกิดขึ้นและฉันไม่พบอะไรดูที่หน้าจอนี้ (ฉันใส่ลิงค์ไว้ที่ด้านท้าย) มันเป็นสิ่งที่แปลกเนื่องจากในฟังก์ชั่น IRQ 2 (แป้นพิมพ์) มันเข้าสู่ จัดเรียงค่า 0 และ 0x20 (32 เพื่อให้พอดีกับการทดสอบ) จากนั้น pushal (การลงทะเบียน GPR 32 บิต) ตามด้วยการลงทะเบียนการแบ่งส่วนจากนั้นด้านบนของสแต็กแล้วเรียก IRQ Handler ฉันเริ่มเห็นแต่ละสแต็กและเห็นได้ชัดว่าเป็นไปตามลำดับ แต่ถ้าคุณเห็นผลลัพธ์ของ VM คุณจะเห็นว่ามันซ้อนกันอีกหนึ่งองค์ประกอบฉันไม่พบที่ไหนฉันเพิ่งรู้ว่ามันเป็น 0x10 และโครงสร้างก็ไม่เป็นระเบียบ นี่คือโครงสร้างบันทึก

        โครงสร้าง regs {
        uint32_t gs, fs, es, ds; / * ดันวินาทีสุดท้าย * /
        uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; / * ผลักดันโดย 'pushal' * /
        uint32_t int_no, err_code; / * รหัสข้อผิดพลาด * /
        uint32_t eip, cs, eflags, useresp, ss; / * ซ้อนเมื่อพัก * /
        };

        มันทำให้ฉันมีกระป๋องที่วางอีกหนึ่งองค์ประกอบที่ด้านบนและฉันไม่สามารถหาตำแหน่งที่วางซ้อนกันได้หากเห็นได้ชัดว่าทุกอย่างเรียงซ้อนกันตามที่ควร คุณมีความคิดว่ามันจะเป็นอย่างไร?

        http://www.subeimagenes.com/img/sin-titulo-1036729.jpg

  7.   Carlosorta dijo

    ทำให้แป้นพิมพ์ขัดจังหวะทำงาน แต่ไม่ใช่ตัวจับเวลาขัดจังหวะ ข้อผิดพลาดอยู่ในวิธีการคอมไพล์ฉันใช้ objdump เพื่อดูวัตถุสุดท้ายและปรากฎว่าทุกฟังก์ชั่นที่ป้อนแม้ใช้ "asm volatile" ก็มาพร้อมกับ pushl ebp, mov ebp, esp จากนั้นเพียงเพิ่ม ebp ป๊อปเพื่อเรียกคืนสแต็กเริ่มต้นและจะส่งผ่านอาร์กิวเมนต์โดยไม่ล้มเหลว ที่นี่ฉันแนบรหัสสำหรับผู้ที่ต้องการตรวจสอบและหากคุณสามารถทราบได้ว่าเหตุใดตัวจับเวลาจึงไม่เกิดการขัดจังหวะฉันชอบที่จะรู้และแนบลิงค์ที่พวกเขาพูดถึงการทำงานหลายอย่างพร้อมกัน http://www.jamesmolloy.co.uk/tutorial_html/9.-Multitasking.html
    ถัดไป Divel
    https://www.mediafire.com/?kmo83dxyzc7c3cz

    1.    เอเดรียน ArroyoStreet dijo

      ฉันเข้าใจแล้ว. มันเป็นข้อบกพร่องในที่อื่นที่ส่งผลต่อการเปิดใช้งาน Timer PIC โดยเฉพาะในการรีแมป IRQ มีสองบรรทัดที่ฉันต้องแก้ไข ต้องขอบคุณที่ฉันเห็นโค้ดบนเว็บไซต์ซึ่ง ณ จุดนั้นมีบางอย่างที่แตกต่างออกไปและคลิก! การเปลี่ยนแปลงเป็นไปตามแนวของ:
      ND :: พอร์ต :: เอาต์พุต B (PIC1 + 1, 0xFF);
      ND :: พอร์ต :: เอาต์พุต B (PIC2 + 1, 0xFF);

      คุณต้องเปลี่ยนค่าจาก 0xFF (ฉันจะบอกว่ามันแสดงว่าปิดใช้งาน) เป็น 0x00 (ฉันจะบอกว่ามันแสดงว่าเปิดใช้งาน) แม้ว่าฉันจะไม่แน่ใจจริงๆ แต่มันก็ใช้ได้ ฉันได้อัปเดตโค้ดบน GitHub แล้ว ขอบคุณมากที่ช่วยฉันในโครงการที่ฉันถูกทิ้งไปเล็กน้อยเนื่องจากปัญหานี้ ซ

      1.    Carlosorta dijo

        ไม่เป็นไรฉันจะรอการอัปเดตหัวข้อต่อไปและคุณสามารถไว้วางใจฉันได้ทุกอย่าง🙂 (Y)

      2.    Carlosorta dijo

        เปลี่ยนรูทีนการจับสตริงคีย์บอร์ด เนื่องจากกำลังอ่านเมื่อปล่อยคีย์และติดตั้ง 0 ในบัฟเฟอร์ซึ่งทำให้เกิดปัญหาในการอ่านและในตอนท้าย '\ n' ให้เปลี่ยนเป็น»เพื่อให้การพิมพ์ที่ถูกต้องทำงานได้

  8.   ดินเปเรซ dijo

    สวัสดีฉันอ่านโพสต์ทั้งหมดของคุณแม้ว่าในทางปฏิบัติจะไม่เกิน 2 โพสต์ แต่มันดีมากจริงๆบันทึกทุกอย่าง แต่ฉันจะต้องศึกษา c ++ และ posix ให้เข้าใจเพราะฉันรู้เกี่ยวกับ "c" (ฉันหลงใหลในภาษาโปรแกรมนั้น) แต่ แม้ว่า c ++ จะเป็น c OO แต่ฉันไม่เคยใช้มันมาก่อนอ่านบทช่วยสอนใน Google จากนั้นฉันก็ส่งคืนสิ่งนี้ที่น่าสนใจมากและคำถามคือการเริ่มต้นหน้าต่างคล้ายกับ linux หรือไม่

    1.    เอเดรียน ArroyoStreet dijo

      การบูตใน Windows มีความคล้ายคลึงกันในแง่ที่ว่านี่เป็นวิธีการบูตระบบด้วยโปรเซสเซอร์ x86 และระบบปฏิบัติการที่สร้างขึ้นมีผลเพียงเล็กน้อย เราไม่ได้ทำการบูทตัวเองจริงๆมันกำลังเริ่ม GRUB สำหรับเรา GRUB ออกแบบมาเพื่อบูต Linux สามารถบูต Windows ได้และในกรณีนี้ NextDivel

      1.    ซอย dijo

        โอเคขอบคุณนั่นหมายความว่าสิ่งที่ฉันต้องการทำนั้นเป็นไปได้ฉันกำลังศึกษา c ++ อยู่แล้วและฉันได้สร้างแอพบางตัวและติดตั้งระบบของคุณบนเพนไดรฟ์และฉันกำลังศึกษารายละเอียดเพิ่มเติมเป็นโพสต์ที่ดีมาก