Làm thế nào để một ngôn ngữ tự mở rộng? [đóng cửa]


208

Tôi đang học C ++ và tôi mới bắt đầu tìm hiểu về một số khả năng của Qt để mã hóa các chương trình GUI. Tôi đã tự hỏi mình câu hỏi sau:

Làm thế nào mà C ++, trước đây không có cú pháp có khả năng yêu cầu HĐH cho một cửa sổ hoặc cách giao tiếp qua mạng (với các API mà tôi không hiểu hoàn toàn, tôi thừa nhận) đột nhiên có được các khả năng đó thông qua các thư viện được viết bằng C ++? Tất cả dường như tròn trịa với tôi. Những hướng dẫn C ++ nào bạn có thể nghĩ ra trong các thư viện đó?

Tôi nhận ra câu hỏi này có vẻ tầm thường đối với một nhà phát triển phần mềm có kinh nghiệm nhưng tôi đã nghiên cứu trong nhiều giờ mà không tìm thấy bất kỳ câu trả lời trực tiếp nào. Đã đến lúc tôi không thể làm theo hướng dẫn về Qt vì sự tồn tại của các thư viện là không thể hiểu được đối với tôi.


27
Làm thế nào để std :: cout thậm chí vẽ bất cứ thứ gì trên màn hình? Hoặc nó ngồi trên trình biên dịch hiểu phần cứng của bạn?
Doctorlove

14
Câu hỏi tuyệt vời. Cuối cùng, thật khó để trả lời cho đến khi bạn học phần cứng.
dùng541686

17
Qt không phải là một bản mở rộng của ngôn ngữ (sẽ yêu cầu trình biên dịch nhận biết Qt). Nó chỉ đơn thuần là một thư viện được thêm vào kho vũ khí của bạn. Cuối cùng, ở cấp độ thấp nhất, tất cả các thư viện giao tiếp với HĐH thông qua các cuộc gọi hệ thống, độc lập với ngôn ngữ, nhưng phụ thuộc rất nhiều vào hệ điều hành và kiến ​​trúc CPU.
DevSolar

8
afaik, C ++ có lắp ráp nội tuyến, có thể làm được khá nhiều thứ
Tên hiển thị

9
@DevSolar: Thật ra Qt mở rộng ngôn ngữ với cơ chế khe tín hiệu, phản xạ và nhiều tính năng động khác. Và những thứ đó đòi hỏi một trình biên dịch (trình biên dịch đối tượng meta) để biên dịch xuống mã C ++.
Siyuan Ren

Câu trả lời:


194

Một máy tính giống như một củ hành, nó có nhiều lớp, từ lõi bên trong của phần cứng thuần túy đến lớp ứng dụng ngoài cùng. Mỗi lớp hiển thị các phần của chính nó cho lớp bên ngoài tiếp theo, do đó lớp bên ngoài có thể sử dụng một số chức năng của lớp bên trong.

Trong trường hợp ví dụ Windows, hệ điều hành sẽ hiển thị cái gọi là API WIN32 cho các ứng dụng chạy trên Windows. Thư viện Qt sử dụng API đó để cung cấp các ứng dụng sử dụng Qt cho API của chính nó. Bạn sử dụng Qt, Qt sử dụng WIN32, WIN32 sử dụng các cấp thấp hơn của hệ điều hành Windows, và cứ thế cho đến khi đó là tín hiệu điện trong phần cứng.


56
Lưu ý: Qtở đây cung cấp một sự trừu tượng của lớp bên dưới nó, bởi vì trên Linux Qtgọi API Linux chứ không phải API WIN32.
Matthieu M.

5
Tôi có lẽ sẽ nói thêm một chút về ví dụ của Qt, rằng nó chỉ xuất hiện, giống như nó dễ dàng mở rộng khả năng của c ++. Khi thực tế, họ đã nỗ lực rất nhiều, để tạo ra một API chung, để (có thể giải thích được) nhiều "lõi hành" khác nhau. Họ là những người cung cấp tính di động trên đầu trang phụ trợ không tiêu chuẩn không di động.
luk32

81
Một chiếc máy tính giống như một củ hành: cắt qua nó khiến bạn khóc, nhưng sau đó nó có phần ngon miệng.
alecov

3
@ChristopherPfohl Vâng, tôi đã phải sử dụng nó vì tôi không thể hiểu làm thế nào một máy tính sẽ giống như một hộp sôcôla. :)
Một số lập trình viên anh chàng

1
@Celeritas giáo viên có thể nói user32.dll, hoặc có thể gdi32.dll.
dùng253751

59

Bạn nói đúng rằng, nói chung, các thư viện không thể tạo ra bất cứ điều gì có thể mà không thể.

Nhưng các thư viện không phải được viết bằng C ++ để có thể sử dụng được bằng chương trình C ++. Ngay cả khi chúng được viết bằng C ++, bên trong chúng có thể sử dụng các thư viện khác không được viết bằng C ++. Vì vậy, thực tế là C ++ không cung cấp bất kỳ cách nào để làm điều đó không ngăn cản nó được thêm vào, miễn là có một số cách để làm điều đó bên ngoài C ++.

