Thư viện chuẩn C trên kim loại trần


24

Tôi chủ yếu thực hiện phát triển trên các thiết bị đã chuyển Linux, vì vậy thư viện C tiêu chuẩn cung cấp rất nhiều chức năng của nó thông qua việc thực hiện các cuộc gọi hệ thống có hành vi được tiêu chuẩn hóa.

Tuy nhiên, đối với kim loại trần, không có hệ điều hành cơ bản. Có một tiêu chuẩn liên quan đến cách triển khai thư viện ac hay bạn phải học lại tính đặc thù của việc triển khai thư viện khi bạn chuyển sang bảng mới cung cấp một BSP khác?


4
Trang web sai cho câu hỏi của bạn.
ott--

8
Tôi đang bỏ phiếu để đóng câu hỏi này ngoài chủ đề vì nó thuộc về Stack Overflow .
uint128_t

1
Nói chung là bạn làm mà không có. Tại sao bạn cần những thứ như vậy mà không có hệ điều hành để hỗ trợ chúng? memcpy và chắc chắn như vậy. Các hệ thống tệp, không nhất thiết, mặc dù đã triển khai fopen, close, v.v ... không quan trọng đối với ram chẳng hạn. printf () là rất rất rất nặng, tấn và tấn mã yêu cầu, làm mà không có. bất kỳ I / O thay thế hoặc làm mà không có. newlib khá cực đoan, nhưng có giúp ích gì nếu bạn không thể làm mà không có, nhưng dù sao bạn cũng phải thực hiện hệ thống trên phần phụ trợ, vậy bạn có cần thêm lớp không?
old_timer

12
Trong khi câu hỏi này là về phần mềm, nó rất cụ thể đối với lập trình nhúng, thường bị SO từ chối. Vì chúng tôi đã có một số câu trả lời tốt ở đây, di chuyển là không phù hợp.
Dave Tweed

1
Mặc dù newlib được đề cập dưới đây trong câu trả lời, bạn cũng có thể thấy newlib-nano hữu ích - dự định của nó là phiên bản rút gọn để sử dụng trong các hệ thống nhúng bị hạn chế tài nguyên. Tôi sử dụng nó trong các dự án trên MCU Cortex M0. Một số trình biên dịch (Atollic TrueSTUDIO là một) sẽ cung cấp tùy chọn sử dụng newlib hoặc newlib-nano.
jjmilburn

Câu trả lời:


20

Vâng, đó là một tiêu chuẩn, chỉ đơn giản là thư viện chuẩn C . Các chức năng của thư viện không yêu cầu HĐH "toàn diện" hay bất kỳ HĐH nào, và có một số triển khai được thiết kế theo mã "kim loại trần", Newlib có lẽ được biết đến nhiều nhất.

Lấy Newlib làm ví dụ, nó yêu cầu bạn viết một tập hợp nhỏ các chức năng cốt lõi, chủ yếu là cách xử lý các tệp và phân bổ bộ nhớ trong hệ thống của bạn. Nếu bạn đang sử dụng một nền tảng mục tiêu chung, rất có thể ai đó đã làm công việc này cho bạn.

Nếu bạn đang sử dụng linux (có thể là OSX và thậm chí là cygwin / msys?) Và gõ man strlen, thì nó sẽ có một phần được gọi là một cái gì đó giống như CONFORMING TO, điều này sẽ cho bạn biết rằng việc triển khai tuân thủ một tiêu chuẩn cụ thể. Bằng cách này, bạn có thể biết liệu thứ gì đó bạn đang sử dụng là một chức năng tiêu chuẩn hay nếu nó phụ thuộc vào một hệ điều hành cụ thể.


1
tôi tò mò về cách thức stdlibthực hiện stdiomà không bị phụ thuộc vào hệ điều hành. như fopen(), fclose(), fread(), fwrite(), putc()getc()? và làm thế nào để malloc()làm việc mà không nói chuyện với hệ điều hành?
robert bristow-johnson

4
Với Newlib, có một lớp bên dưới nó được gọi là "libgloss" chứa (hoặc bạn viết) một vài tá chức năng cho nền tảng của bạn. Ví dụ, một getcharputcharbiết về UART phần cứng của bạn; sau đó các lớp Newlib printftrên đầu trang này. Tệp I / O tương tự sẽ dựa vào một vài nguyên thủy.
Brian Drumond

Vâng, tôi đã không đọc đoạn 2 của ống một cách cẩn thận. ngoài việc xử lý stdinstdoutstderr (chăm sóc putchar()getchar()) điều hướng I / O từ / đến UART, nếu nền tảng của bạn có lưu trữ tệp, như với đèn flash, thì bạn cũng phải viết keo cho điều đó. và bạn phải có phương tiện để malloc()free(). Tôi nghĩ rằng nếu bạn quan tâm đến những vấn đề đó, bạn có thể chạy C di động khá nhiều trong mục tiêu nhúng của mình (không có argvcũng không argc).
robert bristow-johnson

