S.O. Alto nível (extraído do fórum Assuntos Gerais)

Discussão sobre linux para plataformas Intel x86 ou x64 (PC)

Moderadores: 51, guest2003, Renie, gpenga

S.O. Alto nível (extraído do fórum Assuntos Gerais)

Mensagempor Renie » 10 Fev 2011 11:07

Olá pessoALL,

Este topico contém uma parte extraída do fórum Assuntos Gerais, com
informações interessantes e fixado para que não sejam perdidas.

Post Original do Fabin

Pessoal. Comprei o livro sobre linux que me foi indicado aqui no forum, e ja estou na pagina 93.
Entrando nos sites indicados, lendo sobre outras coisas, e dislumbrado sobre coisas que eu nem sonhava que existiam.
Me aprofundei em um site que encontrei e comecei a ler sobre SO LINUX,e BINARIO.

Em um uC, se eu fizer um programa para uma dada aplicação. Onde este eu sei que pelo pragma eu disse que rom começava no endereço 0x00ff. O montador vai assimilar e gerar o asm/hex substituindo labels e tals, por endereços baseando que o ofset é 0x00ff....
Se eu por acaso gravar este hex no endereço 0x00f0, ja f*** todo o sistema.

Agora em um sistema operativo, como o SO ou no caso o kernel do linux trava um bin ? lembrando que o local onde ele carrega os bins de programas é dinamico.

Eu imagino que.: Existe um disassembly, este mesmo disassembly faz uma conversão de endereços nas chamadas e labels conhecidas, ja que este kernel ou SO ja sabe onde vai começar o programa na memoria RAM.

Eu imagino 2.: Que os bins são como se fossem rotinas, e no final da dita existe o comando return. O que é passado então para o SO ou o task mananger é apenas um endereço desta rotina, onde esta não possui CALL´S apenas execução em maquina de estados.

Eu imagino 3.: Que só disse m***, e não tem nada haver o que eu disse com o que realmente acontece !!

Se alguém puder me dar mais este impurrão sobre como funciona isto, ...
_________________
Sermos mais inteligêntes que as outras pessoas, não é errar e aprender certo!!!!!
Ser mais inteligente que os outros, é aprender com o erro dos outros, e observar o que é certo e errado!! E assim nos esforçarmos para acertar sempre!!!
Editado pela última vez por Renie em 10 Fev 2011 12:25, em um total de 1 vez.
Renie
Word
 
Mensagens: 732
Registrado em: 11 Out 2006 22:35
Localização: RJ - Niterói - Brasil

Re: S.O. Alto nível (extraído do fárum Assuntos Gerais)

Mensagempor Renie » 10 Fev 2011 11:12

Post do Polesapart

Seguinte:


O processo de linkedição de um binário *dinâmico* (o padrão no linux) é um processo complicadinho.

Em primeiro lugar, o formato do executável do linux é o ELF. O ELF é um formato bastante versátil, cheio de cabeçalhos, não é simplesmente um arquivo binário que o sistema carrega pra memória e manda ver.

A linkedição de um binário dinâmico ocorre em duas etapas: quando você compila um programa e faz a linkedição final pra gerar o arquivo elf, o que na verdade ocorre é que o linker agrupa os arquivos objeto e gera o arquivo binário, mas não coloca junto as bibliotecas. Ao invés disto, ele cria uma secção no arquivo ELF contendo os nomes dos símbolos não resolvidos e cria as chamadas tabelas de relocação, que podemos pensar que são tabelas de ponteiros para símbolos, que fazem referência aquela outra tabela de nomes.

Feito isto tudo e mais umas coisinhas, está completa a primeira etapa da linkedição, e você tem o seu executável com todo o código compilado, exceto as dependências dinâmicas, que via de regra são as bibliotecas (a própria libc é uma). Nada te impediria também de linkar bibliotecas estáticas, neste caso elas são agregadas ao executável, normalmente, como ocorreria em qualquer linker que você já trabalhou antes. Dá até pra passar a opção -static para o linker, e aí ele degenera em um linker burrinho que não faz nenhuma dessas mágicas e gera um arquivo ELF que pra todos os efeitos é quase tão tapado quanto um .bin (ok, tou exagerando, mas só um pouco).

A segunda etapa ocorre em tempo de execução: quando você decide executar o arquivo elf que você criou, o que acaba ocorrendo é que o shell (ou outro processo qualquer ao qual voce deu a ordem para que execute o programa) faz um fork() de si mesmo, criando um processo filho, e este processo filho faz um exec() contra o executável que você forneceu (isto tem a ver com o fato de, no unix, todo processo ter um processo pai, a excessão do init, mas isto está fora do contexto aqui. No ruindows é diferente, no ruindows um processo manda o kernel criar um novo processo e ele faz isso sozinho).

