C ไลบรารี่มาตรฐานบนโลหะเปลือย


24

ฉันส่วนใหญ่ทำการพัฒนาบนอุปกรณ์ที่มีพอร์ต Linux ดังนั้นไลบรารี C มาตรฐานให้ฟังก์ชันการทำงานจำนวนมากผ่านการเรียกใช้ระบบซึ่งมีพฤติกรรมมาตรฐาน

อย่างไรก็ตามสำหรับโลหะเปลือยไม่มี OS พื้นฐาน มีมาตรฐานที่เกี่ยวข้องกับวิธีการใช้ไลบรารี ac หรือไม่หรือคุณต้องเรียนรู้ลักษณะเฉพาะของการใช้งานไลบรารี่เมื่อคุณเปลี่ยนไปใช้บอร์ดใหม่ที่มี BSP ที่แตกต่างกันหรือไม่?


4
เว็บไซต์ไม่ถูกต้องสำหรับคำถามของคุณ
ott--

8
ฉันลงคะแนนเพื่อปิดคำถามนี้เป็นปิดหัวข้อเพราะมันเป็นในกองมากเกิน
uint128_t

1
โดยทั่วไปคุณทำโดย ทำไมคุณต้องการสิ่งต่าง ๆ โดยไม่มีระบบปฏิบัติการเพื่อสนับสนุนพวกเขา? memcpy และแน่นอนเช่นนั้น ระบบไฟล์ไม่จำเป็นแม้ว่าจะมีการใช้งาน fopen, close, etc ก็ตาม printf () หนักมากมากต้องใช้รหัสตันและตันโดยไม่ต้องทำ I / O ใด ๆ แทนที่หรือทำโดยไม่ต้อง newlib ค่อนข้างสุดขีด แต่จะช่วยได้ถ้าคุณไม่สามารถทำได้โดยไม่ต้องทำ แต่คุณต้องใช้ระบบในแบ็กเอนด์อยู่แล้วคุณต้องการเลเยอร์เพิ่มเติมหรือไม่
old_timer

12
ในขณะที่คำถามนี้เกี่ยวกับซอฟต์แวร์เป็นเรื่องที่เฉพาะเจาะจงมากสำหรับการเขียนโปรแกรมแบบฝังตัวซึ่งโดยทั่วไปถูกปฏิเสธโดย SO เนื่องจากเรามีคำตอบที่ดีอยู่แล้วการย้ายถิ่นจึงไม่เหมาะสม
Dave Tweed

1
ในขณะที่ newlib ได้รับการกล่าวถึงด้านล่างในคำตอบคุณอาจพบว่าnewlib-nanoมีประโยชน์ - ซึ่งมีวัตถุประสงค์เพื่อเป็นเวอร์ชันย้อนกลับสำหรับใช้ในระบบฝังตัวที่ จำกัด ทรัพยากร ฉันใช้มันในโครงการบน Cortex M0 MCUs คอมไพเลอร์จำนวนหนึ่ง (Atollic TrueSTUDIO เป็นหนึ่ง) จะให้ตัวเลือกในการใช้ newlib หรือ newlib-nano
jjmilburn

คำตอบ:


20

ใช่มีมาตรฐานเพียงไลบรารีมาตรฐาน C ฟังก์ชั่นห้องสมุดไม่จำเป็นต้องใช้ระบบปฏิบัติการ "เต็มเป่า" หรือระบบปฏิบัติการใด ๆ และมีการใช้งานจำนวนมากที่เหมาะกับรหัส "โลหะเปลือย" Newlib อาจเป็นที่รู้จักกันดีที่สุด

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

หากคุณใช้ linux (อาจเป็น OSX และแม้กระทั่ง cygwin / msys?) และพิมพ์man strlenก็ควรมีส่วนที่เรียกว่าคล้ายCONFORMING TOๆ ซึ่งจะบอกคุณว่าการใช้งานนั้นสอดคล้องกับมาตรฐานเฉพาะ วิธีนี้คุณสามารถเข้าใจได้ว่าสิ่งที่คุณใช้เป็นฟังก์ชั่นมาตรฐานหรือขึ้นอยู่กับระบบปฏิบัติการเฉพาะ


1
ฉันอยากรู้ว่าการstdlibดำเนินการstdioโดยไม่ต้องพึ่งพาระบบปฏิบัติการเป็นอย่างไร เช่นfopen(), fclose(), fread(), fwrite(), putc()และgetc()? และวิธีการmalloc()ทำงานโดยไม่ต้องพูดคุยกับระบบปฏิบัติการ?
robert bristow-johnson