Ở mức khá thấp, một số chức năng được gọi bởi C ++ (hoặc bằng C) sẽ được viết bằng cách lắp ráp và hội đồng chứa các hướng dẫn cần thiết để làm bất cứ điều gì không thể (hoặc không dễ) trong C ++, ví dụ như để gọi một chức năng hệ thống. Tại thời điểm đó, cuộc gọi hệ thống đó có thể làm bất cứ điều gì máy tính của bạn có khả năng, đơn giản là vì không có gì ngăn chặn nó.


Bạn có nghĩa là những thư viện được viết bằng các ngôn ngữ khác đã được biên dịch bằng cách sử dụng các trình biên dịch khác? Và sau đó sẽ phải có một loại tệp giao diện liên kết từng lệnh gọi hàm được thư viện cung cấp cho C ++ với phiên bản được biên dịch sẵn của thư viện? Vì vậy, cho phép trình biên dịch C ++ biết những gì để dịch những cuộc gọi đó thành?
Med Larbi Sentissi

2
@MedLarbiSentissi 1) Không nhất thiết phải là trình biên dịch khác. Có thể (và thường là như vậy), một trình biên dịch duy nhất có khả năng biên dịch nhiều ngôn ngữ, bao gồm cả trình biên dịch và thậm chí nó có thể có khả năng biên dịch C ++ với lắp ráp nội tuyến. 2) Tùy thuộc vào hệ thống và trình biên dịch cụ thể, việc thực hiện các chức năng đó có thể gọi được từ C ++ thực sự có thể được thực hiện với một loại tệp giao diện nào đó, nhưng loại tệp giao diện đó có thể là tiêu đề C (hoặc thậm chí C ++) có thể sử dụng trực tiếp từ C ++.

1
@MedLarbiSentissi: Nhiều thư viện windows được biên dịch thành các tệp dll chứa giao diện riêng, cũng như mã. Bạn có thể nhìn trộm vào một dll và xem danh sách các chức năng mà nó cho phép bạn sử dụng. Họ cũng thường đi kèm với một tệp tiêu đề C. Khi bạn tạo exe của bạn, nó chứa một danh sách các dll cần thiết để chạy. Khi HĐH cố tải exe của bạn, nó cũng sẽ tự động tải các dll đó trước khi bắt đầu thực thi.
Vịt mướp

8
Câu trả lời này dường như gợi ý rằng "ma thuật" nằm hoàn toàn trong các ngôn ngữ khác được gọi, nhưng thực ra hầu hết các mã cấu thành hầu hết các hệ điều hành hiện đại là C (chỉ có các phần rất quan trọng về phần cứng hoặc hiệu năng được viết trong phần lắp ráp) - và Thay vào đó, chắc chắn có thể sử dụng C ++. Vấn đề là, không có "ma thuật", các ngôn ngữ được tạo ra để xây dựng sự trừu tượng mạnh mẽ như vậy và một khi bạn có thể tương tác với phần cứng thì khả năng gần như vô hạn.
Matteo Italia

1
@hvd Tôi nghĩ rằng toàn bộ xung đột trong cuộc thảo luận này là bạn (và những người khác) định nghĩa C là các tính năng được chỉ định cho nó. Trong thực tế, trình biên dịch thêm rất nhiều so với những gì được chỉ định, làm cho câu hỏi về C là gì không tầm thường để trả lời. Đối với tôi, điều đặc biệt về một ngôn ngữ (vì vậy nó là ngôn ngữ) là cách siêu tốc để thể hiện luồng chương trình và khả năng cấu trúc. Các yếu tố được cấu trúc không quan trọng đối với nó, vì nó chỉ là mã ASM đẹp hơn có thể được thêm vào bởi các trình biên dịch theo ý muốn
LionC

43

C và C ++ có 2 thuộc tính cho phép tất cả khả năng mở rộng này mà OP đang nói đến.

  1. C và C ++ có thể truy cập bộ nhớ
  2. C và C ++ có thể gọi mã lắp ráp cho các hướng dẫn không phải bằng ngôn ngữ C hoặc C ++.

Trong kernel hoặc trong một nền tảng chế độ không được bảo vệ cơ bản, các thiết bị ngoại vi như cổng nối tiếp hoặc ổ đĩa được ánh xạ vào bản đồ bộ nhớ giống như RAM. Bộ nhớ là một loạt các công tắc và lật các công tắc của thiết bị ngoại vi (như cổng nối tiếp hoặc trình điều khiển đĩa) giúp thiết bị ngoại vi của bạn thực hiện những việc hữu ích.

Trong hệ điều hành chế độ được bảo vệ, khi muốn truy cập kernel từ không gian người dùng (giả sử khi ghi vào hệ thống tệp hoặc để vẽ pixel trên màn hình), người ta cần thực hiện cuộc gọi hệ thống. C không có hướng dẫn để thực hiện cuộc gọi hệ thống nhưng C có thể gọi mã trình biên dịch mã có thể kích hoạt lệnh gọi hệ thống chính xác, Đây là điều cho phép mã C của một người nói chuyện với kernel.

