Estou aprendendo C ++ e comecei a aprender sobre alguns dos recursos do Qt para codificar programas GUI. Eu me fiz a seguinte pergunta:
Como o C ++, que anteriormente não tinha sintaxe capaz de solicitar ao sistema operacional uma janela ou uma maneira de se comunicar através de redes (com APIs que eu também não entendo completamente, admito), de repente obtém esses recursos através de bibliotecas escritas em C ++? Tudo parece terrivelmente circular para mim. Quais instruções do C ++ você poderia criar nessas bibliotecas?
Sei que essa pergunta pode parecer trivial para um desenvolvedor de software experiente, mas estou pesquisando há horas sem encontrar nenhuma resposta direta. Chegou ao ponto em que não consigo seguir o tutorial sobre o Qt porque a existência de bibliotecas é incompreensível para mim.
fonte
Respostas:
Um computador é como uma cebola, tem muitas muitas camadas, a partir do núcleo interno de hardware puro para a camada mais externa aplicação. Cada camada expõe partes de si mesma para a próxima camada externa, para que a camada externa possa usar parte da funcionalidade das camadas internas.
No caso do Windows, por exemplo, o sistema operacional expõe a API WIN32 para aplicativos em execução no Windows. A biblioteca Qt usa essa API para fornecer aplicativos usando Qt para sua própria API. Você usa Qt, Qt usa WIN32, WIN32 usa níveis mais baixos do sistema operacional Windows e assim por diante, até que haja sinais elétricos no hardware.
fonte
Qt
aqui fornece uma abstração da camada abaixo dela, porque no LinuxQt
chama a API do Linux e não a API do WIN32.user32.dll
, ou possivelmentegdi32.dll
.Você está certo que, em geral, as bibliotecas não podem tornar possível qualquer coisa que ainda não seja possível.
Mas as bibliotecas não precisam ser escritas em C ++ para serem usadas por um programa em C ++. Mesmo que sejam escritos em C ++, eles podem usar internamente outras bibliotecas não escritas em C ++. Portanto, o fato de o C ++ não fornecer nenhuma maneira de fazê-lo não impede que ele seja adicionado, desde que haja alguma maneira de fazê-lo fora do C ++.
Em um nível bastante baixo, algumas funções chamadas por C ++ (ou C) serão gravadas em assembly, e o assembly contém as instruções necessárias para fazer o que não é possível (ou não é fácil) no C ++, por exemplo, para chamar uma função do sistema. Nesse ponto, essa chamada do sistema pode fazer tudo o que seu computador é capaz, simplesmente porque não há nada para impedi-lo.
fonte
C e C ++ têm duas propriedades que permitem toda essa extensibilidade de que o OP está falando.
No kernel ou em uma plataforma básica não protegida, periféricos como a porta serial ou a unidade de disco são mapeados no mapa de memória da mesma maneira que a RAM. A memória é uma série de comutadores e, alternando os comutadores do periférico (como uma porta serial ou driver de disco), o periférico faz coisas úteis.
Em um sistema operacional em modo protegido, quando se deseja acessar o kernel no espaço do usuário (digamos, ao gravar no sistema de arquivos ou desenhar um pixel na tela), é necessário fazer uma chamada do sistema. C não tem instruções para fazer chamadas de sistema, mas C pode chamar código do assembler que pode acionar a chamada correta do sistema. É isso que permite que o código C de uma pessoa fale com o kernel.
Para facilitar a programação de uma plataforma específica, as chamadas do sistema são agrupadas em funções mais complexas que podem desempenhar alguma função útil no próprio programa. Um é livre para ligar diretamente para o sistema (usando o assembler), mas provavelmente é mais fácil usar apenas uma das funções do wrapper que a plataforma fornece.
Há outro nível de API que é muito mais útil do que uma chamada do sistema. Tomemos, por exemplo, malloc. Isso não apenas chamará o sistema para obter grandes blocos de memória, mas também gerenciará essa memória, fazendo todo o registro contábil do que está ocorrendo.
APIs do Win32 agrupam algumas funcionalidades gráficas com um conjunto de widgets de plataforma comum. O Qt leva isso um pouco mais longe, envolvendo a API do Win32 (ou X Windows) de uma maneira multiplataforma.
Fundamentalmente, embora um compilador C transforme código C em código de máquina e, como o computador foi projetado para usar código de máquina, você deve esperar que C consiga realizar o compartilhamento do Lions ou o que um computador pode fazer. Tudo o que as bibliotecas de wrapper fazem é o trabalho pesado para você, para que você não precise.
fonte
Idiomas (como C ++ 11 ) são especificações , em papel, geralmente escritas em inglês. Veja o rascunho mais recente do C ++ 11 (ou compre as especificações finais caras do seu fornecedor ISO).
Você geralmente usa um computador com alguma implementação de linguagem (em princípio, você pode executar um programa C ++ sem nenhum computador, por exemplo, usando vários escravos humanos para interpretá-lo; isso seria antiético e ineficiente)
Sua implementação geral do C ++ funciona acima de algum sistema operacional e se comunica com ele (usando algum código específico da implementação , geralmente em alguma biblioteca do sistema). Geralmente essa comunicação é feita através de chamadas do sistema . Procure, por exemplo, em syscalls (2), uma lista de chamadas do sistema disponíveis no kernel do Linux .
Do ponto de vista do aplicativo, um syscall é uma instrução elementar da máquina, como
SYSENTER
no x86-64 com algumas convenções ( ABI )Na área de trabalho do Linux, as bibliotecas Qt estão acima das bibliotecas cliente X11 se comunicando com o servidor X11 Xorg através dos protocolos X Windows .
No Linux, use
ldd
no seu executável para ver a (longa) lista de dependências nas bibliotecas. Usepmap
no seu processo de execução para ver quais são "carregados" em tempo de execução. BTW, no Linux, seu aplicativo provavelmente está usando apenas software livre, você pode estudar seu código-fonte (de Qt, Xlib, libc, ... o kernel) para entender mais o que está acontecendofonte
Eu acho que o conceito que você está perdendo são chamadas de sistema . Cada sistema operacional fornece uma enorme quantidade de recursos e funcionalidades nas quais você pode aproveitar para executar tarefas relacionadas ao sistema operacional de baixo nível. Mesmo quando você chama uma função de biblioteca comum, provavelmente está fazendo uma chamada do sistema nos bastidores.
As chamadas do sistema são uma maneira de baixo nível de usar o poder do sistema operacional, mas podem ser complexas e complicadas de usar, por isso geralmente são "encapsuladas" nas APIs para que você não precise lidar com elas diretamente. Porém, por baixo, praticamente qualquer coisa que você faça que envolva recursos relacionados ao sistema operacional usará chamadas do sistema, incluindo impressão, rede e soquetes, etc.
No caso do Windows, o Microsoft Windows tem sua GUI gravada no kernel, portanto, há chamadas do sistema para criação de janelas, gráficos de pintura etc. Em outros sistemas operacionais, a GUI pode não fazer parte do kernel, nesse caso até onde eu sei, não haveria chamadas de sistema para coisas relacionadas à GUI e você só poderia trabalhar em um nível ainda mais baixo com quaisquer gráficos de baixo nível e chamadas relacionadas a entrada disponíveis.
fonte
Boa pergunta. Todo novo desenvolvedor de C ou C ++ tem isso em mente. Estou assumindo uma máquina x86 padrão para o restante deste post. Se você estiver usando o compilador Microsoft C ++, abra o seu bloco de notas e digite este (nomeie o arquivo Test.c)
E agora compile esse arquivo (usando o prompt de comando do desenvolvedor) cl Test.c /FaTest.asm
Agora abra Test.asm no seu bloco de notas. O que você vê é o código traduzido - C / C ++ é traduzido para assembler. Você entendeu a dica?
Os programas C / C ++ são projetados para rodar no metal. O que significa que eles têm acesso a hardware de nível inferior, o que facilita a exploração dos recursos do hardware. Digamos, eu vou escrever uma biblioteca C getch () em uma máquina x86.
Dependendo do assembler, eu digitaria algo assim:
Eu corro com um assembler e giro um .OBJ - Nomeie getch.obj.
Em seguida, escrevo um programa em C (não incluo nada)
Agora, nomeie esse arquivo - GetChTest.c. Compile esse arquivo passando getch.obj. (Ou compile individualmente para .obj e LINK GetChTest.Obj e getch.Obj juntos para produzir GetChTest.exe).
Execute GetChTest.exe e você descobrirá que aguarda a entrada do teclado.
A programação C / C ++ não é apenas sobre linguagem. Para ser um bom programador de C / C ++, você precisa entender bem o tipo de máquina que ele executa. Você precisará saber como o gerenciamento de memória é tratado, como os registros são estruturados, etc. Você pode não precisar de todas essas informações para programação regular - mas elas o ajudarão imensamente. Além do conhecimento básico de hardware, certamente ajuda se você entender como o compilador funciona (por exemplo, como ele se traduz) - o que pode permitir que você ajuste seu código conforme necessário. É um pacote interessante!
Ambos os idiomas suportam a palavra-chave __asm, o que significa que você também pode misturar o código da linguagem assembly. Aprender C e C ++ fará de você um programador arredondado melhor em geral.
Não é necessário sempre vincular ao Assembler. Eu mencionei isso porque pensei que isso ajudaria você a entender melhor. Principalmente, a maioria dessas chamadas de biblioteca utiliza chamadas / APIs do sistema fornecidas pelo sistema operacional (o sistema operacional, por sua vez, faz as coisas de interação do hardware).
fonte
Não há nada mágico em usar outras bibliotecas. Bibliotecas são simples sacos grandes de funções que você pode chamar.
Considere-se escrevendo uma função como esta
Agora, se você incluir esse arquivo, poderá escrever
addExclamation(myVeryOwnString);
. Agora você pode perguntar: "como o C ++ conseguiu repentinamente a capacidade de adicionar pontos de exclamação a uma string?" A resposta é fácil: você escreveu uma função para fazer isso e depois a chamou.Portanto, para responder sua pergunta sobre como o C ++ pode obter recursos para desenhar janelas através de bibliotecas escritas em C ++, a resposta é a mesma. Outra pessoa escreveu funções para fazer isso e, em seguida, compilou-as e deu-as a você na forma de uma biblioteca.
As outras perguntas respondem como o desenho da janela realmente funciona, mas você parecia confuso sobre como as bibliotecas funcionam, então eu queria abordar a parte mais fundamental da sua pergunta.
fonte
A chave é a possibilidade do sistema operacional expor uma API e uma descrição detalhada de como essa API deve ser usada.
O sistema operacional oferece um conjunto de APIs com convenções de chamada. A convenção de chamada está definindo a maneira como um parâmetro é fornecido na API e como os resultados são retornados e como executar a chamada real.
Os sistemas operacionais e os compiladores que criam o código para eles funcionam muito bem juntos, então você geralmente não precisa pensar nisso, apenas use-o.
fonte
Não há necessidade de uma sintaxe especial para criar janelas. Tudo o que é necessário é que o sistema operacional forneça uma API para criar janelas. Essa API consiste em chamadas de função simples para as quais o C ++ fornece sintaxe.
Além disso, C e C ++ são as chamadas linguagens de programação de sistemas e são capazes de acessar ponteiros arbitrários (que podem ser mapeados para algum dispositivo pelo hardware). Além disso, também é bastante simples chamar funções definidas na montagem, o que permite toda a gama de operações que o processador fornece. Portanto, é possível escrever um sistema operacional usando C ou C ++ e uma pequena quantidade de montagem.
Também deve ser mencionado que Qt é um mau exemplo, pois usa o chamado meta compilador para estender a sintaxe do C ++. No entanto, isso não está relacionado à capacidade de chamar as APIs fornecidas pelo sistema operacional para desenhar ou criar janelas.
fonte
Primeiro, há um pouco de mal entendido, eu acho
Não há sintaxe para executar operações do SO. É a questão da semântica .
Bem, o sistema operacional é gravado principalmente em C. Você pode usar bibliotecas compartilhadas (portanto, dll) para chamar o código externo. Além disso, o código do sistema operacional pode registrar rotinas do sistema em syscalls * ou interrupções que você pode chamar usando assembly . As bibliotecas compartilhadas costumam fazer esse sistema chamar você, para que você seja poupado usando o assembly embutido.
Aqui está o bom tutorial sobre isso: http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
É para Linux, mas os princípios são os mesmos.
Como o sistema operacional está realizando operações em placas gráficas, placas de rede etc? É um tema muito amplo, mas principalmente você precisa acessar interrupções, portas ou gravar alguns dados em uma região de memória especial. Como essas operações estão protegidas, você precisa chamá-las pelo sistema operacional de qualquer maneira.
fonte
Na tentativa de fornecer uma visão ligeiramente diferente para outras respostas, responderei dessa maneira.
(Isenção de responsabilidade: estou simplificando um pouco as coisas, a situação apresentada é puramente hipotética e foi escrita como um meio de demonstrar conceitos, em vez de ser 100% fiel à vida).
Pense nas coisas de outra perspectiva, imagine que você acabou de escrever um sistema operacional simples com recursos básicos de segmentação, janelas e gerenciamento de memória. Você deseja implementar uma biblioteca C ++ para permitir que os usuários programem em C ++ e façam coisas como criar janelas, atrair janelas etc. A questão é: como fazer isso.
Primeiro, como o C ++ é compilado no código da máquina, você precisa definir uma maneira de usar o código da máquina para fazer interface com o C ++. É aqui que as funções entram, as funções aceitam argumentos e fornecem valores de retorno, portanto, elas fornecem uma maneira padrão de transferir dados entre diferentes seções do código. Eles fazem isso estabelecendo algo conhecido como convenção de chamada .
Uma convenção de chamada indica onde e como os argumentos devem ser colocados na memória para que uma função possa encontrá-los quando for executada. Quando uma função é chamada, a função de chamada coloca os argumentos na memória e solicita à CPU que pule para a outra função, onde faz o que faz antes de voltar para onde foi chamada. Isso significa que o código que está sendo chamado pode ser absolutamente qualquer coisa e não muda como a função é chamada. Nesse caso, no entanto, o código por trás da função seria relevante para o sistema operacional e operaria no estado interno do sistema operacional.
Portanto, muitos meses depois, você terá todas as suas funções de sistema operacional resolvidas. Seu usuário pode chamar funções para criar janelas e desenhar nelas, pode criar threads e todo tipo de coisas maravilhosas. Mas aqui está o problema: as funções do seu sistema operacional serão diferentes das funções do Linux ou do Windows. Portanto, você decide fornecer ao usuário uma interface padrão para que ele possa escrever um código portátil. Aqui é onde entra o QT.
Como você quase certamente sabe, o QT possui muitas classes e funções úteis para realizar as tarefas que os sistemas operacionais fazem, mas de uma maneira que parece independente do sistema operacional subjacente. A maneira como isso funciona é que o QT fornece classes e funções uniformes na aparência para o usuário, mas o código por trás das funções é diferente para cada sistema operacional. Por exemplo, QApplication :: closeAllWindows () do QT estaria chamando a função de fechamento de janela especializada de cada sistema operacional, dependendo da versão usada. No Windows, provavelmente chamaria CloseWindow (hwnd), enquanto em um sistema operacional usando o X Window System, potencialmente chamaria XDestroyWindow (exibição, janela).
Como é evidente, um sistema operacional possui muitas camadas, todas as quais precisam interagir através de interfaces de várias variedades. Há muitos aspectos em que eu nem mencionei, mas explicar todos eles levaria muito tempo. Se você ainda estiver interessado no funcionamento interno dos sistemas operacionais, recomendo consultar o wiki do desenvolvedor do OS .
Lembre-se, porém, que a razão pela qual muitos sistemas operacionais optam por expor interfaces ao C / C ++ é que eles compilam com código de máquina, permitem que as instruções de montagem sejam misturadas com seu próprio código e oferecem um grande grau de liberdade ao programador.
Novamente, há muita coisa acontecendo aqui. Gostaria de continuar explicando como as bibliotecas como arquivos .so e .dll não precisam ser escritas em C / C ++ e podem ser escritas em assembly ou em outras linguagens, mas acho que, se eu adicionar mais, também devo escreva um artigo inteiro e, por mais que eu adorasse, não tenho um site para hospedá-lo.
fonte
Quando você tenta desenhar algo na tela, seu código chama outro código que chama outro código (etc.) até que finalmente haja uma "chamada do sistema", que é uma instrução especial que a CPU pode executar. Essas instruções podem ser escritas em assembly ou em C ++ se o compilador oferecer suporte a seus "intrínsecos" (que são funções que o compilador manipula "especialmente" convertendo-os em código especial que a CPU possa entender). O trabalho deles é dizer ao sistema operacional para fazer alguma coisa.
Quando ocorre uma chamada do sistema, é chamada uma função que chama outra função (etc.) até que, finalmente, o driver da tela seja instruído a desenhar algo na tela. Nesse ponto, o driver de vídeo examina uma região específica da memória física que na verdade não é memória, mas um intervalo de endereços que pode ser gravado como se fosse memória. Em vez disso, porém, a gravação nesse intervalo de endereços faz com que o hardware gráfico intercepte a gravação na memória e desenhe algo na tela.
Gravar nesta região da memória é algo que pode ser codificado em C ++, pois no lado do software é apenas um acesso regular à memória. Só que o hardware lida com isso de maneira diferente.
Portanto, essa é uma explicação realmente básica de como isso pode funcionar.
fonte
syscall
(e seu primosysenter
) é realmente uma instrução de CPU.sysenter
são caminhos de chamada otimizados, já que a alternância de contexto usada pelos manipuladores de interrupção não foi tão rápida quanto todos desejavam, mas fundamentalmente ainda é uma interrupção gerada por software enquanto é manipulada por vetorização para um manipulador instalado pelo kernel do sistema operacional. Parte do processo de alternância de contexto realizado pelo ISsysenter
é alterar os bits de modo no processador para definir o anel 0 - acesso total a todas as instruções privilegiadas, registros e áreas de memória e E / S.Seu programa C ++ está usando a biblioteca Qt (também codificada em C ++). A biblioteca Qt estará usando a função Windows CreateWindowEx (que foi codificada em C dentro do kernel32.dll). Ou no Linux, ele pode estar usando o Xlib (também codificado em C), mas também pode estar enviando os bytes brutos que no protocolo X significam " Por favor, crie uma janela para mim ".
Relacionada à sua pergunta catch-22, está a observação histórica de que “o primeiro compilador C ++ foi escrito em C ++”, embora na verdade fosse um compilador C com algumas noções de C ++, o suficiente para compilar a primeira versão, que poderia então se compilar. .
Da mesma forma, o compilador GCC usa extensões GCC: ele é compilado primeiro em uma versão e depois usado para se recompilar. (Instruções de criação do GCC)
fonte
Como eu vejo a pergunta, essa é realmente uma pergunta do compilador.
Veja desta maneira: você escreve um pedaço de código no Assembly (você pode fazê-lo em qualquer idioma) que traduz seu idioma recém-escrito que deseja chamar Z ++ em Assembly, para simplificar vamos chamá-lo de compilador (é um compilador) .
Agora você fornece a este compilador algumas funções básicas, para que você possa escrever int, string, matrizes etc. e agora você tem um compilador para o Z ++ escrito em Z ++, bem legal.
O que é ainda mais interessante é que agora você pode adicionar habilidades a esse compilador usando as habilidades já existentes, expandindo a linguagem Z ++ com novos recursos usando os recursos anteriores
Por exemplo, se você escrever código suficiente para desenhar um pixel em qualquer cor, poderá expandi-lo usando o Z ++ para desenhar o que quiser.
fonte
O hardware é o que permite que isso aconteça. Você pode pensar na memória gráfica como uma grande variedade (consistindo de todos os pixels na tela). Para desenhar na tela, você pode gravar nesta memória usando C ++ ou qualquer idioma que permita acesso direto a essa memória. Por acaso, essa memória está acessível ou localizada na placa gráfica.
Nos sistemas modernos, o acesso direto à memória gráfica exigiria a gravação de um driver devido a várias restrições, para que você use meios indiretos. Bibliotecas que criam uma janela (realmente apenas uma imagem como qualquer outra imagem) e depois gravam essa imagem na memória gráfica que a GPU exibe na tela. Nada precisa ser adicionado ao idioma, exceto a capacidade de gravar em locais específicos da memória, e é para isso que servem os ponteiros.
fonte