4
ด้วย Newlib จะมีเลเยอร์ด้านล่างที่เรียกว่า "libgloss" ซึ่งมี (หรือที่คุณเขียน) ฟังก์ชั่นมากมายสำหรับแพลตฟอร์มของคุณ ตัวอย่างเช่น a getcharและputcharรู้เกี่ยวกับ UART ของฮาร์ดแวร์ของคุณ จากนั้นเลเยอร์ Newlib printfด้านบนของสิ่งเหล่านี้ ไฟล์ I / O ในทำนองเดียวกันจะพึ่งพาบางดั้งเดิม
Brian Drummond

ใช่ฉันไม่ได้อ่านย่อหน้าที่ 2 ของไพพ์อย่างระมัดระวัง นอกเหนือจากการจัดการstdinและstdoutและstderr (ซึ่งดูแลputchar()และgetchar()) ซึ่งนำ I / O จาก / ไปยัง UART หากแพลตฟอร์มของคุณมีที่จัดเก็บไฟล์เช่นเดียวกับแฟลชคุณต้องเขียนกาวสำหรับสิ่งนั้น และคุณจะต้องมีวิธีการในและmalloc() free()ฉันคิดว่าถ้าคุณจัดการกับปัญหาเหล่านั้นคุณสามารถเรียกใช้ C พกพาในเป้าหมายฝังตัวของคุณ (ไม่argvและargc)
robert bristow-johnson

2
Newlib ยังเป็นใหญ่ถ้าคุณกำลังติดต่อกับ MCU ด้วย 1 หรือ 2KB ของพื้นที่รหัส ...
ไบรอันดรัมมอนด์

2
@dwelch คุณไม่ได้สร้างระบบปฏิบัติการของคุณเองคุณกำลังสร้างไลบรารี่ C ถ้าคุณไม่ต้องการมันใช่แล้วมันไม่ใหญ่มาก
ท่อ

8

มีมาตรฐานที่เกี่ยวข้องกับวิธีการใช้ไลบรารี ac หรือไม่หรือคุณต้องเรียนรู้ลักษณะเฉพาะของการใช้งานไลบรารี่เมื่อคุณเปลี่ยนไปใช้บอร์ดใหม่ที่มี BSP ที่แตกต่างกันหรือไม่?