Để làm cho việc lập trình một nền tảng cụ thể trở nên dễ dàng hơn, các cuộc gọi hệ thống được gói trong các chức năng phức tạp hơn có thể thực hiện một số chức năng hữu ích trong chương trình của chính mình. Một người có thể gọi trực tiếp các cuộc gọi hệ thống (sử dụng trình biên dịch chương trình) nhưng có thể dễ dàng hơn khi chỉ sử dụng một trong các chức năng bao bọc mà nền tảng cung cấp.

Có một cấp độ API khác hữu ích hơn nhiều so với lệnh gọi hệ thống. Lấy ví dụ malloc. Điều này không chỉ gọi hệ thống để có được các khối bộ nhớ lớn mà còn quản lý bộ nhớ này bằng cách thực hiện tất cả các cuốn sách lưu giữ những gì đang diễn ra.

API Win32 bao bọc một số chức năng đồ họa với một bộ tiện ích nền tảng chung. Qt thực hiện điều này thêm một chút bằng cách gói API Win32 (hoặc X Windows) theo cách đa nền tảng.

Về cơ bản mặc dù trình biên dịch C biến mã C thành mã máy và do máy tính được thiết kế để sử dụng mã máy, bạn nên mong đợi C có thể thực hiện chia sẻ sư tử hoặc những gì máy tính có thể làm. Tất cả những gì các thư viện trình bao bọc làm là nâng đỡ bạn rất nhiều để bạn không phải làm.


Hãy cẩn thận về # 2: C và C ++ chỉ có thể gọi các hàm một cách khả thi tuân theo một "quy ước gọi" mà trình biên dịch hiểu và mong đợi. (Mã hội có thể sử dụng bất kỳ quy ước nào nó thích hoặc thậm chí không có quy ước nào - vì vậy mã có thể không thể gọi được trực tiếp.) May mắn thay, mọi trình biên dịch tự tôn đều cung cấp một cách tích hợp để sử dụng các quy ước chung của nền tảng. (Ví dụ, trình biên dịch Windows C cho phép bạn có / sử dụng các hàm sử dụng các quy ước "cdecl", "stdcall" hoặc "fastcall".) Nhưng mã lắp ráp phải sử dụng quy ước mà trình biên dịch biết, hoặc C và C ++ có thể ' T gọi nó trực tiếp.
cHao

2
Ngoài ra: I / O được ánh xạ bộ nhớ là phổ biến, nhưng không phải là toàn bộ câu chuyện. PC, ví dụ, thường xử lý các cổng nối tiếp, ổ đĩa, v.v ... bằng cách sử dụng "cổng I / O" của x86, một cơ chế hoàn toàn khác. (Bộ đệm video thường được ánh xạ bộ nhớ, nhưng các chế độ video, v.v. thường được điều khiển thông qua các cổng I / O.)
cHao

@cHao: Tất nhiên cách tiếp cận cổ điển sử dụng INP và OUTP đang được đưa ra để ủng hộ DMA; thế hệ PCI dường như làm được nhiều hơn với các thanh ghi chức năng đặc biệt được ánh xạ bộ nhớ, giờ đây có một cách để tự động ánh xạ các thiết bị đến các vùng không chồng lấp và khám phá chúng từ các trình điều khiển, và ít hơn với các cổng I / O.
Ben Voigt

các thiết bị ngoại vi hiện đại sẽ sử dụng DMA để truyền dữ liệu số lượng lớn nhưng bạn vẫn sẽ lập trình bộ điều khiển DMA với bộ nhớ có thể đánh địa chỉ
doron

@doron: Umm, bạn lập trình bộ điều khiển DMA thông qua không gian địa chỉ I / O (không phải không gian bộ nhớ) trên PC, ít nhất là nếu bạn lành mạnh. Các CPU x86 hiện đại muốn sắp xếp lại các truy cập bộ nhớ để cải thiện hiệu suất. Với MMIO, điều đó có thể là thảm họa ... vì vậy bạn cần cẩn thận để làm cho các địa chỉ đó không bị xóa đặt các hướng dẫn nối tiếp ở tất cả các vị trí thích hợp. OTOH, x86 tự đảm bảo rằng việc đọc và ghi vào không gian I / O được thực hiện theo thứ tự chương trình. Đó là lý do tại sao nhiều nội dung quan trọng vẫn được thực hiện thông qua không gian I / O (thường không thể truy cập được thông qua một con trỏ) và có lẽ sẽ luôn luôn như vậy.
cHao

23

Ngôn ngữ (như C ++ 11 ) là thông số kỹ thuật , trên giấy, thường được viết bằng tiếng Anh. Xem bên trong bản nháp C ++ 11 mới nhất (hoặc mua thông số cuối cùng tốn kém từ nhà cung cấp ISO của bạn).