O que ocorre é que a chamada de sistema exec() faz com que o processo filho seja "limpo", ou seja, na prática é como se fosse um novo processo, e então o kernel substitui a imagem do processo por aquela do arquivo executável que foi passado como parametro pro exec.

Pra fazer esta última etapa, o kernel, que é espertinho, olha o cabeçalho do arquivo e descobre que é um arquivo ELF dinâmico. O kernel não sabe carregar um arquivo ELF dinâmico sozinho, então no próprio arquivo ELF existe um secção que aponta para um programa auxiliar, o dynamic linker. É um simples texto com o caminho do executável, geramente /lib/ld-linux.so.2 ou algo que o valha.

O kernel então carrega este /lib/ld-linux.so.2 , que é um executável ELF estático, para uma área de memória que é sempre alocada inicialmente.

Mas onde fica esta área de memória? Fisicamente, não importa: cada processo no linux usa endereçamento virtual de memória, então o processo pensa que tem (quase) todo o espaço de endereçamento de 4GB (32 bits) pra ele.

O kernel então executa o código nesta área de memória. O dynamic linker então começa a sua brincadeira: ele começa a escarafunchar os cabeçalhos do arquivo ELF e a carregar secções para a memória. Como existe apenas uma área de memória inicialmente alocada pelo kernel, o que o dynamic linker faz é criar novas, e setar suas propriedades, usando a chamada de sistema mmap() (neste ponto, se fosse um executável estático, o kernel estaria fazendo esta etapa sozinho).

Então quando ele achou uma secção de dados somente leitura, ele a carrega para a memória e marca seus atributos como área com permissão apenas de leitura. Quando acha uma secção de código executável, ele marca como permissão para leitura e execução, e assim por diante, sendo que certas áreas de dados terão permissão pra gravação. Se o teu programa, quando for executar, desrespeitar estas permissões, o kernel dará um chute no traseiro dele, mandando um sinal SIGSEGV ou equivalente, e terminando o processo.

Mas teu programa não tá pronto pra executar ainda. O dynamic linker descobre que existem dependências para bibliotecas dinamicas, entao o que ele faz é ir atrás do arquivo mencionado no cabeçalho do teu arquivo ELF, por exemplo, procurando o nome libc.so.6 num pequeno banco de dados que ele possui, e achando o arquivo /lib/libc-2.3.2.so como sendo o responsavel por prover este nome, e então o dynamic linker faz a carga desta biblioteca para uma área da memória, e aí descobre que esta biblioteca tem dependencias para outras, e vai fazendo o processo recursivamente, até resolver todas as pendencias.

Durante este processo, ele analisa o teu programa, consultando a secção de relocações não resolvidas, e descobre que os símbolos que o teu programa precisa são providos por certas bibliotecas, e assim sucessivamente, colocando nas tabelas de relocações ponteiros para os endereços onde ele carregou os símbolos das bibliotecas.

Basicamente, ele fez boa parte do que o linker estático varia em tempo de linkedição, e tem que fazer tudo isso pra cada ELF dinâmico que for carregado.

Mas em que endereço da memória virtual ele carregas as bibliotecas? geralmente, ele vai alocando os endereços a medida em que carrega as bibliotecas, sendo que eles podem até ser de certa forma randomizados.

O mapa de endereços alocados ao teu executável costuma ser fixo, mas as bibliotecas podem ir parar em faixas de memória diferentes, razão pela qual em certas arquiteturas, elas precisam ser compiladas com a opção PIC (position-independent code), de modo que endereços absolutos não sejam usados diretamente.

Existem algumas sutilezas neste processo, mas em linhas gerais, é isso.
_________________
Warning: time of day goes back (-163479us), taking countermeasures. Smile
Renie
Word
 
Mensagens: 732
Registrado em: 11 Out 2006 22:35
Localização: RJ - Niterói - Brasil

Re: S.O. Alto nível (extraído do fárum Assuntos Gerais)

Mensagempor Renie » 10 Fev 2011 11:17

Post do Polesapart
Dá pra acrescentar mais umas coisinhas ...

Quando eu disse que ele carrega pra memória, na verdade ele faz o mmap() do arquivo, as páginas ("pedaços") de todos os arquivos ELF, até mesmo do próprio executável, só vão realmente ser carregados pra memória quando forem lidos ou executados pela primeira vez (afinal, quem gosta de trabalhar a toa são os vírus do windows!). Por padrão a página tem tamanho de 4kbytes, mas isto varia de arquitetura pra arquitetura.

Tendo dito isto, podemos entrar no "por que ele faz essa p*** toda? não seria mais fácil linkeditar tudo estaticamente"?