ก่อนอื่นมาตรฐาน C กำหนดสิ่งที่เรียกว่าการใช้งาน "อิสระ" ซึ่งตรงข้ามกับการใช้ "โฮสต์" (ซึ่งเป็นสิ่งที่เราส่วนใหญ่คุ้นเคย

การดำเนินการ "อิสระ" จำเป็นต้องกำหนดชุดย่อยของส่วนหัวของไลบรารี C คือส่วนที่ไม่ต้องการการสนับสนุนหรือแม้แต่คำจำกัดความของฟังก์ชั่น (พวกเขาทำเพียง#defines และtypedefs):

  • <float.h>
  • <iso646.h>
  • <limits.h>
  • <stdalign.h>
  • <stdarg.h>
  • <stdbool.h>
  • <stddef.h>
  • <stdint.h>
  • <stdnoreturn.h>

เมื่อคุณเข้าสู่ขั้นตอนถัดไปสู่การใช้งานโฮสต์คุณจะพบว่ามีฟังก์ชั่นเพียงไม่กี่ตัวเท่านั้นที่จำเป็นต้องใช้อินเทอร์เฟซ "ระบบ" ในทางใดทางหนึ่งกับส่วนที่เหลือของห้องสมุด " ในการนำPDCLibมาใช้ฉันพยายามแยกบางส่วนในไดเรกทอรีย่อยแยกต่างหากเพื่อระบุตัวตนได้ง่ายเมื่อทำการย้าย lib ไปยังแพลตฟอร์มใหม่ (ตัวอย่างสำหรับพอร์ต Linux ในวงเล็บ):

  • getenv()( extern char * * environ)
  • system()( fork()/ execve()/ wait())
  • malloc()และfree()( brk()/ sbrk())
  • _Exit()( _exit())
  • time() (ยังไม่ได้ใช้งาน)

และสำหรับ<stdio.h>(เนื้อหาส่วนใหญ่ "ระบบปฏิบัติการที่เกี่ยวข้อง" ของส่วนหัว C99):

  • วิธีการเปิดไฟล์ ( open())
  • วิธีปิดบางอย่าง ( close())
  • วิธีลบ ( unlink())
  • วิธีการเปลี่ยนชื่อบางอย่าง ( link()/ unlink())
  • วิธีการเขียนถึงมัน ( write())
  • วิธีการอ่านจากมัน ( read())
  • วิธีการเปลี่ยนตำแหน่งภายใน ( lseek())

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

  • time()ฟังก์ชั่นอาจจะถูกต้องตามกฎหมายเพียงกลับ(time_t)-1ถ้าไม่มีกลศาสตร์เวลาการเก็บรักษาที่มีอยู่

  • ตัวจัดการสัญญาณที่อธิบายถึง<signal.h>ความต้องการไม่จำเป็นต้องถูกเรียกใช้โดยสิ่งอื่นใดนอกจากการเรียกไปraise()ยังไม่มีความต้องการที่ระบบจะส่งสิ่งที่ชอบSIGSEGVไปยังแอปพลิเคชัน

  • ส่วนหัว C11 <threads.h>ซึ่งเป็น (สำหรับเหตุผลที่ชัดเจน) ขึ้นอยู่กับระบบปฏิบัติการเป็นอย่างมากไม่จำเป็นต้องมีให้ถ้าการใช้งานกำหนด__STDC_NO_THREADS__...

มีตัวอย่างเพิ่มเติม แต่ฉันไม่ได้อยู่ในมือตอนนี้

ส่วนที่เหลือของไลบรารีสามารถดำเนินการได้โดยไม่มีความช่วยเหลือจากสภาพแวดล้อม (*)


(*) Caveat: การใช้งาน PDCLib ยังไม่สมบูรณ์ดังนั้นฉันอาจมองข้ามสิ่งหนึ่งหรือสองอย่าง ;-)


4

มาตรฐาน C ถูกกำหนดแยกต่างหากจากสภาพแวดล้อมการทำงานจริง ไม่มีข้อสันนิษฐานเกี่ยวกับโฮสต์ระบบปฏิบัติการที่มีอยู่และส่วนเหล่านั้นที่ขึ้นอยู่กับโฮสต์จะถูกกำหนดเช่น

นั่นคือมาตรฐาน C เป็นโลหะเปลือยสวยอยู่แล้ว

แน่นอนส่วนภาษาเหล่านั้นที่เรารักมากห้องสมุดมักจะเป็นที่ภาษาหลักผลักดันสิ่งที่โฮสต์ที่เฉพาะเจาะจง ดังนั้นสิ่งทั่วไปของคอมไพเลอร์ข้าม "xxx-lib" ที่พบสำหรับเครื่องมือแพลตฟอร์มโลหะเปลือยจำนวนมาก


3

Newlib ตัวอย่างน้อยที่สุด runnable

นี่ฉันให้เป็นตัวอย่างอย่างอัตโนมัติและจัดทำเอกสารที่แสดงให้เห็น newlib ในการดำเนินการใน QEMU

ด้วย newlib คุณสามารถใช้การเรียกระบบของคุณเองสำหรับแพลตฟอร์มเปล่าของคุณ

ตัวอย่างเช่นในตัวอย่างข้างต้นเรามีโปรแกรมตัวอย่างexit.c:

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

void main(void) {
    exit(0);
}

และในไฟล์ C ที่แยกต่างหากcommon.cเราใช้ the exitด้วยการสร้าง semihostingด้วยARM :

void _exit(int status) {
    __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
}

syscalls ทั่วไปอื่น ๆ ที่คุณจะใช้คือ:

  • writeเพื่อผลลัพธ์ผลลัพธ์ไปยังโฮสต์ สามารถทำได้ทั้งกับ:

    • semihosting มากขึ้น
    • ฮาร์ดแวร์ UART
  • brkmallocสำหรับ

    ง่ายใน baremetal เนื่องจากเราไม่ต้องสนใจเรื่องการเพจ!

สิ่งที่ต้องทำผมสงสัยว่ามันเป็นเหตุผลที่จะไปถึงการดำเนินมาตรการการจัดตารางเวลา syscalls โดยไม่ต้องไปเป็นเป่าเต็มRTOSเช่นZephyrหรือFreeRTOS

สิ่งที่ยอดเยี่ยมเกี่ยวกับ Newlib คือมันใช้สิ่งต่าง ๆ ที่ไม่ใช่ของระบบปฏิบัติการเช่นเดียวstring.hกับคุณและช่วยให้คุณสามารถติดตั้งระบบปฏิบัติการได้อย่างสมบูรณ์

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

ทรีซอร์สของ Newlib มีการใช้งานบางอย่างอยู่แล้วรวมถึงการใช้งานเซมิโคสติ้ง ARM ภายใต้newlib/libc/sys/armแต่ส่วนใหญ่คุณจะต้องใช้งานของคุณเอง แต่จะให้ฐานที่มั่นคงสำหรับงาน

วิธีที่ง่ายที่สุดในการตั้งค่า Newlib คือการสร้างคอมไพเลอร์ของคุณเองด้วย crosstool-NG คุณเพียงแค่บอกว่าคุณต้องการใช้ Newlib เป็นไลบรารี C จับตั้งค่าของฉันที่จะให้คุณกับสคริปต์นี้ซึ่งใช้ newlib configs crosstool_ng_configในปัจจุบันที่

ฉันคิดว่า C ++ จะทำงานได้เช่นกัน แต่ต้องทดสอบ


3
@downvoters: โปรดอธิบายเพื่อที่ฉันจะได้เรียนรู้และปรับปรุงข้อมูล หวังว่าผู้อ่านในอนาคตจะเห็นคุณค่าของการตั้งค่า Newlib เบื้องต้นเพียงอย่างเดียวที่มีอยู่บนเว็บที่ใช้งานได้ :-)
Ciro Santilli 新疆改造中心中心法轮功事件事件

