Saya belajar C ++ dan saya baru mulai belajar tentang beberapa kemampuan Qt untuk mengkode program GUI. Saya bertanya pada diri sendiri pertanyaan berikut:
Bagaimana C ++, yang sebelumnya tidak memiliki sintaks yang mampu meminta OS untuk jendela atau cara untuk berkomunikasi melalui jaringan (dengan API yang saya juga tidak mengerti, saya akui) tiba-tiba mendapatkan kemampuan seperti itu melalui perpustakaan yang ditulis dalam C ++ sendiri? Semua itu tampak melingkar bagiku. Instruksi C ++ apa yang bisa Anda buat di perpustakaan itu?
Saya menyadari pertanyaan ini mungkin tampak sepele bagi pengembang perangkat lunak yang berpengalaman tetapi saya telah meneliti selama berjam-jam tanpa menemukan respons langsung. Sudah sampai pada titik di mana saya tidak bisa mengikuti tutorial tentang Qt karena keberadaan perpustakaan tidak bisa dimengerti oleh saya.
sumber
Jawaban:
Komputer seperti bawang, ia memiliki banyak lapisan, dari inti perangkat keras murni hingga lapisan aplikasi terluar. Setiap lapisan memperlihatkan bagian dirinya ke lapisan luar berikutnya, sehingga lapisan luar dapat menggunakan beberapa fungsi lapisan dalam.
Dalam kasus misalnya Windows sistem operasi memperlihatkan apa yang disebut WIN32 API untuk aplikasi yang berjalan pada Windows. Pustaka Qt menggunakan API itu untuk menyediakan aplikasi yang menggunakan Qt ke APInya sendiri. Anda menggunakan Qt, Qt menggunakan WIN32, WIN32 menggunakan tingkat yang lebih rendah dari sistem operasi Windows, dan seterusnya sampai sinyal listrik di perangkat keras.
sumber
Qt
sini menyediakan abstraksi dari lapisan di bawahnya, karena di LinuxQt
memanggil API Linux dan bukan WIN32 API.user32.dll
, atau mungkingdi32.dll
.Anda benar bahwa secara umum, perpustakaan tidak dapat membuat apa pun yang mungkin belum terjadi.
Tetapi pustaka tidak harus ditulis dalam C ++ agar dapat digunakan oleh program C ++. Bahkan jika mereka ditulis dalam C ++, mereka mungkin secara internal menggunakan perpustakaan lain yang tidak ditulis dalam C ++. Jadi fakta bahwa C ++ tidak menyediakan cara untuk melakukannya tidak mencegahnya ditambahkan, asalkan ada beberapa cara untuk melakukannya di luar C ++.
Pada level yang cukup rendah, beberapa fungsi yang dipanggil oleh C ++ (atau oleh C) akan ditulis dalam assembly, dan assembly berisi instruksi yang diperlukan untuk melakukan apapun yang tidak mungkin (atau tidak mudah) di C ++, misalnya untuk memanggil fungsi sistem. Pada titik itu, panggilan sistem dapat melakukan apa pun yang mampu dilakukan komputer Anda, hanya karena tidak ada yang menghentikannya.
sumber
C dan C ++ memiliki 2 properti yang memungkinkan semua ekstensibilitas yang dibicarakan oleh OP ini.
Dalam kernel atau dalam platform mode dasar yang tidak dilindungi, periferal seperti port serial atau disk drive dipetakan ke dalam peta memori dengan cara yang sama seperti RAM. Memori adalah serangkaian sakelar dan membalik sakelar periferal (seperti port serial atau driver disk) membuat periferal Anda melakukan hal-hal yang bermanfaat.
Dalam sistem operasi mode terproteksi, ketika seseorang ingin mengakses kernel dari userspace (katakanlah ketika menulis ke sistem file atau menggambar piksel pada layar) seseorang perlu membuat panggilan sistem. C tidak memiliki instruksi untuk melakukan panggilan sistem tetapi C dapat memanggil kode assembler yang dapat memicu panggilan sistem yang benar, Inilah yang memungkinkan kode C seseorang berbicara dengan kernel.
Untuk membuat pemrograman platform tertentu lebih mudah, panggilan sistem dibungkus dengan fungsi yang lebih kompleks yang dapat melakukan beberapa fungsi yang berguna dalam program sendiri. Seseorang bebas untuk memanggil sistem panggilan secara langsung (menggunakan assembler) tetapi mungkin lebih mudah untuk hanya menggunakan salah satu fungsi pembungkus yang disediakan oleh platform.
Ada tingkat API lain yang jauh lebih berguna daripada panggilan sistem. Ambil contoh malloc. Ini tidak hanya akan memanggil sistem untuk mendapatkan blok memori besar tetapi juga akan mengelola memori ini dengan melakukan semua pembukuan pada apa yang terjadi.
Win32 API membungkus beberapa fungsionalitas grafik dengan satu set widget platform umum. Qt mengambil ini sedikit lebih jauh dengan membungkus Win32 (atau X Windows) API dengan cara lintas platform.
Meskipun pada dasarnya kompiler C mengubah kode C menjadi kode mesin dan karena komputer dirancang untuk menggunakan kode mesin, Anda harus mengharapkan C untuk dapat menyelesaikan bagian singa atau apa yang dapat dilakukan komputer. Semua yang dilakukan perpustakaan pembungkus adalah melakukan angkat berat untuk Anda sehingga Anda tidak perlu melakukannya.
sumber
Bahasa (seperti C ++ 11 ) adalah spesifikasi , di atas kertas, biasanya ditulis dalam bahasa Inggris. Lihat di dalam draft C ++ 11 terbaru (atau beli spec final mahal dari vendor ISO Anda).
Anda biasanya menggunakan komputer dengan beberapa implementasi bahasa (pada prinsipnya Anda dapat menjalankan program C ++ tanpa komputer apa pun, misalnya menggunakan sekelompok budak manusia yang menerjemahkannya; itu tidak etis dan tidak efisien)
Umum implementasi C ++ Anda berfungsi di atas beberapa sistem operasi dan berkomunikasi dengannya (menggunakan beberapa kode implementasi spesifik , sering di beberapa pustaka sistem). Umumnya komunikasi itu dilakukan melalui panggilan sistem . Cari misalnya ke syscalls (2) untuk daftar panggilan sistem yang tersedia di kernel Linux .
Dari sudut pandang aplikasi, syscall adalah instruksi mesin dasar seperti
SYSENTER
pada x86-64 dengan beberapa konvensi ( ABI )Di desktop Linux saya, pustaka Qt berada di atas pustaka klien X11 yang berkomunikasi dengan server X11 Xorg melalui protokol X Windows .
Di Linux, gunakan
ldd
pada executable Anda untuk melihat daftar dependensi (panjang) pada perpustakaan. Gunakanpmap
pada proses Anda berjalan untuk melihat mana yang "dimuat" saat runtime. BTW, di Linux, aplikasi Anda mungkin hanya menggunakan perangkat lunak gratis, Anda dapat mempelajari kode sumbernya (dari Qt, ke Xlib, libc, ... kernel) untuk lebih memahami apa yang terjadisumber
Saya pikir konsep yang Anda lewatkan adalah panggilan sistem . Setiap sistem operasi menyediakan sejumlah besar sumber daya dan fungsionalitas yang dapat Anda manfaatkan untuk melakukan hal-hal terkait sistem operasi tingkat rendah. Bahkan ketika Anda memanggil fungsi perpustakaan biasa, itu mungkin membuat panggilan sistem di belakang layar.
Panggilan sistem adalah cara tingkat rendah memanfaatkan kekuatan sistem operasi, tetapi bisa rumit dan rumit untuk digunakan, jadi sering "dibungkus" dalam API sehingga Anda tidak harus menghadapinya secara langsung. Namun di baliknya, hampir semua hal yang Anda lakukan yang melibatkan sumber daya terkait O / S akan menggunakan panggilan sistem, termasuk pencetakan, jaringan dan soket, dll.
Dalam kasus windows, Microsoft Windows memiliki GUI yang sebenarnya ditulis ke dalam kernel, sehingga ada panggilan sistem untuk membuat windows, melukis gambar, dll. Dalam sistem operasi lain, GUI mungkin bukan bagian dari kernel, dalam hal ini Sejauh yang saya tahu tidak akan ada panggilan sistem untuk hal-hal yang berhubungan dengan GUI, dan Anda hanya bisa bekerja pada level yang lebih rendah dengan grafis level rendah apa pun dan panggilan terkait input yang tersedia.
sumber
Pertanyaan bagus. Setiap pengembang C atau C ++ baru memikirkan hal ini. Saya mengasumsikan mesin x86 standar untuk sisa posting ini. Jika Anda menggunakan kompiler Microsoft C ++, buka notepad Anda dan ketik ini (beri nama file Test.c)
Dan sekarang kompilasi file ini (menggunakan prompt perintah pengembang) cl Test.c / FaTest.asm
Sekarang buka Test.asm di notepad Anda. Apa yang Anda lihat adalah kode yang diterjemahkan - C / C ++ diterjemahkan ke assembler. Apakah Anda mendapatkan petunjuknya?
Program C / C ++ dirancang untuk berjalan pada logam. Yang berarti mereka memiliki akses ke perangkat keras tingkat rendah yang membuatnya lebih mudah untuk mengeksploitasi kemampuan perangkat keras. Katakanlah, saya akan menulis getar C library () pada mesin x86.
Bergantung pada assembler, saya akan mengetik sesuatu dengan cara ini:
Saya menjalankannya dengan assembler dan menghasilkan .OBJ - Sebut saja getch.obj.
Saya kemudian menulis program C (saya tidak memasukkan apa pun)
Sekarang beri nama file ini - GetChTest.c. Kompilasi file ini dengan meneruskan getch.obj. (Atau kompilasi satu per satu untuk .obj dan LINK GetChTest.Obj dan getch.Obj bersama-sama untuk menghasilkan GetChTest.exe).
Jalankan GetChTest.exe dan Anda akan menunggu input keyboard.
Pemrograman C / C ++ bukan hanya tentang bahasa. Untuk menjadi programmer C / C ++ yang baik, Anda harus memiliki pemahaman yang baik tentang jenis mesin yang dijalankannya. Anda akan perlu tahu bagaimana manajemen memori ditangani, bagaimana register disusun, dll., Anda mungkin tidak memerlukan semua informasi ini untuk pemrograman reguler - tetapi mereka akan sangat membantu Anda. Terlepas dari pengetahuan perangkat keras dasar, tentu membantu jika Anda memahami cara kerja kompiler (mis., Bagaimana menerjemahkannya) - yang dapat memungkinkan Anda untuk mengubah kode Anda seperlunya. Ini adalah paket yang menarik!
Kedua bahasa mendukung kata kunci __as yang artinya Anda juga dapat mencampur kode bahasa majelis Anda. Belajar C dan C ++ akan membuat Anda menjadi programmer yang lebih baik secara keseluruhan.
Tidak perlu selalu terhubung dengan Assembler. Saya telah menyebutkannya karena saya pikir itu akan membantu Anda memahami lebih baik. Sebagian besar, sebagian besar panggilan perpustakaan seperti itu menggunakan panggilan sistem / API yang disediakan oleh Sistem Operasi (OS pada gilirannya melakukan hal-hal interaksi perangkat keras).
sumber
Tidak ada yang ajaib dengan menggunakan perpustakaan lain. Perpustakaan adalah kumpulan fungsi sederhana yang dapat Anda hubungi.
Anggap diri Anda menulis fungsi seperti ini
Sekarang jika Anda memasukkan file itu, Anda dapat menulis
addExclamation(myVeryOwnString);
. Sekarang Anda mungkin bertanya, "bagaimana C ++ tiba-tiba mendapatkan kemampuan untuk menambahkan tanda seru ke string?" Jawabannya mudah: Anda menulis fungsi untuk melakukan itu lalu Anda menyebutnya.Jadi untuk menjawab pertanyaan Anda tentang bagaimana C ++ bisa mendapatkan kemampuan untuk menggambar windows melalui pustaka yang ditulis dalam C ++, jawabannya adalah sama. Orang lain menulis fungsi untuk melakukan itu, dan kemudian menyusunnya dan memberikannya kepada Anda dalam bentuk perpustakaan.
Pertanyaan-pertanyaan lain menjawab bagaimana gambar jendela benar-benar bekerja, tetapi Anda terdengar bingung tentang bagaimana perpustakaan bekerja jadi saya ingin membahas bagian paling mendasar dari pertanyaan Anda.
sumber
Kuncinya adalah kemungkinan sistem operasi untuk mengekspos API dan deskripsi terperinci tentang bagaimana API ini digunakan.
Sistem operasi menawarkan satu set API dengan konvensi pemanggilan. Konvensi panggilan mendefinisikan cara parameter diberikan ke API dan bagaimana hasilnya dikembalikan dan bagaimana menjalankan panggilan yang sebenarnya.
Sistem operasi dan kompiler yang membuat kode untuk mereka bermain bersama dengan baik, jadi Anda biasanya tidak harus memikirkannya, cukup gunakan.
sumber
Tidak perlu sintaks khusus untuk membuat windows. Yang diperlukan hanyalah bahwa OS menyediakan API untuk membuat windows. API semacam itu terdiri dari panggilan fungsi sederhana yang disediakan oleh C ++ untuk sintaksis.
Lebih jauh, C dan C ++ disebut bahasa pemrograman sistem dan dapat mengakses pointer arbitrer (yang mungkin dipetakan ke beberapa perangkat oleh perangkat keras). Selain itu, ini juga cukup sederhana untuk memanggil fungsi-fungsi yang ditentukan dalam perakitan, yang memungkinkan berbagai operasi yang disediakan prosesor. Karena itu dimungkinkan untuk menulis OS itu sendiri menggunakan C atau C ++ dan sejumlah kecil perakitan.
Hal ini juga harus disebutkan bahwa Qt adalah contoh buruk, karena menggunakan apa yang disebut meta compiler untuk memperpanjang C ++' sintaks. Namun ini tidak terkait dengan kemampuannya untuk memanggil ke dalam API yang disediakan oleh OS untuk benar-benar menggambar atau membuat windows.
sumber
Pertama, ada sedikit kesalahpahaman, saya pikir
Tidak ada sintaks untuk melakukan operasi OS. Ini pertanyaan tentang semantik .
Nah, sistem operasinya sebagian besar ditulis dalam C. Anda dapat menggunakan shared library (jadi, dll) untuk memanggil kode eksternal. Selain itu, kode sistem operasi dapat mendaftarkan rutin sistem pada syscalls * atau interupsi yang dapat Anda panggil menggunakan rakitan . Pustaka bersama yang sering membuat sistem memanggil Anda, jadi Anda terhindar menggunakan perakitan inline.
Inilah tutorial yang bagus tentang itu: http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
Ini untuk Linux, tetapi prinsip-prinsipnya sama.
Bagaimana sistem operasi melakukan operasi pada kartu grafis, kartu jaringan dll? Ini adalah tema yang sangat luas, tetapi sebagian besar Anda perlu mengakses interupsi, port atau menulis beberapa data ke wilayah memori khusus. Karena operasi itu dilindungi, Anda harus memanggil mereka melalui sistem operasi.
sumber
Dalam upaya memberikan pandangan yang sedikit berbeda dengan jawaban lain, saya akan menjawab seperti ini.
(Penafian: Saya menyederhanakan sedikit hal, situasi yang saya berikan adalah murni hipotetis dan ditulis sebagai sarana untuk menunjukkan konsep daripada 100% benar untuk kehidupan).
Pikirkan hal-hal dari sudut pandang lain, bayangkan Anda baru saja menulis sebuah sistem operasi sederhana dengan kemampuan dasar threading, windowing dan manajemen memori. Anda ingin menerapkan pustaka C ++ untuk membiarkan pengguna memprogram dalam C ++ dan melakukan hal-hal seperti membuat windows, menggambar ke windows dll. Pertanyaannya adalah, bagaimana melakukan ini.
Pertama, karena C ++ mengkompilasi ke kode mesin, Anda perlu menentukan cara untuk menggunakan kode mesin untuk berinteraksi dengan C ++. Di sinilah fungsi masuk, fungsi menerima argumen dan memberikan nilai balik, sehingga memberikan cara standar untuk mentransfer data antara berbagai bagian kode. Mereka melakukan ini dengan menetapkan sesuatu yang dikenal sebagai konvensi pemanggilan .
Sebuah konvensi menelepon negara di mana dan bagaimana argumen harus ditempatkan di memori sehingga fungsi dapat menemukan mereka ketika dijalankan. Ketika suatu fungsi dipanggil, fungsi panggilan menempatkan argumen dalam memori dan kemudian meminta CPU untuk beralih ke fungsi lainnya, di mana ia melakukan apa yang dilakukannya sebelum melompat kembali ke tempat ia dipanggil. Ini berarti bahwa kode yang dipanggil dapat berupa apa saja dan itu tidak akan mengubah cara fungsi dipanggil. Namun dalam hal ini, kode di belakang fungsi akan relevan dengan sistem operasi dan akan beroperasi pada kondisi internal sistem operasi.
Jadi, berbulan-bulan kemudian dan Anda memiliki semua fungsi OS Anda beres. Pengguna Anda dapat memanggil fungsi untuk membuat windows dan menggambar di atasnya, mereka dapat membuat utas dan segala macam hal indah. Inilah masalahnya, fungsi OS Anda akan berbeda dengan fungsi Linux atau fungsi Windows. Jadi, Anda memutuskan untuk memberikan antarmuka standar kepada pengguna agar mereka dapat menulis kode portabel. Di sinilah QT masuk.
Seperti yang hampir pasti Anda ketahui, QT memiliki banyak kelas dan fungsi yang berguna untuk melakukan hal-hal yang dilakukan sistem operasi, tetapi dengan cara yang tampak independen dari sistem operasi yang mendasarinya. Cara kerjanya adalah bahwa QT menyediakan kelas dan fungsi yang seragam dalam penampilannya bagi pengguna, tetapi kode di belakang fungsi berbeda untuk setiap sistem operasi. Sebagai contoh, QApplication QT :: closeAllWindows () akan memanggil fungsi penutupan jendela khusus setiap sistem operasi tergantung pada versi yang digunakan. Di Windows kemungkinan besar akan memanggil CloseWindow (hwnd) sedangkan pada os menggunakan Sistem X Window, itu berpotensi akan memanggil XDestroyWindow (tampilan, jendela).
Seperti terbukti, sistem operasi memiliki banyak lapisan, yang semuanya harus berinteraksi melalui antarmuka banyak varietas. Ada banyak aspek yang belum saya sentuh, tetapi untuk menjelaskan semuanya, akan memakan waktu yang sangat lama. Jika Anda lebih tertarik dengan cara kerja sistem operasi dalam, saya sarankan memeriksa wiki dev OS .
Ingatlah bahwa alasan banyak sistem operasi memilih untuk mengekspos antarmuka ke C / C ++ adalah karena mereka mengkompilasi ke kode mesin, mereka mengizinkan instruksi perakitan untuk dicampur dengan kode mereka sendiri dan mereka memberikan tingkat kebebasan yang besar kepada programmer.
Sekali lagi, ada banyak hal yang terjadi di sini. Saya ingin terus menjelaskan bagaimana perpustakaan seperti file .so dan .dll tidak harus ditulis dalam C / C ++ dan dapat ditulis dalam bahasa assembly atau bahasa lain, tetapi saya merasa bahwa jika saya menambahkan lagi saya mungkin juga akan tulis seluruh artikel, dan sebanyak yang saya ingin lakukan, saya tidak punya situs untuk menyimpannya.
sumber
Ketika Anda mencoba menggambar sesuatu di layar, kode Anda memanggil beberapa kode lain yang memanggil beberapa kode lain (dll.) Sampai akhirnya ada "system call", yang merupakan instruksi khusus yang dapat dijalankan CPU. Instruksi-instruksi ini dapat ditulis dalam rakitan atau dapat ditulis dalam C ++ jika kompiler mendukung "intrinsik" mereka (yang merupakan fungsi yang ditangani kompiler "secara khusus" dengan mengubahnya menjadi kode khusus yang dapat dimengerti CPU). Tugas mereka adalah memberi tahu sistem operasi untuk melakukan sesuatu.
Ketika panggilan sistem terjadi, suatu fungsi dipanggil yang memanggil fungsi lain (dll.) Sampai akhirnya driver layar disuruh menggambar sesuatu di layar. Pada saat itu, driver tampilan melihat wilayah tertentu dalam memori fisik yang sebenarnya bukan memori, melainkan rentang alamat yang dapat ditulis seolah-olah itu adalah memori. Namun, sebaliknya, menulis ke kisaran alamat itu menyebabkan perangkat keras grafis menyadap penulisan memori, dan menggambar sesuatu di layar.
Menulis ke wilayah memori ini adalah sesuatu yang bisa. dikodekan dalam C ++, karena di sisi perangkat lunak itu hanya akses memori biasa. Hanya saja perangkat keras menanganinya secara berbeda. Itulah
penjelasan mendasar tentang cara kerjanya.
sumber
syscall
(dan sepupunyasysenter
) memang merupakan instruksi CPU.sysenter
adalah jalur panggilan yang dioptimalkan, karena pengalihan konteks yang digunakan oleh penangan interupsi tidak secepat yang diinginkan semua orang, tetapi pada dasarnya itu masih berupa peranti lunak yang dihasilkan interupsi sementara ditangani dengan cara vektor ke penangan yang dipasang oleh kernel OS. Bagian dari proses switching konteks yang dilakukan ISsysenter
adalah mengubah bit mode dalam prosesor untuk mengatur dering 0 - akses penuh ke semua instruksi, register, dan memori dan area I / O yang istimewa.Program C ++ Anda menggunakan pustaka Qt (juga diberi kode dalam C ++). Pustaka Qt akan menggunakan fungsi Windows CreateWindowEx (yang dikodekan dalam C di dalam kernel32.dll). Atau di Linux mungkin menggunakan Xlib (juga dikodekan dalam C), tetapi bisa juga mengirim byte mentah yang dalam protokol X berarti " Tolong buat jendela untuk saya ".
Terkait dengan tangkapan Anda -22 pertanyaan adalah catatan historis bahwa "kompiler C ++ pertama ditulis dalam C ++", meskipun sebenarnya itu adalah kompiler C dengan beberapa gagasan C ++, cukup sehingga dapat mengkompilasi versi pertama, yang kemudian dapat mengkompilasi sendiri .
Demikian pula, kompiler GCC menggunakan ekstensi GCC: ia pertama kali dikompilasi ke versi kemudian digunakan untuk mengkompilasi ulang dirinya. (Instruksi pembuatan GCC)
sumber
Bagaimana saya melihat pertanyaan ini sebenarnya adalah pertanyaan kompiler.
Lihatlah dengan cara ini, Anda menulis sepotong kode dalam Assembly (Anda dapat melakukannya dalam bahasa apa pun) yang menerjemahkan bahasa yang baru Anda tulis, Anda ingin memanggil Z ++ menjadi Assembly, untuk kesederhanaan, sebut saja itu kompilator (kompiler) .
Sekarang Anda memberikan kompiler ini beberapa fungsi dasar, sehingga Anda dapat menulis int, string, array dll sebenarnya Anda memberikannya kemampuan yang cukup sehingga Anda dapat menulis kompiler itu sendiri di Z ++. dan sekarang Anda memiliki kompiler untuk Z ++ yang ditulis dalam Z ++, cukup rapi.
Yang lebih keren lagi adalah sekarang Anda dapat menambahkan kemampuan ke kompiler itu menggunakan kemampuan yang sudah dimilikinya, sehingga memperluas bahasa Z ++ dengan fitur-fitur baru dengan menggunakan fitur-fitur sebelumnya.
Contoh, jika Anda menulis kode yang cukup untuk menggambar piksel dalam warna apa pun, maka Anda dapat mengembangkannya menggunakan Z ++ untuk menggambar apa pun yang Anda inginkan.
sumber
Perangkat keraslah yang memungkinkan ini terjadi. Anda dapat menganggap memori grafis sebagai susunan besar (terdiri dari setiap piksel pada layar). Untuk menggambar di layar Anda dapat menulis ke memori ini menggunakan C ++ atau bahasa apa pun yang memungkinkan akses langsung ke memori itu. Memori itu kebetulan dapat diakses oleh atau terletak di kartu grafis.
Pada sistem modern yang mengakses memori grafis secara langsung akan memerlukan penulisan driver karena berbagai pembatasan sehingga Anda menggunakan cara tidak langsung. Perpustakaan yang membuat jendela (benar-benar hanya gambar seperti gambar lainnya) dan kemudian menulis gambar itu ke memori grafis yang kemudian ditampilkan oleh GPU di layar. Tidak ada yang harus ditambahkan ke bahasa kecuali kemampuan untuk menulis ke lokasi memori tertentu, yang adalah untuk apa pointer.
sumber