Bạn thường sử dụng máy tính với một số triển khai ngôn ngữ (Về nguyên tắc, bạn có thể chạy chương trình C ++ mà không cần bất kỳ máy tính nào, ví dụ: sử dụng một nhóm nô lệ người diễn giải nó; điều đó là phi đạo đức và không hiệu quả)

Tổng triển khai C ++ của bạn hoạt động trên một số hệ điều hành và giao tiếp với nó (sử dụng một số mã cụ thể triển khai , thường trong một số thư viện hệ thống). Nói chung là giao tiếp được thực hiện thông qua các cuộc gọi hệ thống . Tìm ví dụ vào các tòa nhà chọc trời (2) để biết danh sách các cuộc gọi hệ thống có sẵn trên nhân Linux .

Từ quan điểm ứng dụng, một tòa nhà chọc trời là một hướng dẫn máy cơ bản như SYSENTERtrên x86-64 với một số quy ước ( ABI )

Trên máy tính để bàn Linux của tôi, các thư viện Qt là trên X11 thư viện client giao tiếp với máy chủ X11 Xorg qua giao thức X Windows .

Trên Linux, sử dụng lddtrên tệp thực thi của bạn để xem danh sách (dài) phụ thuộc vào thư viện. Sử dụng pmaptrong quá trình chạy của bạn để xem cái nào được "tải" khi chạy. BTW, trên Linux, ứng dụng của bạn có thể chỉ sử dụng phần mềm miễn phí, bạn có thể nghiên cứu mã nguồn của nó (từ Qt, đến Xlib, libc, ... kernel) để hiểu thêm về những gì đang xảy ra


2
Để tham khảo, ANSI bán thông số kỹ thuật C ++ 11 với mức giá thấp hơn một chút là US $ 60. (Trước đây chỉ bằng một nửa, nhưng lạm phát .: P) Nó được dán nhãn là THU NHẬP / ISO / IEC 14882, nhưng ít nhất đó là cùng một thông số kỹ thuật cơ bản mà ISO cung cấp. Không chắc chắn về errata / TRs.
cHao

19

Tôi nghĩ rằng khái niệm bạn đang thiếu là các cuộc gọi hệ thống . Mỗi hệ điều hành cung cấp một lượng lớn tài nguyên và chức năng mà bạn có thể khai thác để thực hiện những việc liên quan đến hệ điều hành cấp thấp. Ngay cả khi bạn gọi một chức năng thư viện thông thường, nó có thể đang thực hiện một cuộc gọi hệ thống đằng sau hậu trường.

Các cuộc gọi hệ thống là một cách sử dụng sức mạnh của hệ điều hành ở mức độ thấp, nhưng có thể phức tạp và khó sử dụng, do đó, thường được "bọc" trong các API để bạn không phải đối phó trực tiếp với chúng. Nhưng bên dưới, bất cứ điều gì bạn làm liên quan đến các tài nguyên liên quan đến O / S sẽ sử dụng các cuộc gọi hệ thống, bao gồm in ấn, kết nối mạng và ổ cắm, v.v.

Trong trường hợp của windows, Microsoft Windows có GUI thực sự được ghi vào kernel, do đó, có các lệnh gọi hệ thống để tạo windows, vẽ đồ họa, v.v. Trong các hệ điều hành khác, GUI có thể không phải là một phần của kernel, trong trường hợp này theo như tôi biết thì sẽ không có bất kỳ cuộc gọi hệ thống nào cho những thứ liên quan đến GUI và bạn chỉ có thể làm việc ở mức thậm chí thấp hơn với bất kỳ cuộc gọi liên quan đến đồ họa và đầu vào cấp thấp nào có sẵn.


3
Điều quan trọng còn thiếu là những cuộc gọi hệ thống đó hoàn toàn không phải là phép thuật. Chúng được phục vụ bởi kernel thường được viết bằng C (++). Hơn nữa, các tòa nhà cao tầng thậm chí không cần thiết. Trong hệ điều hành thô sơ không có bảo vệ bộ nhớ, các cửa sổ có thể được vẽ bằng cách đưa pixel trực tiếp vào bộ đệm khung phần cứng.
el.pescado

15

Câu hỏi hay. Mỗi nhà phát triển C hoặc C ++ mới đều có ý tưởng này. Tôi giả sử một máy x86 tiêu chuẩn cho phần còn lại của bài viết này. Nếu bạn đang sử dụng trình biên dịch Microsoft C ++, hãy mở notepad của bạn và nhập tệp này (đặt tên tệp Test.c)

int main(int argc, char **argv)
{
   return 0
}

Và bây giờ biên dịch tệp này (sử dụng dấu nhắc lệnh của nhà phát triển) cl Test.c /FaTest.asm

Bây giờ hãy mở Test.asm trong notepad của bạn. Những gì bạn thấy là mã được dịch - C / C ++ được dịch sang trình biên dịch chương trình. Bạn có nhận được gợi ý?

_main   PROC
    push    ebp
    mov ebp, esp
    xor eax, eax
    pop ebp
    ret 0
_main   ENDP