2

เมื่อคุณใช้มันเปล่าคุณจะค้นพบการพึ่งพาที่ไม่ได้ดำเนินการและต้องจัดการมัน การพึ่งพาเหล่านี้ทั้งหมดเกี่ยวกับการปรับ internals ตามบุคลิกภาพของระบบของคุณ ตัวอย่างเช่นเมื่อฉันพยายามใช้ sprintf () ซึ่งใช้ malloc () ด้านใน Malloc มีสัญลักษณ์ฟังก์ชั่น "t_sbrk" ในรูปแบบของ hook ซึ่งจะต้องมีการใช้งานโดยผู้ใช้เพื่อบังคับใช้ฮาร์ดแวร์ที่สอดคล้องกัน ที่นี่ฉันอาจใช้หรือทำให้ malloc ของฉันเอง () ถ้าฉันเชื่อว่าฉันสามารถทำได้ดีกว่าสำหรับฮาร์ดแวร์ที่ฝังตัวส่วนใหญ่สำหรับการใช้งานอื่น ๆ ไม่เพียง แต่ sprintf


ทำไม sprintf ควรต้องการ malloc ()
supercat

ฉันไม่รู้ ฉันคิดว่าจุดของคุณคือบัฟเฟอร์ที่มีอยู่แล้วใช่ไหม แต่แม้ printf ไม่ควรต้องการ malloc บางทีการจัดสรรตัวแปรภายในบางส่วนแบบไดนามิกเมื่อการคำนวณของเอาต์พุตที่ร้องขอนั้นหนักกว่าการคาดการณ์ล่วงหน้าของการจัดสรรแบบซ้อน (ตัวแปรแบบไดนามิกของฟังก์ชัน) ฉันแน่ใจว่า sprintf ต้องการ malloc (arm-none-eabi-newlib) ตอนนี้ฉันทดลองโปรแกรมง่าย ๆ ใช้ sprintf บนคอมพิวเตอร์ (glibc) มันไม่เคยเรียกว่า malloc จากนั้นใช้ printf มันเรียกว่า malloc Malloc เป็นของปลอมกลับมาที่ 0 เสมอ แต่ก็ใช้ได้ดี พวกเขาจะพิมพ์สตริงและตัวแปรทศนิยม @supercat
Ayhan

ฉันได้สร้าง printf หรือวิธีที่คล้ายกันสองสามแบบด้วยตัวเองซึ่งได้รับการปรับแต่งเพื่อรองรับรูปแบบที่แอปพลิเคชันของฉันใช้ ทศนิยมเอาต์พุตต้องการบัฟเฟอร์ที่ยาวพอที่จะเก็บหมายเลขที่ยาวที่สุดที่เป็นไปได้ แต่มิฉะนั้นรูทีนฐานจะยอมรับโครงสร้างที่สมาชิกรายแรกเป็นฟังก์ชันเอาต์พุตที่ยอมรับตัวชี้ไปยังโครงสร้างนั้นพร้อมกับข้อมูลที่จะส่งออก การออกแบบดังกล่าวทำให้สามารถเพิ่มตัวแปร printf ที่ส่งออกไปยังคอนโซลสไตล์ curses ซ็อกเก็ตและอื่น ๆ ฉันไม่เคยมีความต้องการ "malloc" ในสิ่งนี้
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.