2
Newlib cũng rất lớn nếu bạn giao dịch với MCU với 1 hoặc 2kB không gian mã ...
Brian Drumond

2
@dwelch Bạn không tạo hệ điều hành của riêng mình, bạn đang tạo thư viện C. Nếu bạn không muốn điều đó, thì ừ, nó không cần thiết.
ống

8

Có một tiêu chuẩn liên quan đến cách triển khai thư viện ac hay bạn phải học lại tính đặc thù của việc triển khai thư viện khi bạn chuyển sang bảng mới cung cấp một BSP khác?

Trước hết, tiêu chuẩn C định nghĩa một thứ gọi là triển khai "tự do", trái ngược với triển khai "được lưu trữ" (đó là điều mà hầu hết chúng ta đều quen thuộc, toàn bộ các chức năng C được hỗ trợ bởi HĐH cơ bản).

Việc triển khai "tự do" chỉ cần xác định một tập hợp con của các tiêu đề thư viện C, cụ thể là các tiêu đề không yêu cầu hỗ trợ hoặc thậm chí là định nghĩa của các hàm (chúng chỉ đơn thuần là #defines và typedefs):

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

Khi bạn đang thực hiện bước tiếp theo đối với triển khai được lưu trữ, bạn sẽ thấy rằng chỉ có rất ít chức năng thực sự cần giao diện "hệ thống" theo bất kỳ cách nào, với phần còn lại của thư viện có thể triển khai trên các "nguyên thủy" đó ". Khi triển khai PDCLib , tôi đã nỗ lực cách ly chúng trong một thư mục con riêng biệt để dễ nhận biết khi chuyển lib sang nền tảng mới (ví dụ về cổng Linux trong ngoặc đơn):

  • getenv()( extern char * * environ)
  • system()( fork()/ execve()/ wait())
  • malloc()free()( brk()/ sbrk())
  • _Exit()( _exit())
  • time() (chưa được thực thi)

Và đối với <stdio.h>(được cho là "liên quan đến hệ điều hành" nhất trong số các tiêu đề C99):

  • một số cách để mở một tập tin ( open())
  • một số cách để đóng nó ( close())
  • một số cách để loại bỏ nó ( unlink())
  • một số cách để đổi tên nó ( link()/ unlink())
  • một số cách để viết cho nó ( write())
  • một số cách để đọc từ nó ( read())
  • một số cách để định vị lại trong nó ( lseek())

Một số chi tiết nhất định của thư viện là tùy chọn, với tiêu chuẩn chỉ cung cấp chúng để được thực hiện theo cách tiêu chuẩn nhưng không yêu cầu thực hiện như vậy.

  • Các time()chức năng có thể trở lại hợp pháp (time_t)-1nếu không có cơ chế giữ thời gian có sẵn.

  • Các trình xử lý tín hiệu được mô tả cho <signal.h>nhu cầu không được gọi bởi bất kỳ thứ gì ngoài lệnh gọi đến raise(), không có yêu cầu nào là hệ thống thực sự gửi một cái gì đó giống như SIGSEGVcho ứng dụng.

  • Tiêu đề C11 <threads.h>, (vì lý do rõ ràng) rất phụ thuộc vào HĐH, hoàn toàn không cần phải cung cấp nếu việc triển khai xác định __STDC_NO_THREADS__...

Có nhiều ví dụ hơn, nhưng tôi không có chúng trong tay ngay bây giờ.

Phần còn lại của thư viện có thể được thực hiện mà không cần bất kỳ sự trợ giúp nào từ môi trường. (*)


(*) Hãy cẩn thận: Việc triển khai PDCLib chưa hoàn tất, vì vậy tôi có thể đã bỏ qua một hoặc hai điều. ;-)


4

Tiêu chuẩn C thực sự được định nghĩa tách biệt với môi trường hoạt động. Không có giả định nào được đưa ra về một hệ điều hành máy chủ hiện diện và những phần phụ thuộc vào máy chủ được định nghĩa như vậy.

Đó là, C Standard đã là kim loại trần khá đẹp.

Tất nhiên, những phần ngôn ngữ mà chúng ta yêu thích rất nhiều, các thư viện, thường là nơi ngôn ngữ cốt lõi thúc đẩy việc lưu trữ những thứ cụ thể. Do đó, công cụ biên dịch chéo "xxx-lib" điển hình được tìm thấy cho nhiều công cụ nền tảng kim loại trần.


3

Ví dụ runlable tối thiểu Newlib

Ở đây tôi cung cấp một ví dụ rất tự động và được ghi lại cho thấy newlib đang hoạt động trong QEMU .

Với newlib, bạn thực hiện các cuộc gọi hệ thống của riêng mình cho nền tảng từ xa.