Các chương trình C / C ++ được thiết kế để chạy trên kim loại. Điều đó có nghĩa là họ có quyền truy cập vào phần cứng cấp thấp hơn giúp khai thác các khả năng của phần cứng dễ dàng hơn. Giả sử, tôi sẽ viết thư viện C getch () trên máy x86.

Tùy thuộc vào trình biên dịch, tôi sẽ gõ một cái gì đó theo cách này:

_getch proc 
   xor AH, AH
   int 16h
   ;AL contains the keycode (AX is already there - so just return)
ret

Tôi chạy nó với trình biên dịch chương trình và tạo một .OBJ - Đặt tên là getch.obj.

Sau đó tôi viết một chương trình C (Tôi không bao gồm bất cứ điều gì)

extern char getch();

void main(int, char **)
{
  getch();
}

Bây giờ đặt tên tệp này - GetChTest.c. Biên dịch tệp này bằng cách chuyển getch.obj cùng. (Hoặc biên dịch riêng lẻ thành .obj và LINK GetChTest.Obj và getch.Obj cùng nhau để tạo GetChTest.exe).

Chạy GetChTest.exe và bạn sẽ thấy rằng nó chờ đầu vào bàn phím.

Lập trình C / C ++ không chỉ là về ngôn ngữ. Để trở thành một lập trình viên C / C ++ giỏi, bạn cần có một sự hiểu biết tốt về loại máy mà nó chạy. Bạn sẽ cần biết cách quản lý bộ nhớ được xử lý, cách đăng ký cấu trúc, v.v., Bạn có thể không cần tất cả những thông tin này để lập trình thông thường - nhưng chúng sẽ giúp bạn rất nhiều. Ngoài kiến ​​thức phần cứng cơ bản, chắc chắn sẽ giúp ích nếu bạn hiểu cách trình biên dịch hoạt động (nghĩa là, cách dịch) - có thể cho phép bạn điều chỉnh mã của mình khi cần thiết. Nó là một gói thú vị!

Cả hai ngôn ngữ đều hỗ trợ từ khóa __asm, có nghĩa là bạn cũng có thể trộn mã ngôn ngữ lắp ráp của mình. Học C và C ++ sẽ giúp bạn trở thành một lập trình viên tròn tốt hơn.

Không nhất thiết phải luôn luôn liên kết với Trình biên dịch. Tôi đã đề cập đến nó bởi vì tôi nghĩ rằng sẽ giúp bạn hiểu rõ hơn. Hầu hết, hầu hết các cuộc gọi thư viện như vậy đều sử dụng các cuộc gọi / API hệ thống do Hệ điều hành cung cấp (lần lượt HĐH thực hiện các công cụ tương tác phần cứng).


10

Làm thế nào mà C ++ ... đột nhiên có được những khả năng như vậy thông qua các thư viện được viết bằng chính C ++?

Không có gì kỳ diệu về việc sử dụng các thư viện khác. Thư viện là những túi lớn đơn giản của các chức năng mà bạn có thể gọi.

Hãy xem xét bản thân bạn viết một chức năng như thế này

void addExclamation(std::string &str)
{
    str.push_back('!');
}

Bây giờ nếu bạn bao gồm tập tin đó bạn có thể viết addExclamation(myVeryOwnString);. Bây giờ bạn có thể hỏi, "làm thế nào mà C ++ đột nhiên có được khả năng thêm các dấu chấm than vào một chuỗi?" Câu trả lời rất dễ: bạn đã viết một hàm để làm điều đó sau đó bạn gọi nó.

Vì vậy, để trả lời câu hỏi của bạn về cách C ++ có thể có khả năng vẽ các cửa sổ thông qua các thư viện được viết bằng C ++, câu trả lời là như nhau. Một số người khác đã viết (các) hàm để làm điều đó, sau đó biên dịch chúng và đưa chúng cho bạn dưới dạng thư viện.

Các câu hỏi khác trả lời cách hoạt động của bản vẽ cửa sổ, nhưng bạn có vẻ bối rối về cách các thư viện hoạt động nên tôi muốn giải quyết phần cơ bản nhất của câu hỏi của bạn.


8

Điều quan trọng là khả năng hệ điều hành phơi bày API và mô tả chi tiết về cách sử dụng API này.

Hệ điều hành cung cấp một bộ API với các quy ước gọi. Quy ước gọi là xác định cách đưa ra một tham số vào API và cách trả về kết quả và cách thực hiện cuộc gọi thực tế.

Các hệ điều hành và trình biên dịch tạo mã cho chúng chơi độc đáo với nhau, vì vậy bạn thường không phải suy nghĩ về nó, chỉ cần sử dụng nó.


7

Không cần một cú pháp đặc biệt để tạo các cửa sổ. Tất cả những gì được yêu cầu là HĐH cung cấp API để tạo các cửa sổ. Một API như vậy bao gồm các lệnh gọi hàm đơn giản mà C ++ cung cấp cú pháp.