Seria, mas não seria tão eficiente, do ponto de vista do uso de recursos: não é a toa que no unix as bibliotecas dinâmicas são apelidadas de DSOs, que é a sigla pra Dynamic *shared* objects: quando a libc por exemplo é carregada, as páginas de memória fisica que efetivamente forem carregadas pra memória poderão ser compartilhadas por todos os processos: se vc tem 1000 processos, ao invés de ter mil cópias de printf(), getpid(), fork(), exec(), motherfucker() (ok essa última eu inventei), etc., você acaba tendo só uma.

Isto ocorre pq supersimplificando, o sistema de memória virtual consiste numa tabela que faz a translação entre endereços físicos e virtuais. Um endereço virtual num processo pode ser bem diferente do outro, e apontar pra mesma página de memória física. O linux inclusive implementa um mecanismo chamado copy-on-write: se dois processos fizeram mmap() da mesma origem (um arquivo por exemplo), as páginas na memória que se referem a esta origem serão compartilhadas (leia-se: serão a mesma página de memória física efetivamente alocada) até que um dos processo "suje" a página, tentando gravar nela: quando isto ocorre, o kernel recebe uma excessão (como se fosse um aviso: tem um mané tentando gravar nesta página ae meu!), cria uma nova página, copia o conteúdo da original nela, e muda o mapeamento fisico <-> virtual de modo que o processo que tentou gravar agora enxerga, transparentemente, a nova página. Então o kernel libera a permissão de gravação na nova página e tudo acontece transparentemente: o processo que gravou enxerga a página alterada, os demais continuam enxergando a página original, antiga.

Isto é bastante eficiente pois minimiza o uso de memória física (já que a memória virtual, como o próprio nome diz, não existe efetivamente), e otimiza uma série de procedimentos que precisam ler dados com muito mais frequencia do que gravar, e ainda assim, a sobrecarga de gravação (edição, na verdade, quando a página já contem dados), é bem pequena.

Existem alguns mais avançados, por exemplo, páginas somente-leitura mapeadas de algum arquivo (inclusive os trechos de código dos programas executáveis) podem ser descartadas se o sistema estiver com pouca memória, e recarregadas mais tarde quando houver uma tentativa de ler/executar, etc.
_________________
Warning: time of day goes back (-163479us), taking countermeasures. Smile
Renie
Word
 
Mensagens: 732
Registrado em: 11 Out 2006 22:35
Localização: RJ - Niterói - Brasil

Mensagempor fabim » 21 Jul 2011 12:45

Pedalando no assunto.

Uma outra duvida que depois de alguns meses, comecei a entender para poder cogitar é o seguinte.

quando você pega um host pra fazer um cross, tu escolhe lá no tool chain o kernel para ARM926 por exemplo.
Ele gera o KERNEL para esta arquitetura.

Me veio uma coisa muito louca agora em mente.

por acaso a EMC e a MMU para ARM926 são em endereços padrão ?
Tipo.

O BOOT, vai pegar a imagem do kernel, e cuspir para a RAM.

Mas, a configuração inicial do processador, a configuração da EMC, é feito pelo BOOT inicial né ?
Feito isso, pega o BIN do kernel e cospe para o endereço 0 a X da memoria externa ?

Eu nem imaginava como funcionava essa etapa, agora que estou lendo mais afundo, começaram as necessidades de entendimento..rs
Mano, ve só.
Sou responsável pelo que escrevo!!! E não pelo que você entende !!!
fabim
Dword
 
Mensagens: 5001
Registrado em: 16 Out 2006 10:18
Localização: aqui uái!!!?

Mensagempor tcpipchip » 27 Jul 2011 13:01

Mas, a configuração inicial do processador, a configuração da EMC, é feito pelo BOOT inicial né ?


Sim, ele se chama BOOTSTRAP, o famoso ROMBOT da ATMEL...

Ele é o mais importante dos ETAPAS de inicialização de um ARM com linux...ou sem...
BOOTSTRAP--->U-BOOT-->KERNEL--->FS

É startup de configuração de Hardware e mais chata. Muda toda vez que voce pega um fabricante de memoria diferente :(, relacionado ao barramento...velocidade....tamanho do setor...etc...

Uma vez inicializado o hardware, o boot-strap chama o u-boot...(este normalmente é liberado...)...ai ele tem varios utilitários para voce pode executam teus primeiros programas objetos sem chamadas ao S.O.

O u-boot pode ser configurado BOOTARGS para carregar o KERNEL para a memoria e entao executa-lo...

No kernel, via U-boot ou compilação, voce pode apontar para onde está o FILE SYSTEM.

Enfim, é uma dor de cabeça danada fazer funcionar....quando voce mesmo projeta a sua placa...e não compra pronta...

TCPIPCHIP
Avatar do usuário
tcpipchip
Dword
 
Mensagens: 6560
Registrado em: 11 Out 2006 22:32
Localização: TCPIPCHIPizinho!


Voltar para Linux ( x86 ou x64 )

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

cron

x