Ví dụ, trong ví dụ trên, chúng tôi có một chương trình ví dụ exit.c:

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

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

và trong một tệp C riêng biệt common.c, chúng tôi thực hiện exitvới bán phần ARM :

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

Các tòa nhà cao tầng điển hình khác mà bạn sẽ thực hiện là:

  • writeđể kết quả đầu ra cho máy chủ. Điều này có thể được thực hiện với:

    • thêm bán kết
    • một phần cứng UART
  • brkcho malloc.

    Dễ dàng thực hiện, vì chúng ta không cần phải quan tâm đến việc phân trang!

TODO Tôi tự hỏi liệu có thực tế khi đạt được việc thực hiện các tòa nhà chọc trời được lên lịch trước mà không đi vào một RTOS đầy đủ như Zephyr hay FreeRTOS không .

Điều thú vị về Newlib, là nó thực hiện tất cả những thứ không thuộc hệ điều hành cụ thể như string.hdành cho bạn và cho phép bạn thực hiện chỉ các sơ khai hệ điều hành.

Ngoài ra, bạn không phải thực hiện tất cả các sơ khai, mà chỉ những cái bạn sẽ cần. Ví dụ, nếu chương trình của bạn chỉ cần exit, thì bạn không phải cung cấp a print.

Cây nguồn Newlib đã có một số triển khai, bao gồm cả triển khai bán phần ARM newlib/libc/sys/arm, nhưng đối với hầu hết các phần bạn phải thực hiện riêng của mình. Tuy nhiên, nó cung cấp một cơ sở vững chắc cho nhiệm vụ.

Cách dễ nhất để thiết lập Newlib là xây dựng trình biên dịch của riêng bạn với crosstool-NG, bạn chỉ cần nói với bạn rằng bạn muốn sử dụng Newlib làm thư viện C. Thiết lập của tôi xử lý tự động cho bạn với tập lệnh này , sử dụng cấu hình newlib có tại crosstool_ng_config.

Tôi nghĩ C ++ cũng sẽ hoạt động, nhưng TODO thử nghiệm nó.


3
@downvoters: vui lòng giải thích để tôi có thể tìm hiểu và cải thiện thông tin. Hy vọng độc giả trong tương lai có thể thấy giá trị của thiết lập Newlib giới thiệu duy nhất có sẵn trên web chỉ hoạt động :-)
Ciro Santilli 改造

2

Khi bạn sử dụng nó, bạn phát hiện ra một số phụ thuộc chưa được thực hiện và phải xử lý chúng. Tất cả những phụ thuộc này là về việc điều chỉnh nội bộ theo tính cách hệ thống của bạn. Ví dụ: khi tôi cố gắng sử dụng sprintf () sử dụng malloc () bên trong. Malloc có biểu tượng chức năng "t_sbrk" như một cái móc trong mã, được người dùng triển khai để thực thi các ràng buộc phần cứng. Ở đây tôi có thể thực hiện nó, hoặc tự tạo malloc () nếu tôi tin rằng tôi có thể làm tốt hơn cho phần cứng nhúng, chủ yếu cho các mục đích sử dụng khác, không chỉ chạy nước rút.


Tại sao sprintf cần malloc ()?
supercat

Tôi không biết. Tôi nghĩ rằng quan điểm của bạn là bộ đệm đã có, phải không? Nhưng ngay cả printf không cần malloc. Có thể để phân bổ động một số biến nội bộ, khi việc tính toán đầu ra được yêu cầu nặng hơn tầm nhìn xa của phân bổ xếp chồng (biến động của hàm)? Tôi chắc chắn rằng sprintf yêu cầu malloc (arm-none-eabi-newlib). Bây giờ tôi đã thử nghiệm một chương trình đơn giản sử dụng sprintf trên máy tính (glibc). Nó không bao giờ được gọi là malloc. Sau đó sử dụng printf. Nó được gọi là malloc. Malloc là giả, luôn luôn trả về 0. Nhưng nó hoạt động tốt. Họ đã in một chuỗi và một biến thập phân. @supercat
Ayhan

Bản thân tôi đã tạo một vài phiên bản printf hoặc các phương thức tương tự, được tùy chỉnh để hỗ trợ các định dạng mà ứng dụng của tôi sử dụng. Đầu ra thập phân yêu cầu bộ đệm đủ dài để chứa số dài nhất có thể, nhưng nếu không, thường trình cơ sở chấp nhận một cấu trúc có thành viên đầu tiên là hàm đầu ra chấp nhận một con trỏ tới cấu trúc đó cùng với dữ liệu được xuất. Thiết kế như vậy cho phép thêm các biến thể printf xuất ra các bảng điều khiển kiểu nguyền rủa, ổ cắm, v.v. Tôi chưa bao giờ có nhu cầu "malloc" trong một việc như vậy.
supercat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.