Hơn nữa, C và C ++ được gọi là ngôn ngữ lập trình hệ thống và có thể truy cập các con trỏ tùy ý (phần cứng có thể được ánh xạ tới một số thiết bị bởi phần cứng). Ngoài ra, cũng khá đơn giản để gọi các hàm được định nghĩa trong lắp ráp, cho phép phạm vi hoạt động đầy đủ mà bộ xử lý cung cấp. Do đó, có thể tự viết một HĐH bằng C hoặc C ++ và một số lượng nhỏ lắp ráp.

Cũng nên đề cập rằng Qt là một ví dụ tồi, vì nó sử dụng một trình biên dịch meta được gọi là để mở rộng cú pháp của C ++. Tuy nhiên, điều này không liên quan đến khả năng gọi các API do HĐH cung cấp để thực sự vẽ hoặc tạo các cửa sổ.


7

Đầu tiên, tôi nghĩ có một chút hiểu lầm

Làm thế nào để C ++, mà trước đây không có cú pháp có khả năng yêu cầu HĐH cho một cửa sổ hoặc một cách để giao tiếp qua các mạng

Không có cú pháp để thực hiện các hoạt động hệ điều hành. Đó là câu hỏi về ngữ nghĩa .

đột nhiên có được những khả năng như vậy thông qua các thư viện được viết bằng C ++

Chà, hệ điều hành được viết chủ yếu bằng C. Bạn có thể sử dụng các thư viện dùng chung (vì vậy, dll) để gọi mã bên ngoài. Ngoài ra, mã hệ điều hành có thể đăng ký các thói quen hệ thống trên các tòa nhà cao tầng * hoặc các ngắt mà bạn có thể gọi bằng cách sử dụng lắp ráp . Các thư viện chia sẻ đó thường chỉ làm cho hệ thống đó gọi cho bạn, vì vậy bạn không cần phải sử dụng lắp ráp nội tuyến.

Đây là hướng dẫn tuyệt vời về điều đó: http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
Nó dành cho Linux, nhưng các nguyên tắc là như nhau.

Làm thế nào hệ điều hành đang hoạt động trên card đồ họa, card mạng vv? Đó là một chủ đề rất rộng, nhưng chủ yếu bạn cần truy cập các ngắt, cổng hoặc ghi một số dữ liệu vào vùng nhớ đặc biệt. Vì các hoạt động đó được bảo vệ, bạn vẫn cần gọi chúng thông qua hệ điều hành.


7

Trong nỗ lực cung cấp một cái nhìn hơi khác với các câu trả lời khác, tôi sẽ trả lời như thế này.

(Tuyên bố miễn trừ trách nhiệm: Tôi đơn giản hóa mọi thứ một chút, tình huống tôi đưa ra hoàn toàn là giả thuyết và được viết như một phương tiện để chứng minh các khái niệm hơn là đúng 100% với cuộc sống).

Hãy nghĩ về những điều từ quan điểm khác, hãy tưởng tượng bạn vừa viết một hệ điều hành đơn giản với các khả năng quản lý bộ nhớ, cửa sổ và quản lý bộ nhớ cơ bản. Bạn muốn triển khai thư viện C ++ để cho phép người dùng lập trình trong C ++ và thực hiện những việc như tạo cửa sổ, vẽ lên cửa sổ, v.v. Câu hỏi là, làm thế nào để làm điều này.

Đầu tiên, vì C ++ biên dịch thành mã máy, bạn cần xác định cách sử dụng mã máy để giao tiếp với C ++. Đây là nơi các hàm xuất hiện, các hàm chấp nhận các đối số và đưa ra các giá trị trả về, do đó chúng cung cấp một cách truyền dữ liệu tiêu chuẩn giữa các phần khác nhau của mã. Họ làm điều này bằng cách thiết lập một cái gì đó gọi là quy ước gọi .

Một quy ước gọi cho biết vị trí và cách đặt các đối số trong bộ nhớ để một hàm có thể tìm thấy chúng khi nó được thực thi. Khi một chức năng được gọi, chức năng gọi sẽ đặt các đối số vào bộ nhớ và sau đó yêu cầu CPU chuyển sang chức năng khác, nơi nó thực hiện công việc trước khi quay trở lại nơi nó được gọi. Điều này có nghĩa là mã được gọi có thể hoàn toàn là bất cứ thứ gì và nó sẽ không thay đổi cách gọi hàm. Tuy nhiên, trong trường hợp này, mã đằng sau chức năng sẽ có liên quan đến hệ điều hành và sẽ hoạt động trên trạng thái bên trong của hệ điều hành.

Vì vậy, nhiều tháng sau và bạn đã sắp xếp tất cả các chức năng HĐH của mình. Người dùng của bạn có thể gọi các chức năng để tạo các cửa sổ và vẽ lên chúng, họ có thể tạo ra các chủ đề và tất cả các loại điều tuyệt vời. Tuy nhiên, đây là vấn đề, các chức năng của HĐH của bạn sẽ khác với các chức năng của Linux hoặc các chức năng của Windows. Vì vậy, bạn quyết định bạn cần cung cấp cho người dùng một giao diện chuẩn để họ có thể viết mã di động. Đây là nơi mà QT đến.

Như bạn gần như chắc chắn biết, QT có vô số các lớp và hàm hữu ích để thực hiện các loại công việc mà hệ điều hành làm, nhưng theo cách xuất hiện độc lập với hệ điều hành cơ bản. Cách thức hoạt động này là QT cung cấp các lớp và hàm đồng nhất theo cách chúng xuất hiện cho người dùng, nhưng mã đằng sau các hàm này khác nhau đối với mỗi hệ điều hành. Ví dụ: QApplication :: close ALLWindows () của QT thực sự sẽ gọi chức năng đóng cửa sổ chuyên dụng của mỗi hệ điều hành tùy thuộc vào phiên bản được sử dụng. Trong Windows, rất có thể sẽ gọi CloseWindow (hwnd) trong khi trên hệ điều hành sử dụng Hệ thống cửa sổ X, nó có khả năng sẽ gọi XDestroyWindow (hiển thị, cửa sổ).

Rõ ràng, một hệ điều hành có nhiều lớp, tất cả đều phải tương tác thông qua các giao diện của nhiều loại. Có nhiều khía cạnh tôi thậm chí không chạm tới, nhưng để giải thích tất cả chúng sẽ mất một thời gian rất dài. Nếu bạn quan tâm hơn đến hoạt động bên trong của các hệ điều hành, tôi khuyên bạn nên kiểm tra hệ điều hành wiki wiki .

Mặc dù vậy, lý do nhiều hệ điều hành chọn hiển thị giao diện cho C / C ++ là vì chúng biên dịch thành mã máy, chúng cho phép các hướng dẫn lắp ráp được trộn lẫn với mã của riêng chúng và chúng cung cấp một mức độ tự do tuyệt vời cho lập trình viên.

Một lần nữa, có rất nhiều thứ đang diễn ra ở đây. Tôi muốn tiếp tục giải thích làm thế nào các thư viện như các tệp .so và. Không phải viết bằng C / C ++ và có thể được viết bằng ngôn ngữ lắp ráp hoặc các ngôn ngữ khác, nhưng tôi cảm thấy rằng nếu tôi thêm nữa viết toàn bộ bài viết và nhiều như tôi muốn làm là tôi không có trang web để lưu trữ nó.


6

Khi bạn cố gắng vẽ một cái gì đó trên màn hình, mã của bạn sẽ gọi một đoạn mã khác gọi một số mã khác (v.v.) cho đến khi cuối cùng có một "cuộc gọi hệ thống", đây là một lệnh đặc biệt mà CPU có thể thực thi. Các hướng dẫn này có thể được viết bằng cách lắp ráp hoặc có thể được viết bằng C ++ nếu trình biên dịch hỗ trợ "nội tại" của chúng (là các chức năng mà trình biên dịch xử lý "đặc biệt" bằng cách chuyển đổi chúng thành mã đặc biệt mà CPU có thể hiểu được). Công việc của họ là bảo hệ điều hành làm gì đó.

Khi một cuộc gọi hệ thống xảy ra, một chức năng được gọi sẽ gọi một chức năng khác (v.v.) cho đến khi cuối cùng trình điều khiển hiển thị được yêu cầu vẽ một cái gì đó trên màn hình. Tại thời điểm đó, trình điều khiển hiển thị nhìn vào một vùng cụ thể trong bộ nhớ vật lý thực sự không phải là bộ nhớ, mà là một dải địa chỉ có thể được ghi vào như thể đó là bộ nhớ. Tuy nhiên, thay vào đó, ghi vào phạm vi địa chỉ đó làm cho phần cứng đồ họa chặn bộ nhớ ghi và vẽ một cái gì đó trên màn hình.
Viết cho vùng nhớ này là điều có thể được mã hóa trong C ++, vì về phía phần mềm, nó chỉ là truy cập bộ nhớ thông thường. Chỉ là phần cứng xử lý nó khác nhau.
Vì vậy, đó là một lời giải thích thực sự cơ bản về cách nó có thể hoạt động.


4
Afaik một systemcall không thực sự là một hướng dẫn cpu và không liên quan gì đến nội tại. Đây là một chức năng của nhân hệ điều hành, giao tiếp với các thiết bị.
MatthiasB

3
@MatthiasB: Chà, bạn đã nhầm, vì syscall(và anh em họ của nó sysenter) thực sự là một hướng dẫn CPU.
dùng541686

2
Đây chỉ là một gợi ý để cải thiện câu trả lời của bạn, vì bản thân nó không rõ ràng. Đừng xem đó là cuộc tấn công cá nhân hay bất cứ điều gì.
MatthiasB

1
@MatthiasB: Tôi không dùng nó cá nhân. Tôi đang nói rằng tôi đã biết câu trả lời không thực sự chính xác 100% nhưng tôi nghĩ rằng đó là một sự đơn giản hóa đủ tốt để trả lời OP - vì vậy, nếu bạn thực sự biết cách viết câu trả lời tốt hơn thì hãy tự viết trả lời hoặc dành thời gian để chỉnh sửa của tôi. Tôi thực sự không có bất cứ điều gì để thêm vào mà tôi nghĩ là đáng giá, vì vậy nếu bạn muốn thấy một cái gì đó tốt hơn trên trang này, bạn sẽ phải tự nỗ lực.
dùng541686

3
Các cuộc gọi hệ thống được thực hiện bằng cách sử dụng các ngắt phần mềm. Các hướng dẫn như sysenterđường dẫn cuộc gọi được tối ưu hóa, vì chuyển đổi ngữ cảnh được sử dụng bởi các trình xử lý ngắt không nhanh như mọi người mong muốn, nhưng về cơ bản, đó vẫn là một phần mềm được tạo ra trong khi được xử lý bằng cách chuyển sang trình xử lý được cài đặt bởi kernel OS. Một phần của quy trình chuyển đổi ngữ cảnh mà IS thực hiện bằng cách sysenterthay đổi các bit chế độ trong bộ xử lý để đặt vòng 0 - truy cập đầy đủ vào tất cả các hướng dẫn, thanh ghi, vùng nhớ và I / O đặc quyền.
Ben Voigt

4

Chương trình C ++ của bạn đang sử dụng thư viện Qt (cũng được mã hóa bằng C ++). Thư viện Qt sẽ sử dụng chức năng Windows CreateWindowEx (được mã hóa bằng C bên trong kernel32.dll). Hoặc trong Linux, nó có thể đang sử dụng Xlib (cũng được mã hóa bằng C), nhưng nó cũng có thể gửi các byte thô mà trong giao thức X có nghĩa là " Hãy tạo một cửa sổ cho tôi ".

Liên quan đến câu hỏi Catch-22 của bạn là ghi chú lịch sử rằng trình biên dịch C ++ đầu tiên được viết bằng C ++, mặc dù thực tế nó là trình biên dịch C với một vài khái niệm C ++, đủ để nó có thể biên dịch phiên bản đầu tiên, sau đó có thể tự biên dịch .

Tương tự, trình biên dịch GCC sử dụng các phần mở rộng GCC: lần đầu tiên nó được biên dịch thành phiên bản sau đó được sử dụng để tự biên dịch lại. (Hướng dẫn xây dựng GCC)


2

Làm thế nào tôi thấy câu hỏi này thực sự là một câu hỏi biên dịch.

Nhìn vào nó theo cách này, bạn viết một đoạn mã trong hội (bạn có thể thực hiện bằng bất kỳ ngôn ngữ nào) dịch ngôn ngữ mới viết của bạn mà bạn muốn gọi Z ++ thành hội, vì đơn giản hãy gọi nó là trình biên dịch (nó là trình biên dịch) .

Bây giờ bạn cung cấp cho trình biên dịch này một số hàm cơ bản, để bạn có thể viết int, chuỗi, mảng, v.v. thực sự bạn cung cấp cho nó đủ khả năng để bạn có thể tự viết trình biên dịch trong Z ++. và bây giờ bạn có một trình biên dịch cho Z ++ được viết bằng Z ++, khá gọn gàng phải không.

Điều tuyệt vời hơn nữa là bây giờ bạn có thể thêm các khả năng cho trình biên dịch đó bằng các khả năng đã có, do đó mở rộng ngôn ngữ Z ++ với các tính năng mới bằng cách sử dụng các tính năng trước đó

Một ví dụ, nếu bạn viết đủ mã để vẽ pixel bằng bất kỳ màu nào, thì bạn có thể mở rộng nó bằng Z ++ để vẽ bất cứ thứ gì bạn muốn.


0

Phần cứng là những gì cho phép điều này xảy ra. Bạn có thể nghĩ bộ nhớ đồ họa là một mảng lớn (bao gồm mọi pixel trên màn hình). Để vẽ lên màn hình, bạn có thể ghi vào bộ nhớ này bằng C ++ hoặc bất kỳ ngôn ngữ nào cho phép truy cập trực tiếp vào bộ nhớ đó. Bộ nhớ đó chỉ có thể truy cập bằng hoặc nằm trên card đồ họa.

Trên các hệ thống hiện đại truy cập trực tiếp vào bộ nhớ đồ họa sẽ yêu cầu viết trình điều khiển vì nhiều hạn chế khác nhau nên bạn sử dụng các phương tiện gián tiếp. Các thư viện tạo một cửa sổ (thực sự chỉ là một hình ảnh như bất kỳ hình ảnh nào khác) và sau đó ghi hình ảnh đó vào bộ nhớ đồ họa mà GPU sau đó sẽ hiển thị trên màn hình. Không có gì phải được thêm vào ngôn ngữ ngoại trừ khả năng ghi vào các vị trí bộ nhớ cụ thể, đó là những gì con trỏ dành cho.


Điểm tôi đang cố gắng đưa ra là một ngôn ngữ không cần phải "mở rộng" theo nghĩa là một phiên bản mới của ngôn ngữ cần phải được viết lại, và nó không thực sự là một chương trình thú vị phải giao diện với phần cứng.
John
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.