Irmão de luto Hao (rato da orelha esquerda), uma pessoa puramente técnica

Eu ouvi sobre o irmão Hao no fim de semana passado, mas não acreditei no começo. Procurando notícias na Internet, parece ser verdade. Ele estava apenas na casa dos quarenta anos e sentiu muito. Há muito tempo que presto atenção ao irmão Hao. Ele é um especialista técnico, honesto, puro e adora compartilhar. Quer se trate de compartilhamento de tecnologia, insights de vida ou relacionado ao crescimento, aprendi muito. Uma vez influenciado por seus artigos, me interessei por tecnologia e sistemas linux. É uma pena que Deus tenha ciúme dos talentos e o mundo tenha perdido um excelente técnico e guia. Aqui também compartilho os artigos unix de Haoge, esperando que mais pessoas se interessem por sistemas linux (tipo unix).

prefácio

"Zhilan nasceu em um vale profundo, não porque não há pessoas, mas um cavalheiro; um cavalheiro cultiva a moralidade e não muda sua vontade por causa da pobreza;" Este é o lema do tio Mouse, e acho que ele o alcançou. Gostaria de usar este artigo para lamentar o professor Chen Hao e boa viagem! Obrigado ao Mr. Left Ear Mouse por sua contribuição para o mundo de TI!

Espero também que todos os meus amigos estejam de boa saúde, seguros e tranquilos! Preste mais atenção ao seu corpo em sua agenda lotada e fortaleça seu exercício.

Endereço do blog Haoel: https://haoel.blog.csdn.net/?type=blog

Cool Shell – CoolShell.cn

330a52843233d655d8a287e489f89adc.jpegNa sociedade impetuosa de hoje, tudo está avançando. Quantas pessoas realmente amam compartilhar e pesquisar tecnologia. O irmão rato que conheço é uma pessoa que adora tecnologia e está disposto a compartilhar. Chen Hao tem 20 anos de experiência em desenvolvimento de software e trabalhos relacionados e trabalhou em empresas conhecidas como Alibaba, Amazon e Thomson Reuters. Ele é bom na arquitetura técnica subjacente e tem experiência e experiência em sistemas de processamento de dados de alto desempenho, alta disponibilidade, distribuição, alta simultaneidade e larga escala. Gosto de prestar atenção à plataforma de tecnologia subjacente e aos aplicativos da indústria da Internet. Tecnicamente bom em C/C++/Java e Unix/Linux/Windows.

Ele costumava ser um especialista sênior no Alibaba Beijing R&D Center e Merchant Business Department, responsável pela plataforma de nuvem de comércio eletrônico , plataforma aberta, monitoramento de nuvem e plataforma multimídia de comércio eletrônico. Ele também estava envolvido no desenvolvimento de plataformas de virtualização relacionadas ao Alibaba Core System e Alibaba Cloud ECS no Alibaba Core System Expert Group.

Embora seja verdade que todo mundo tem suas próprias buscas por necessidades materiais, também é um prazer fazer algumas pesquisas técnicas de coração e hobbies. Especialmente no setor de TI, não busque algumas estruturas e ferramentas aparentemente poderosas e envolva-se em várias estruturas e linguagens, mas preste atenção na camada inferior para estabelecer uma boa base.

Além disso, a ideia de que é melhor comprar do que fazer é muito enganosa, não é a lição do nosso pescoço que está presa? Se sempre usarmos as ferramentas e estruturas avançadas de estrangeiros e fizermos apenas alguma inovação e implementação no nível de negócios, como podemos produzir grandes avanços e progressos? Como o modelo de pirâmide, de fato, o custo-benefício da camada de negócios no nível superior não é alto. Pense em quanto dinheiro os países estrangeiros ganham com o licenciamento de IP de apenas um núcleo ARM, por causa do alto teor de ouro.

 Aqui, gostaria de compartilhar um artigo sobre o sistema unix de Hao Ge "Na verdade, o Unix é muito simples" Espero que mais amigos que estão envolvidos na indústria de software prestem mais atenção à tecnologia subjacente e à pesquisa básica. Use menos o sistema Windows, pesquise e use mais o Linux. Não construa plataformas altas na areia flutuante, para que a força de nosso software possa se tornar cada vez mais forte. Independentemente de você estar desenvolvendo ou não, é recomendável experimentar o sistema doméstico. O atual sistema operacional doméstico deepin não é ruim, é recomendável experimentá-lo_Diferença entre deepin20.8 e v23_Maverick cat a's blog-CSDN blog

Somente compreendendo o passado do Unix podemos saber o que é e por quê. Resumindo o passado, saberemos como planejar e como agir amanhã. Na roda do tempo, muitas coisas passam como estrelas cadentes, mas algumas coisas podem resistir ao teste do tempo e exalar um charme duradouro, o que faz as pessoas falarem sobre isso e é transmitido até hoje. Para saber como escolher e como fazer amanhã, você não deve seguir cegamente as várias tecnologias de ponta de hoje, mas deve ir - entender e revisar a história com seriedade.

Na verdade, o Unix é muito simples

"Na verdade, o Unix é muito simples" autor Chen Hao, link:

https://haoel.blog.csdn.net/article/details/1533720?spm=1001.2014.3001.5502

Muitos amigos programadores me fazem algumas perguntas como esta na Internet, como aprender Unix? Por que o Unix é tão difícil? Como posso aprender bem? E deixe-me dar a eles alguma experiência em aprender bem o Unix. Na maioria das vezes, descobri que os amigos que fazem essas perguntas têm duas características:

1) Tem medo do Unix e não confia nele;

2) Gosto de comparar o Windows com o Unix.

Essas duas características são como duas "dicas psicológicas", dando a entender que o Unix é difícil de aprender, dando a entender que o Unix é péssimo e não tão bom quanto o Windows. Como resultado, fiquei hipnotizado por minha própria "dica" de longo prazo. Porque, desde o início, tenho medo das dificuldades, então sinto que o Unix não é bom, e sinto que é muito difícil, no final vai levar ao desgosto e à antipatia pelo Unix. Portanto, para corrigir as "dicas psicológicas" dos amigos acima mencionados. Quero escrever este artigo para dizer a você que o Unix é realmente simples.

Antes de descrever oficialmente "Simple Unix", gostaria de fazer algumas observações: (para evitar discussões sem sentido)

1) Este artigo é explicado do ponto de vista do desenvolvedor, então se alguém discordar do meu ponto de vista, por favor, me faça perguntas e discussões do ponto de vista do desenvolvedor, serei muito bem-vindo.

2) Este artigo inevitavelmente usará o Windows para comparar com o Unix. Isso não significa que eu não goste do Windows, nem significa que estou dizendo para você desistir do Windows. Também sabemos que esse tipo de comparação não faz sentido, mas como muitos amigos são preconcebidos pelo Windows, devo usar o Windows para fazer a cirurgia a fim de reverter a "dica psicológica". Isso é tudo.

 OK, vamos ao que interessa. Deixe-me primeiro falar sobre uma das características mais importantes do Unix - "alta coesão, baixo acoplamento"! Em outras palavras, vários aplicativos no Unix são irrelevantes para outros. Este é o pensamento que permeia todo o Unix - um alto grau de independência de módulos e programas. Esse tipo de design e prática tornará seu sistema mais estável e também o tornará particularmente fácil de gerenciar e manter. Os aplicativos no Unix são organizados como um exército regular. Enquanto o comandante (kernel) ainda estiver lá, o sistema não será capaz de inicializar devido à perda de um exército. Os aplicativos do Windows são como uma floresta... Da superfície, as árvores estão bem arrumadas, mas suas raízes se entrelaçam umas nas outras no subsolo, cortando constantemente, e o arranjo ainda é caótico, o que é extremamente complicado.

 O resultado de "alta coesão e baixo acoplamento" ao Unix é que seu sistema é basicamente pequenos programas com uma única função. precisa fazer é um simples jogo de "blocos de construção". O Windows é construído magnificamente, mas, infelizmente, é quase impossível para você colocar os "blocos de construção" de outras pessoas em seu próprio prédio. Sempre peça para você imitar ou reescrever.

(Interlúdio: você notou que quando você baixa o software Windows online, haverá um chamado "software verde"? Essa é a maior ironia do Windows. O Windows baixa um software e N DLLs são colocados no diretório do sistema Windows. Escreva N valores de chave no registro e haverá muitas ações que você não conhece. Ao instalar o software no Unix, você não precisa se preocupar com alguns arquivos confusos no diretório do sistema. É tão simples quanto copiar, mesmo que seja instalado automaticamente por rpm, após a instalação, você também pode consultar as alterações feitas no sistema após a instalação do software. Portanto, ao distribuir seu software no Unix, você sentirá que é mais fácil de fazer do que no Janelas Muitas, muitas.)

       Vamos falar sobre outro recurso mais importante do Unix - "Todos os dispositivos podem ser operados como arquivos". Simples. Todas as preparações, operações de E/S em arquivos, impressoras, monitores, terminais, redes, disquetes, fitas, USB, CDROM, etc., são realizadas na forma de descritores de arquivo. As duas chamadas de sistema mais importantes no Unix, leitura/gravação, podem lidar com a E/S de todos os dispositivos. O Unix já criou esses arquivos para você no diretório /dev. É muito simples de usar.

       Muitas pessoas podem pensar que a linha de comando do Unix é muito complicada. Um comando tem vários parâmetros, o que é extremamente complicado. Mas a razão pela qual os aplicativos sob Unix hoje ainda são dominados por interfaces de caracteres, isso apenas reflete as características simples do Unix. Este também é outro recurso do Unix - "suporte mútuo de comandos" Os comandos podem ser conectados uns aos outros por meio de um pipeline ou redirecionamento e com o suporte de scripts Shell, mesmo que algumas funções complexas (como um banco de dados de texto pequeno), também são extremamente simples.

       Se a discussão acima ainda não conseguiu convencê-lo de que o Unix é muito simples, então vamos usar alguns exemplos concretos e práticos para ver como o Unix é simples. Vamos tentar fazer a seguinte suposição: "Se aprendermos primeiro o Unix ao aprender a programar e depois mudarmos para o Windows", o que acontecerá?

1) Criamos um processo no Unix e o chamamos usando fork. No Windows, verificamos o MSDN e descobrimos que uma chamada de sistema chamada CreateProcess pode criar um processo, mas descobrimos que essa chamada de sistema tem 10 parâmetros. No entanto, fork no Unix não tem parâmetros. Neste caso, você sente uma cabeça grande? Porque, no Unix, você não pode ver uma API de chamada de sistema tão complexa com 10 parâmetros.

2) É muito simples para nós operar as permissões de arquivo no Unix. As permissões de arquivo são divididas em três grupos (pessoal, mesmo grupo e outros), e cada grupo é legível, gravável e executável. Duas chamadas de sistema simples chmod/chown farão isso. No Windows, se for NTFS, se você quiser definir permissões de arquivo programaticamente, hehe, você precisa primeiro entender o que é: SID, o que é DACL, o que é SACL, o que é ACE e mais de uma dúzia de funções de API do sistema relacionadas Esperando Por Você. (Veja meu "Manipulando permissões de arquivos NTFS com programas") Você pode pensar que uma política de segurança tão complexa é a base para tornar o sistema mais seguro.Desde o dia em que o Windows apareceu, ele não superou o Unix em termos de segurança. Isso, sem dúvida, faz as pessoas sentirem que o Windows fez uma coisa ingrata.

(Inserir: A troca de usuário no Unix é bastante simples e conveniente. No entanto, a troca de usuário no Windows fará com que você precise sair do programa de primeiro plano do usuário atual. Isso leva ao fato de que os usuários no Windows quase sempre escolherão estar no superusuário Trabalhando/navegando na Internet sob a autoridade, isso é deixar completamente sua própria máquina rodar nua, então uma vez que o vírus no Windows é executado no sistema, ele pode fazer o que quiser. Sob o Unix, poucos usuários operarão o máquina como root, porque a troca de usuários é muito difícil. conveniente.)

3) No Unix, os usuários têm IDs, os grupos de usuários têm IDs e os processos/threads têm IDs. O ID é simples e fácil de entender, assim como nosso cartão de identificação. No Windows, o ID do usuário é chamado Token e o ID do processo é chamado Handle (na verdade, é um tipo DWORD) Vejo que muitas perguntas na Internet estão perguntando sobre o conceito de Handle no Windows. Tenho me perguntado, por que a Microsoft não adota um termo simples e compreensível? Para ficar tão abstrato, tão confuso. Embora isso faça o Windows parecer muito NB, também aumentará a complexidade do aprendizado. (A complexidade de desenvolvimento e aprendizado do Windows é muito mais complicada do que a do Unix, e há muitos termos aparentemente de alto nível que confundem as pessoas)

4) Vamos examinar novamente o gerenciamento de usuários e o proprietário do programa. No Unix, o que você precisa fazer é configurar o servidor NIS e o servidor NFS (montado automaticamente com Autofs), que é conciso e claro. No Windows, é semelhante a algo chamado Domain (controlador de domínio primário). Primeiro, para ingressar no domínio, você precisa reiniciar o computador (no Unix, você só precisa configurar o arquivo /etc/nsswitch.conf para informar o usuário local logar na fonte, não precisa reiniciar), e para desenvolvedores, essa configuração do Unix é totalmente transparente para o programa. Usuários de domínio do Windows e usuários locais precisam de um nome de domínio para distinguir. Ao trocar de usuário em um programa, o Unix só precisa de setuid/seteuid, e o Windows tem três APIs mais complicadas para isso: CreateProcessAsUser, ImpersonateLoggedOnUser, LogonUser, e a complexidade não precisa ser comparada. Além disso, no modo Domínio, todos os arquivos do seu diretório de Documentos e Configurações serão colocados no servidor do Domínio, e você precisará baixar esses arquivos quando fizer login em outras máquinas. Finalmente, não estou preocupado que seus registros de bate-papo do MSN com internautas estejam em todos os lugares por causa do seu login. O que me preocupa é se o código que você escreve em um ambiente de gerenciamento tão complicado pode tranquilizar outras pessoas? :-(

5) No Unix, para adicionar seu próprio programa ao serviço de inicialização do sistema, basta configurá-lo em /etc/init.d. Escreva um script com funções de início e parada e vincule-o a diferentes diretórios do modo de início de uma maneira especial de nomenclatura. Adicionar um serviço de inicialização no Windows é provavelmente mais difícil se você não escrever um programa.

6) No Unix, se você deseja obter informações do sistema. Basta ir para o diretório /proc e pegar a pilha de arquivos. Status de todos os threads/entradas, linha de comando, uso da área de memória/swap, descritores de arquivos abertos, etc., CPU do sistema, memória, área de swap, arquivo de memória IO, partição, informações, rede, status de execução do sistema, equipamento do sistema, etc. , pode ser tão detalhado quanto você quiser e é totalmente em texto simples, para que você possa lê-lo diretamente. No Windows, não é fácil enumerar as informações do processo atual do sistema, muito menos obter outras informações.

Admiro a Microsoft por complicar tanto o sistema operacional, é o registro, é a política de segurança, é OLE e é COM... . Toda vez que abro o regedit.exe, não ouso tocar em HKEY_CLASSES_ROOT, porque olho para aqueles milhares de CLSIDs dentro e me sinto um pouco tonto.

Desde 1995, 10 anos se passaram. A Microsoft introduziu uma ampla variedade de tecnologias. Ainda me lembro que existem duas coisas chamadas FoxPro e J++ no Visual Studio 6.0. FoxPro vem da Foxbase, e ainda tem alguns anos, e J++ parece ter alguns anos. O controle ActiveX é uma tecnologia completamente falha, e a linguagem de programação chamada VB, na visão de hoje, de fato arruinou muitos programadores em potencial. Quando COM apareceu, quantas pessoas hoje ainda se lembram de uma coisa chamada MTS? Hoje, não sei quantas pessoas ainda se lembram de algo chamado ODBC? Você está cansado de se atualizar neste mundo complexo e caótico do Windows? O .NET de hoje não sabe quantas tecnologias serão precipitadas ao longo do tempo? No Windows, aprendemos muitas tecnologias com falha ou tecnologias de transição. E nosso Unix não mudou muito desde a década de 1970, e a linguagem C que surgiu por causa do Unix ainda é deslumbrante hoje. Eu acredito no Unix, que foi experimentado e testado por mais de 30 anos e ainda é tão simples.

O Unix é tão simples, amigos que querem aprender a desenvolver no Unix, o Windows é um sistema operacional tão complicado, você ainda tem medo de um Unix tão simples?

Introdução ao sistema de compartilhamento de tempo UNIX

O artigo "The UNIX Time-Sharing System" pode ser descrito como um grande avanço no sistema operacional e teve um impacto profundo.

Pessoalmente, acho que vale a pena ler este artigo. É muito útil para a compreensão de sistemas do tipo Unix. Afinal, artigos bem conhecidos podem ser considerados como conhecimento de "primeira mão" e seu rigor é inquestionável. E o artigo não é longo, todo o artigo é introdutório e não envolve pesquisa teórica, então não é tão difícil de entender quanto a maioria dos outros trabalhos. Em detalhes, o sistema operacional moderno se tornou um gigante, e o uso diário geralmente vê apenas as árvores, mas não a floresta, e há uma certa lacuna entre o conhecimento teórico e a situação real; portanto, ao aprender o conhecimento teórico, também é necessário entender a forma inicial desses gigantes. útil.

O conteúdo a seguir é transferido da tradução do artigo em inglês pelo internauta chocovon, obrigado por compartilhar. Endereço de tradução chinesa:

GitHub - chocovon/the-unix-time-sharing-system-chinês

Endereço original em inglês (para download): http://www.course.sdu.edu.cn/Download/d8b9e205-789f-40e7-8343-3f933425a5ca.pdf 

Resumo

O UNIX é um sistema operacional interativo multiusuário de uso geral usado nos computadores PDP-11/40 e 11/45 da Digital Equipment Corporation (DEC). Possui recursos que raramente são vistos mesmo em sistemas operacionais maiores, incluindo: (1) um sistema de arquivos hierárquico que integra volumes removíveis; (2) arquivos compatíveis, dispositivos e comunicação entre processos; (3) Capaz de inicializar processos assíncronos; (4) O idioma de comando do sistema pode ser selecionado pelo usuário; (5) Mais de 100 subsistemas, incluindo mais de uma dúzia de idiomas. Este artigo discute as características e a implementação do sistema de arquivos e da interface de comando do usuário.

1. Introdução

Existem três versões do UNIX. As versões mais antigas (por volta de 1969-70) rodavam nos computadores PDP-7 e -9 da Digital Equipment Corporation. A segunda versão é executada em um computador PDP-11/20 desprotegido. Este artigo abrange apenas os sistemas PDP-11/40 e /45  [1]  porque são mais modernos e diferem dos sistemas UNIX mais antigos de muitas maneiras apenas no redesenho de alguns recursos defeituosos ou ausentes.

Desde que o PDP-11 UNIX se tornou prático em fevereiro de 1971, ele foi usado em cerca de 40 dispositivos; esses dispositivos geralmente têm sistemas menores do que os descritos neste artigo. A maioria deles é usada em aplicações como escrever e formatar pedidos de patente e outras literaturas, coleta e processamento de dados de falha de vários switches dentro do Sistema Bell e registro e revisão de tíquetes de serviço telefônico. Nossas próprias instalações são usadas principalmente para pesquisas sobre sistemas operacionais, linguagens, redes de computadores e outros tópicos de informática, bem como para documentação.

Talvez a conquista mais importante do UNIX seja mostrar que um poderoso sistema operacional interativo não requer equipamentos ou mão de obra caros. O UNIX pode ser executado em hardware que custa apenas US$ 40.000, com menos de 2 homens-ano de esforço gastos em software de sistema principal. Embora o UNIX inclua alguns recursos que raramente estão disponíveis mesmo em sistemas muito maiores, esperamos que os usuários do UNIX percebam que as características mais importantes do sistema são simplicidade, elegância e facilidade de uso.

Além do próprio sistema, os principais programas no UNIX são: assembler, editor de texto baseado em QED  [2] , link loader, depurador simbólico,   compilador de linguagem como BCPL [3] bottom-up (YACC), um gerador de letras de formulário, um processador de macro ( M6 ) [5]  , e um indexador de nova linha.

Além disso, há uma série de programas de manutenção, utilidade, entretenimento e novidades. Esses programas são todos escritos localmente. Notavelmente, o sistema é completamente autossuficiente. Todo software UNIX é mantido sob UNIX; da mesma forma, a documentação UNIX é gerada e formatada por editores UNIX e formatadores de texto.

2. Ambiente de hardware e software

O PDP-11/45 no qual nosso UNIX está instalado é um computador de palavra de 16 bits (byte de 8 bits) com uma memória principal de 144KB; o UNIX ocupa 42K bytes. No entanto, este sistema inclui um número muito grande de drivers de dispositivo e aloca uma grande quantidade de espaço para buffers de E/S e tabelas de sistema; um sistema mínimo capaz de executar o software acima requer um total de apenas 50K bytes de núcleos.

O PDP-11 tinha um disco de cabeça fixa de 1 Mbyte para armazenamento e troca de sistema de arquivos; quatro unidades de disco de cabeça móvel, cada uma fornecendo 2,5 Mbytes em cartuchos de disco removíveis; e uma unidade de disco de cabeça removível de 40 Mbytes com pacotes de discos removíveis. Além disso, havia um perfurador de leitor de fita de papel de alta velocidade, fita magnética de nove trilhas e fita D (uma instalação de fita magnética que podia endereçar e reescrever registros individuais). Além da máquina de escrever do console, existem 14 interfaces de comunicação de velocidade variável conectadas ao conjunto de dados da série 100 e uma interface de conjunto de dados 201 usada principalmente para emendar impressões em impressoras de linha pública. Também foram exclusivos a interface Picturephone®, a unidade de resposta de voz, o sintetizador de fala, o fotocompositor, a rede de comutação digital e o satélite PDP-11/20 que gerou vetores, curvas e caracteres em um display de tubo de memória Tektronix 611.

A maior parte do software UNIX é escrita na linguagem C acima mencionada  [6] . As primeiras versões do sistema operacional foram escritas em linguagem assembly, mas no verão de 1973 reescrevemos uma versão em C. O novo sistema é um terço maior que o antigo. Como o novo sistema não é apenas mais fácil de entender e modificar, mas também inclui muitas melhorias funcionais, incluindo multiprogramação e a capacidade de compartilhar código reentrante entre vários programas do usuário, consideramos esse aumento de tamanho bastante aceitável.

3. Sistema de arquivos

O trabalho mais importante do UNIX é fornecer um sistema de arquivos. Do ponto de vista do usuário, existem três tipos de arquivos: arquivos de disco comuns, diretórios e arquivos especiais.

3.1 Documentos comuns

Arquivos comuns contêm qualquer informação que o usuário coloca neles, como símbolos ou programas binários (objetos). O sistema não faz suposições sobre a estrutura de arquivos comuns. Um arquivo de texto é apenas uma sequência de caracteres, separados por novas linhas entre as linhas. Um programa binário é a sequência de palavras que aparecerá na memória central quando o programa começar a ser executado. Os arquivos manipulados por alguns programas do usuário são mais estruturados: os arquivos objeto gerados pelo montador e processados ​​pelo carregador têm um formato específico. No entanto, a estrutura dos arquivos é controlada pelos programas que os utilizam, não pelo sistema.

3.2 Conteúdo

Os diretórios fornecem um mapeamento entre os nomes dos arquivos e os próprios arquivos, formando uma estrutura em todo o sistema de arquivos. Cada usuário tem seu próprio diretório de arquivos; ele também pode criar subdiretórios para colocar grupos de arquivos em diferentes diretórios para processamento. A forma de um diretório é exatamente a mesma de um arquivo comum, exceto que programas sem permissão não podem escrever nele, ou seja, o conteúdo do diretório é controlado pelo sistema. No entanto, qualquer usuário com as permissões apropriadas pode ler o diretório como qualquer outro arquivo.

O sistema mantém vários diretórios para uso próprio. Um deles é o diretório raiz ( root ). Todos os arquivos do sistema podem ser encontrados de acordo com a sequência de diretórios no caminho. O ponto de partida para essa pesquisa geralmente é o diretório raiz. Outro diretório do sistema contém todos os programas comumente usados; ou seja, todos os comandos ( comandos ). Porém, como veremos, não é necessário que um programa seja colocado neste diretório para ser executado.

Os arquivos são nomeados em sequências de no máximo 14 caracteres. Ao especificar um nome de arquivo para o sistema, pode ser na forma de um nome de caminho , que é uma sequência de nomes de diretórios separados por barras "/", terminando com um nome de arquivo. Se a sequência começar com uma barra, a pesquisa começará no diretório raiz. O nome do arquivo  /alpha/beta/gamma fará com que o sistema procure por alpha  no diretório raiz  , depois  procure por beta no  diretório  alpha e finalmente   encontre  gamma no diretório beta . gamma  pode ser um arquivo comum, um diretório ou um arquivo especial. Como caso extremo, o nome "/" refere-se ao próprio diretório raiz.

Os nomes de caminho que não começam com "/" fazem com que o sistema comece a pesquisar no diretório atual do usuário. Assim, o nome alpha/beta  especifica  o arquivo chamado beta  no  subdiretório alpha do diretório atual  . A classe mais simples de nomes de arquivos, como  alpha , refere-se a um arquivo que está no diretório atual. Como outro caso extremo, um nome de arquivo vazio refere-se ao diretório atual.

O mesmo arquivo não-diretório pode aparecer em vários diretórios com nomes diferentes. Esse recurso é chamado de vinculação ; a entrada do diretório que aponta para esse arquivo às vezes é chamada de link . O UNIX difere de outros sistemas que permitem links porque todos os links para o mesmo arquivo recebem o mesmo status. Ou seja, um arquivo não existe em um diretório específico; a entrada do diretório do arquivo consiste apenas em seu nome e um ponteiro para a descrição real do arquivo. Assim, um arquivo existe independentemente de quaisquer entradas de diretório, embora na prática um arquivo desapareça com o último link.

Cada diretório tem pelo menos dois itens de dados. Os nomes no diretório referem-se ao próprio diretório. Assim, um programa pode ler o diretório atual como "." sem saber seu caminho completo. Por convenção, ".." refere-se ao diretório pai que apresenta o diretório, ou seja, o diretório que o criou.

A estrutura do diretório é restrita à forma de uma árvore enraizada. Com exceção das entradas especiais "." e "..", cada diretório deve aparecer como uma entrada dentro de outro diretório, ou seja, seu diretório pai. Isso é feito para facilitar o acesso às subárvores dessa estrutura de diretório ao escrever programas, mas, mais importante, para evitar a separação parcial da hierarquia. Se links arbitrários para diretórios fossem permitidos, seria difícil detectar quando a última conexão da raiz para um diretório foi interrompida.

3.3 arquivos especiais

Arquivos especiais constituem o recurso mais incomum do sistema de arquivos UNIX. Todo dispositivo de E/S suportado pelo UNIX tem pelo menos um desses arquivos associado a ele. Arquivos especiais são lidos e gravados como arquivos de disco comuns, mas a solicitação de leitura e gravação causará a ativação do dispositivo relacionado. A entrada do diretório para cada arquivo especial está no diretório  /dev  , e também pode ser vinculado a qualquer um desses arquivos como arquivos comuns. Então, por exemplo, para perfurar fita de papel, escreva no arquivo  /dev/ppt  . Existem arquivos especiais para cada linha de comunicação, cada disco, cada unidade de fita e memória de núcleo físico. Obviamente, discos ativos e arquivos especiais do kernel são protegidos contra acesso aleatório.

Esse tratamento de dispositivos de E/S tem três vantagens: arquivos e dispositivos de E/S são os mais semelhantes possíveis; nomes de arquivos e nomes de dispositivos têm a mesma sintaxe e significado, portanto, programas que recebem nomes de arquivos como argumentos também podem passar nomes de dispositivos; e, finalmente, os arquivos especiais têm o mesmo mecanismo de proteção que os arquivos comuns.

3.4 Sistema de arquivos montável

Embora a raiz do sistema de arquivos seja sempre armazenada no mesmo dispositivo, toda a hierarquia do sistema de arquivos não precisa ser mantida nesse dispositivo. Há uma  solicitação de sistema de montagem  , que possui dois parâmetros: um é o nome de um arquivo comum existente e o outro é o nome de um arquivo especial que é acessado diretamente. O volume de armazenamento (como um grupo de discos) associado ao arquivo especial deve ter uma estrutura de sistema de arquivos independente, com sua própria hierarquia de diretórios. O efeito da montagem  é alterar a referência original a um arquivo comum para uma referência ao diretório raiz do sistema de arquivos no volume montável. Na verdade, mount  substitui uma folha (arquivo comum) da árvore hierárquica atual por uma subárvore totalmente nova (a hierarquia armazenada no volume montável). Depois de montados, há pouca diferença entre os arquivos no volume montável e os do sistema de arquivos permanente. Usando nosso próprio dispositivo como exemplo, o diretório raiz é montado em um disco de cabeçote fixo, enquanto o grande drive de disco contendo arquivos do usuário é montado pelo programa de inicialização do sistema, e quatro pequenos drives de disco estão disponíveis para os usuários montarem seu próprio disco. grupos. Gera um sistema de arquivos montável escrevendo em seu arquivo especial correspondente. Existe um utilitário que pode criar um sistema de arquivos vazio ou simplesmente copiar um sistema de arquivos existente.

As regras para manipulação de arquivos em diferentes dispositivos são as mesmas, com uma exceção: não deve haver ligação entre uma hierarquia de sistema de arquivos e outra. Essa restrição é aplicada para evitar contabilidade pesada que, de outra forma, exigiria a garantia de que os links sejam removidos quando o volume montável for finalmente desmontado. Deve-se observar que no diretório raiz de todos os sistemas de arquivos, independentemente de ser montável ou não, o nome ".." refere-se ao próprio diretório, não ao diretório pai.

3.5 Proteção

Embora o esquema de controle de acesso do UNIX seja bastante simples, ele possui alguns recursos incomuns. Cada usuário no sistema recebe um número de identificação de usuário exclusivo. Quando um arquivo é criado, ele é marcado com o ID de usuário de seu proprietário. Para novos arquivos, também é fornecido um conjunto de 7 bits de proteção. Seis desses bits especificam permissões de leitura, gravação e execução para o proprietário do arquivo e todos os outros usuários, respectivamente.

Se o sétimo bit for verdadeiro, o sistema mudará temporariamente o ID do usuário atual para o do criador do arquivo sempre que o arquivo for executado como um programa. Esta mudança de ID do usuário só é efetiva quando o programa que o chama é executado. A função set-user-ID fornece aos programas privilegiados a capacidade de usar arquivos que outros usuários não podem acessar. Por exemplo, um programa pode manter um arquivo de contabilidade que não deve ser lido nem alterado, exceto pelo próprio programa. Se o bit set-user-ID do programa estiver ativado, ele poderá acessar o arquivo, enquanto outros programas executados pelo usuário do programa não terão acesso ao arquivo. Como qualquer programa pode obter o ID de usuário real de seu chamador, um programa com o privilégio set-user-ID também pode obter os privilégios de seu chamador, se necessário. Esse mecanismo permite que os usuários executem comandos cuidadosamente projetados que invocam entradas privilegiadas do sistema. Por exemplo, há uma entrada de sistema que somente o "superusuário" (abaixo) pode invocar, o que cria um diretório vazio. Conforme mencionado acima, os diretórios devem ter duas entradas, "." e "..". O comando para criar um diretório pertence ao superusuário e tem o bit set-user-ID definido. Cria o diretório e define as entradas para "." e ".." após verificar a autorização de seu chamador para criar o diretório especificado.

Como qualquer pessoa pode definir o bit set-user-ID em seus próprios arquivos, esse mecanismo geralmente não requer intervenção do administrador. Por exemplo, este esquema de proteção pode resolver facilmente   o problema de contabilidade MOO proposto em [7] .

Há um ID de usuário especial no sistema (isto é, o ID de "superusuário") que não está sujeito às restrições usuais de acesso a arquivos; assim (por exemplo) é possível gravar diretamente para despejar e recarregar o programa do sistema de arquivos.

3.6 Para ajuste de E/S

Através do design de chamadas de sistema de I/O, eliminamos as diferenças de vários dispositivos e métodos de acesso. Não há distinção entre E/S "aleatória" e sequencial, e o sistema não impõe tamanhos de registro lógicos. O tamanho de um arquivo comum é determinado pelo maior número de bytes gravados no arquivo, não é necessário ou possível predeterminar o tamanho do arquivo.

Para ilustrar a essência do I/O no UNIX, algumas chamadas básicas são resumidas abaixo. Especificamos os parâmetros necessários em uma linguagem anônima sem envolver as complexidades da programação em linguagem de máquina. Cada chamada ao sistema pode resultar em um retorno de erro, que não é representado na sequência de chamadas para simplificar.

Para ler ou escrever em um arquivo que supostamente já existe, ele deve ser aberto com a seguinte chamada:

filep = open(name, flag)

name  indica o nome do arquivo, que pode ser qualquer nome de caminho. O parâmetro flag  indica se o arquivo é lido, escrito ou "atualizado", ou seja, lido e escrito ao mesmo tempo.

O valor de retorno  filep  é chamado de descritor de arquivo. É um pequeno número inteiro usado como identificação do arquivo para chamadas subsequentes de leitura, gravação ou outras operações.

Para criar um novo arquivo ou sobrescrever completamente um arquivo antigo, use  a  chamada de sistema create. Ele cria um novo arquivo quando o arquivo especificado não existe e trunca o arquivo antigo para comprimento zero, se existir. create  também abrirá o arquivo recém-criado para gravação, assim como  open  , retornará um descritor de arquivo.

Não há bloqueios visíveis ao usuário no sistema de arquivos e não há limite para o número de usuários que podem abrir um arquivo para leitura e gravação; embora quando dois usuários gravam em um arquivo ao mesmo tempo, o conteúdo do arquivo pode estar confuso, mas na operação real, nenhuma dificuldade surgiu. Acreditamos que em nosso ambiente locks não são necessários e nem suficientes para evitar interferências entre usuários em um mesmo arquivo. É desnecessário porque não estamos lidando com um grande banco de dados de arquivo único mantido por vários processos independentes. Não é suficiente porque os bloqueios no sentido geral, que impedem um usuário de escrever em um arquivo que outro usuário está lendo, também não podem evitar confusão, por exemplo, quando dois usuários estão editando o mesmo arquivo, mas seu editor fará primeiro um cópia do arquivo.

Deve-se dizer que quando dois usuários executam simultaneamente atividades que interferem mutuamente, como escrever no mesmo arquivo, criar arquivos no mesmo diretório ou excluir arquivos abertos pela outra parte, o sistema possui intertravamento interno suficiente para manter a consistência lógica do sistema de arquivos.

Exceto conforme observado abaixo, leituras e gravações são sequenciais. Isso significa que, se um byte no arquivo foi gravado (ou lido) mais recentemente, a próxima chamada de E/S apontará implicitamente para o byte seguinte a esse byte. Para cada arquivo aberto existe um ponteiro, mantido pelo sistema, que indica o próximo byte a ser lido ou escrito. Se n bytes forem lidos ou gravados, o ponteiro será avançado em n bytes.

Depois que um arquivo é aberto, as seguintes chamadas podem ser feitas:

n = read(filep, buffer, count)
n = write(filep, buffer, count)

Entre   o arquivo representado por  filep e  o array de bytes representado por  buffer ,  serão transferidos no máximo count bytes de dados. O valor de retorno  n  é o número real de bytes transferidos. No caso de escrita, n  e  contagem  serão consistentes, a menos que algumas circunstâncias especiais, como erros de E/S ou o término do meio físico de arquivos especiais; no caso de leitura, n pode ser menor que contagem, mesmo que não haja  exceção  ocorre . Se o ponteiro de leitura estiver tão próximo do final do arquivo que a leitura de  caracteres de contagem  resultaria em uma leitura além do final, o número de bytes transferidos fará com que o ponteiro passe logo após o final do arquivo; caso contrário, definitivamente não para a máquina de escrever Dispositivos do tipo Entrada de mais de uma linha. Quando  n é igual a zero  retornado pela  chamada read  , indica o fim do arquivo. Para arquivos em disco, isso ocorre quando o ponteiro de leitura avança para o tamanho atual do arquivo. Para uma máquina de escrever, o fim do arquivo (EOF) pode ser gerado pela sequência de escape especificada pela máquina de escrever.

Os bytes gravados no arquivo alterarão apenas   os bytes especificados pela posição e contagem do ponteiro de gravação, deixando o restante do arquivo inalterado. Se o último byte gravado estiver além do final do arquivo, o arquivo será estendido conforme necessário.

Para fazer I/O aleatório (acesso direto), simplesmente mova o ponteiro para ler ou escrever no local apropriado no arquivo:

location = seek(filep, base, offset)

 O ponteiro associado ao  arquivo  filep será movido  em bytes de deslocamento do início, meio ou fim do arquivo,  dependendo da base do ponto inicial . deslocamento  pode ser negativo. Para alguns dispositivos (como fita de papel e máquinas de escrever), a  chamada de busca é ignorada. O deslocamento real do ponteiro desde o início do arquivo será retornado em  location  .

3.6.1 Outras chamadas de E/S

Existem também várias entradas de sistema relacionadas a E/S e sistema de arquivos, que não serão discutidas aqui. Por exemplo: fechar um arquivo, obter o status de um arquivo, alterar o modo de proteção ou o proprietário de um arquivo, criar um diretório, gerar um link para um arquivo existente, excluir um arquivo.

4. Implementação do sistema de arquivos

Conforme declarado em §3.2 acima, uma entrada de diretório contém apenas o nome de um arquivo associado e um ponteiro para o próprio arquivo. Esse ponteiro é um número inteiro chamado  i-number (número de índice) do arquivo. Quando um arquivo é acessado, seu  i-number  é usado como um índice para uma tabela do sistema (ou seja,  i-list ) mantida em uma área conhecida do dispositivo onde reside o diretório. A entrada assim encontrada (  o i-node do arquivo ) contém a seguinte descrição do arquivo:

  1. proprietário
  2. bit de proteção
  3. O endereço onde o conteúdo do arquivo está armazenado em um disco físico ou fita
  4. Tamanho do arquivo
  5. Última modificação
  6. A contagem de links do arquivo, ou seja, o número de vezes que o arquivo aparece no diretório
  7. Um bit binário indicando se o arquivo é um diretório
  8. Um bit binário indicando se o arquivo é um arquivo especial
  9. Um bit binário indicando se o arquivo é "grande" ou "pequeno"

O objetivo da chamada de sistema open  ou  create  é converter um nome de caminho fornecido pelo usuário em um i-number pesquisando em diretórios explicitamente ou implicitamente nomeados. Depois que um arquivo é aberto, seu dispositivo, i-number e ponteiros de leitura/gravação são armazenados em uma tabela do sistema indexada pelo   descritor de arquivo retornado por open  ou  create . Assim, o descritor de arquivo fornecido em chamadas subsequentes para ler ou escrever no arquivo pode ser facilmente associado às informações necessárias para acessar o arquivo.

Quando um novo arquivo é criado, é atribuído a ele um i-node e uma entrada de diretório é criada contendo o nome do arquivo e o número do i-node (Anotação: i-number). Para vincular a um arquivo existente, crie uma entrada de diretório com um novo nome, copie o i-number da entrada do arquivo original e incremente o campo de contagem de links do i-node. Para remover (excluir) um arquivo, diminua o número do link do i-node apontado por sua entrada de diretório e exclua a entrada de diretório. Se a contagem de links cair para 0, todos os blocos de disco ocupados pelo arquivo são liberados e o i-node é desalocado.

O espaço em qualquer disco fixo ou removível com sistema de arquivos é dividido em blocos de 512 bytes, com endereços lógicos atribuídos de 0 a um limite superior, dependendo do dispositivo. Existe uma área no i-node de cada arquivo que pode armazenar 8 endereços de dispositivo. Um arquivo pequeno (não especial)  pode preencher diretamente blocos de no máximo 8; nesse caso, o endereço do próprio bloco é armazenado no i-node. Para arquivos grandes (não especiais) , cada um dos 8 endereços de dispositivo pode apontar para um bloco indireto e um bloco indireto pode conter 256 endereços de bloco usados ​​para formar o arquivo. Esses arquivos podem ter até 8⋅256⋅512 ou 1.048.576 (2^20) bytes.

A discussão acima se aplica a arquivos comuns. Ao fazer uma solicitação de I/O para um arquivo que é indicado como um arquivo especial no i-node, as últimas 7 palavras de endereço do dispositivo não são importantes e a lista é interpretada como um par de bytes que formam o nome do dispositivo interno ( dispositivo ) . Esses bytes especificam o tipo de dispositivo e o número do subdispositivo, respectivamente. O tipo de dispositivo indica qual programa de sistema tratará de E/S naquele dispositivo; o número do subdispositivo é usado para selecionar, por exemplo, uma unidade de disco conectada a um controlador ou uma das várias interfaces de máquina de escrever.

Neste ambiente, a implementação da chamada de sistema mount  (§3.4) é muito simples. mount  mantém uma tabela do sistema (anotação: um relacionamento de mapeamento), seu argumento é o   número i e o nome do dispositivo do arquivo comum especificado no processo de montagem e o valor correspondente é o nome do dispositivo do arquivo especial especificado. Ao  digitalizar o nome do caminho durante o processo de abertura  ou  criação  , cada grupo (número i, dispositivo) será pesquisado e, se for encontrada uma correspondência, o número i será substituído por 1 (ou seja, o número i de o diretório raiz em todos os números dos sistemas de arquivos) e o nome do dispositivo é substituído pelo valor correspondente na tabela.

Do ponto de vista do usuário, a leitura e a escrita do arquivo são síncronas, sem buffering. Ou seja,  após o  retorno da chamada de leitura, os dados podem ser usados ​​imediatamente e, inversamente, após  a gravação  , o espaço de trabalho do usuário pode ser reutilizado. Na verdade, o sistema mantém um mecanismo de buffer bastante sofisticado que reduz bastante o número de operações de E/S necessárias para acessar os arquivos. O seguinte assume que uma chamada de gravação é feita   , especificando um byte a ser transferido.

O UNIX pesquisará os vários buffers para ver se o bloco de disco afetado está atualmente residente na memória do kernel; caso contrário, ele será lido do dispositivo. Os bytes afetados são então substituídos no buffer e uma entrada é adicionada à lista de blocos a serem escritos. A chamada de gravação retorna diretamente   , embora a E/S real possa não ser concluída até mais tarde. Pelo contrário, se um byte for lido, o sistema julgará se o bloco de armazenamento secundário onde o byte está localizado já está em um buffer do sistema; se sim, o byte pode ser devolvido imediatamente. Caso contrário, leia o bloco no buffer e selecione o byte.

Um programa que lê e grava um arquivo em unidades de 512 bytes tem uma vantagem sobre um programa que lê ou escreve apenas um byte por vez, mas o ganho não é enorme e vem principalmente de evitar a sobrecarga do sistema. Para um programa que não executa E/S de grande capacidade ou não é comumente usado, ainda é razoável usar a menor unidade possível para leitura e gravação.

O conceito de i-list é um recurso incomum do UNIX. Na prática, esse método de organização de sistemas de arquivos provou ser bastante confiável e manejável. Uma vantagem do próprio sistema é que cada arquivo tem um nome curto e inequívoco que é associado de maneira fácil à proteção, endereço e outras informações necessárias para acessar o arquivo. Ele também permite um algoritmo bastante simples e rápido para verificar a consistência do sistema de arquivos, como verificar se as partes contendo informações e livremente alocáveis ​​de cada dispositivo são separadas e somadas para preencher o espaço no dispositivo. Este algoritmo é independente da hierarquia de diretórios, pois ele só precisa varrer a i-list organizada linearmente. Ao mesmo tempo, o conceito de i-list apresenta certas peculiaridades não encontradas em outras organizações de sistemas de arquivos. Por exemplo, uma questão é, uma vez que todas as entradas de diretório para um arquivo têm o mesmo status, quem deve ser cobrado pelo espaço ocupado por um arquivo? Em geral, é injusto cobrar do proprietário de um arquivo porque é possível que um usuário crie um arquivo, outro usuário crie um link para ele e o primeiro usuário o exclua. O primeiro usuário ainda é o proprietário do arquivo, mas o segundo usuário deve ser cobrado. O algoritmo razoavelmente justo mais simples parece ser distribuir a taxa igualmente entre os usuários que têm um link para o arquivo. As versões atuais do UNIX evitam esse problema ao não cobrar nada.

4.1 Eficiência do sistema de arquivos

Para ilustrar a eficiência do UNIX em geral e a eficiência do sistema de arquivos em particular, registramos o tempo de compilação de um programa de 7621 linhas. A montagem foi executada apenas na máquina; o tempo total do relógio foi de 35,9 segundos a 212 linhas por segundo. A distribuição de tempo é a seguinte: 63,5% de tempo de execução do montador, 16,5% de sobrecarga, 20,0% de tempo de espera do disco. Não estamos tentando colocar nenhuma interpretação nesses números ou compará-los com outros sistemas, apenas para apontar que geralmente estamos satisfeitos com o desempenho geral do sistema.

5. Processos e imagens

Uma imagem ( image ) é um ambiente de execução de computador. Inclui a imagem principal, valores gerais de registro, status de arquivos abertos, diretório atual, etc. Uma imagem é o estado atual de um pseudocomputador.

Um processo ( processo ) é a execução de uma imagem. Quando o processador executa em nome do processo, a imagem deve residir no núcleo; durante a execução de outros processos, a imagem permanece residente no núcleo, a menos que apareça um processo ativo de alta prioridade que o obrigue a ser trocado para um fixado no disco do cabeçote.

A parte principal do usuário da imagem é dividida em três segmentos lógicos. O segmento de texto do programa começa no local 0 do espaço de endereço virtual. Durante a execução, esse segmento é protegido contra gravação e uma única cópia é compartilhada entre todos os processos que executam o mesmo programa. No primeiro limite de 8 Kbytes acima do segmento de texto do programa no espaço de endereço virtual, um segmento de dados gravável não compartilhado é aberto e o tamanho do segmento de dados pode ser estendido por meio de chamadas do sistema. Abaixo do endereço mais alto no espaço de endereço virtual está o segmento da pilha.Com a flutuação do ponteiro da pilha de hardware, o segmento da pilha se expande automaticamente para baixo.

5.1 Processo

A menos que o UNIX esteja se autoinicializando, apenas  a chamada de sistema fork  pode ser usada para gerar um novo processo.

processid = fork(label)

Quando fork é executado por um processo, ele se divide em dois processos de execução independente. Esses dois processos têm cópias independentes da imagem central original e compartilham todos os arquivos abertos. Os novos processos diferem apenas em que um deles é considerado o processo pai: no processo pai o controle é retornado diretamente do  fork  , enquanto no processo filho o controle é passado para o local label  label . O processid  retornado pela  chamada fork  é o ID do outro processo.

Como os pontos de retorno do processo pai e do processo filho são diferentes,   cada imagem existente após a bifurcação pode determinar se é o processo pai ou o processo filho.

5.2 Oleoduto

 Os processos podem se comunicar com processos relacionados usando as mesmas  chamadas de sistema de leitura  e  gravação que para E/S do sistema de arquivos. transferir

filep = pipe()

Retorna um descritor de arquivo  filep e cria um canal interprocesso chamado pipe .  Esse canal é passado do processo pai para o filho na imagem por meio de uma chamada de bifurcação , como qualquer outro arquivo aberto  . Ao ler usando um descritor de arquivo pipe, ele primeiro esperará que outro processo grave usando o mesmo descritor de arquivo pipe. Neste ponto, os dados são passados ​​entre as imagens dos dois processos. Nenhum dos processos precisa saber que estão lendo e gravando pipes em vez de arquivos comuns.

Embora a comunicação entre processos por meio de pipes seja uma ferramenta bastante valiosa (consulte §6.2), não é um mecanismo totalmente geral, pois os pipes devem ser estabelecidos por um ancestral comum dos processos relacionados.

5.3 Execução do programa

Outra primitiva de sistema importante é

execute(file, arg1, arg2, ..., argn)

Ele solicita ao sistema que leia e execute  o programa denominado arquivo  , e passe na string os parâmetros  arg1 , arg2 , ..., argn . Normalmente, arg1  deve ser   a mesma string do arquivo para que o programa possa determinar o nome com o qual foi invocado. Todo o código e dados no processo usando  execute  são substituídos desse arquivo, mas arquivos abertos, diretórios atuais e relacionamentos entre processos não são alterados.  Retorne da primitiva de execução apenas se a chamada falhar, por exemplo, porque o arquivo não pode ser encontrado ou porque seu bit de permissão de execução não está definido;  é semelhante a uma instrução de máquina "jump", em vez de uma chamada de sub-rotina.

5.4 Sincronização de processos

Outra chamada de sistema que manipula o processo

processid = wait()

Faz com que seu chamador suspenda a execução até que um de seus processos filho conclua a execução. Em seguida,  wait retorna o processid  do processo encerrado  . Se o processo de chamada não tiver filhos, um retorno de erro será obtido. wait  também pode representar algum estado de processos filhos e até mesmo estados de netos ou ancestrais mais distantes; veja §5.5.

5.5 Rescisão

afinal,

exit(status)

Encerra um processo, destrói sua imagem, fecha os arquivos abertos e geralmente encerra o processo. Quando o processo pai é notificado por meio  da  primitiva de espera, o processo pai pode obter o status especificado ( status ); se o processo pai foi encerrado, o processo avô pode obter o status e assim por diante. Os processos também podem terminar devido a várias ações ilegais ou sinais gerados pelo usuário (§7 abaixo).

6. Casca

Para a maioria dos usuários, a comunicação com o UNIX é feita por meio de um programa chamado Shell. Um shell é um interpretador de linha de comando: ele lê as linhas inseridas pelo usuário e as interpreta como solicitações para executar outros programas. Uma linha de comando, em sua forma mais simples, consiste no nome do comando e nos parâmetros do comando, todos os parâmetros separados por espaços:

command arg1 arg2 ... argn

O shell divide nomes de comando e argumentos em strings separadas. Em seguida, procure um arquivo chamado  command  , command  pode ser um nome de caminho, incluindo os caracteres "/" para especificar qualquer arquivo no sistema. Se o comando for encontrado  , ele é trazido para o núcleo e executado. Os parâmetros coletados pelo shell podem ser acessados ​​por comandos. Quando a execução do comando termina, o shell retoma sua própria execução e indica sua prontidão para aceitar outro comando com um caractere de prompt.

Se o comando file  não puder ser encontrado , o shell prefixa o comando com a string  /bin/ e tenta localizar o arquivo novamente. O diretório  /bin  contém todos os comandos comumente usados.

6.1 E/S padrão

A discussão de E/S no §3 acima parece implicar que todo arquivo usado por um programa deve ser aberto ou criado pelo programa para obter um descritor de arquivo para o arquivo. No entanto, um programa executado pelo shell começa com dois arquivos abertos com descritores de arquivo 0 e 1. Quando esse programa começa a ser executado, o arquivo 1 é usado para gravação, que é melhor entendido como o arquivo de saída padrão. Exceto conforme observado abaixo, este arquivo é a máquina de escrever do usuário. Portanto, os programas que desejam gravar informações de prompt ou diagnóstico geralmente usam o descritor de arquivo 1. Por outro lado, o arquivo 0 é usado por padrão para leitura e geralmente é lido por programas que desejam ler as informações inseridas pelo usuário.

O shell pode alterar a atribuição padrão desses dois descritores de arquivo para a atribuição padrão da tela e do teclado da máquina de escrever. Se um dos parâmetros de uma linha de comando for prefixado com ">", o descritor de arquivo 1 se referirá ao arquivo nomeado após o ">" durante a execução do comando. Por exemplo,

ls

Normalmente, os nomes dos arquivos no diretório atual são listados na máquina de escrever. Ordem

ls >there

Crie um arquivo nomeado   e coloque os resultados listados no arquivo. Assim, o argumento ">there" significa "colocar a saída  ". por outro lado,

ed

Geralmente em um editor, que aceita solicitações do usuário por meio de uma máquina de escrever. Ordem

ed <script

Um arquivo que interpreta  o script  como comandos do editor, então "<script" significa "obter entrada do  script  ".

Embora o nome do arquivo após o "<" ou ">" pareça ser um argumento para o comando, na verdade ele é interpretado inteiramente pelo shell e não é passado para o comando. Portanto, nenhum código especial é necessário dentro do comando para lidar com o redirecionamento de E/S, apenas use os descritores de arquivo padrão 0 e 1 onde apropriado.

6.2 Filtros

Aqui, o conceito de E/S padrão é estendido para permitir que a saída de um comando seja direcionada para a entrada de outro comando. Uma sequência de comandos separados por barras verticais faz com que o shell execute todos os comandos simultaneamente e faça com que a saída padrão de cada comando seja passada para a entrada padrão do próximo comando na sequência. Portanto, na linha de comando

ls | pr –2 | opr

, ls  lista os nomes de arquivo no diretório atual; sua saída é passada para  pr , que  pagina a entrada com cabeçalhos de data. Parâmetro "-2" significa coluna dupla. Da mesma forma, a saída de pr  é alimentada em  opr  , um comando que canaliza sua entrada em um arquivo para impressão off-line.

Esse processo também pode ser implementado de uma maneira mais complicada:

ls >temp1
pr –2 <temp1 >temp2
opr <temp2

Em seguida, exclua os arquivos temporários. Na ausência da capacidade de redirecionar a saída e a entrada, uma abordagem mais desajeitada é exigir que o  comando ls  aceite as solicitações do usuário para paginar sua saída, imprimi-la em formato de várias colunas e providenciar para que sua saída seja entregue offline. Na verdade, esperar que  os autores de comandos como ls  forneçam uma gama tão ampla de opções de saída é surpreendente e, de fato, imprudente por razões de eficiência.

Programas como  o pr  que copiam a entrada padrão (após o processamento) para a saída padrão são chamados de filtros . Alguns filtros que consideramos úteis fazem tradução de caracteres, classificação de entrada e criptografia e descriptografia.

6.3 Separador de Comandos: Multitarefa

Outro recurso fornecido pelo shell é relativamente simples. Os comandos não precisam estar em linhas separadas, mas podem ser separados por ponto e vírgula.

ls ; ed

listará primeiro o conteúdo do diretório atual e, em seguida, entrará no editor.

Um recurso relacionado é mais interessante. Se um comando for seguido por "&", o shell não espera o final do comando antes de solicitar, mas se prepara imediatamente para aceitar novos comandos. por exemplo

as source >output &

A fonte é   montada e os diagnósticos são enviados para  a saída ; não importa quanto tempo leve a montagem, o shell retorna imediatamente. Quando o shell não espera a conclusão de um comando, ele imprime o ID do processo que está executando o comando. Esse ID pode ser usado para aguardar a conclusão do comando ou para encerrar o comando. "&" pode ser usado várias vezes em uma linha:

as source >output & ls >files &

A compilação e listagem simultâneas acontecem em segundo plano. No exemplo acima usando "&", um arquivo de saída diferente de Typewriter foi fornecido; caso contrário, a saída dos vários comandos seria misturada.

O shell também permite parênteses nas operações acima. Por exemplo:

(date; ls) >x &

Imprima a data e hora atuais, seguidas por uma listagem do diretório atual, para o arquivo  x  . Shell também retorna imediatamente, aguardando a próxima solicitação.

6.4 Shell como comando: arquivo de comando

O próprio shell é um comando e pode ser invocado recursivamente. Suponha que o arquivo  tryout  contenha as seguintes linhas

as source
mv a.out testprog
testprog

O comando mv  faz com que o arquivo  a.out  seja renomeado  como testprog . a.out  é a saída (binária) do montador, pronta para execução. Portanto, se as três linhas de código acima forem inseridas no console, a fonte  será montada, o programa resultante será nomeado  testprogtestprog será executado . Quando essas linhas são colocadas  em um arquivo tryout  , o comando

sh < tryout

Deixe o programa Shell  executar  esses comandos em sequência.

O shell tem muito mais recursos, incluindo a substituição de argumentos e a capacidade de construir uma lista de argumentos a partir de um subconjunto especificado de nomes de arquivo em um diretório. Você também pode executar comandos condicionalmente com base em comparações de strings ou na existência de um determinado arquivo, bem como realizar transferências de controle em arquivos de sequência de comandos.

6.5 Implementação da Shell

Agora é hora de obter uma visão geral de como o Shell funciona. Na maioria das vezes, o shell está esperando que o usuário insira um comando.  A chamada read do shell retorna quando um caractere de nova linha é digitado para encerrar a linha atual  . O shell analisa a linha de comando e converte os parâmetros em um formato adequado para  execução  . Então ligue para  fork . O código para o processo filho ainda é o código do shell, que tenta chamar execute com os parâmetros apropriados  . Se executável, o programa com o nome dado é trazido e executado. Ao mesmo tempo,   outro processo gerado por fork , o processo pai, aguarda a morte do processo filho. Depois disso, o shell sabe que o comando acabou, então ele digita seu prompt e lê a máquina de escrever para outro comando.

Nesse framework, a implementação de processos em segundo plano é trivial; sempre que a linha de comando contém "&", o Shell simplesmente não espera que a thread criada por ele execute o comando.

Felizmente, todos esses mecanismos se integram perfeitamente ao conceito de arquivos de entrada e saída padrão. Quando um processo é criado pela  primitiva fork  , ele não apenas herda a imagem principal do processo pai, mas também todos os arquivos atualmente abertos no processo pai, incluindo arquivos com descritores de arquivo 0 e 1. Claro, o Shell usa esses dois arquivos para ler linhas de comando e escrever prompts e diagnósticos e, em circunstâncias normais, seus processos filhos – os programas de comando – herdam esses arquivos automaticamente. No entanto, quando um parâmetro com "<" ou ">" é fornecido, a sub-rotina permitirá que o descritor de arquivo de I/O padrão 0 ou 1 se refira ao arquivo com o nome especificado pelo parâmetro correspondente antes de executar execute. Isso é fácil de fazer porque, por convenção, quando um novo arquivo é aberto (ou criado), um menor descritor de arquivo não utilizado é alocado; então, basta fechar o arquivo 0 (ou 1) e abrir o arquivo especificado, ou seja, Can. Como o processo que executa o programa de comando será encerrado diretamente após a execução, quando o processo for encerrado, a associação entre o arquivo especificado após "<" ou ">" e o descritor de arquivo 0 ou 1 será encerrada automaticamente. Portanto, o shell não precisa saber os nomes reais de seus próprios arquivos padrão de entrada e saída, porque não precisa reabrir esses arquivos.

Os filtros são extensões simples do redirecionamento de E/S padrão, usando pipes em vez de arquivos.

Em circunstâncias normais, o loop principal do shell nunca termina. (O loop principal inclui  o branch retornado por fork  que pertence ao processo pai, ou seja, o branch que executa  wait e depois lê a próxima linha de comando). Uma maneira de um shell terminar é detectar uma condição de fim de arquivo (EOF) em seu arquivo de entrada. Assim, quando o shell é executado como um comando com um determinado arquivo de entrada, como

sh < comfile

 Os comandos no  comfile serão executados até que o  ponto final do  comfile seja alcançado; então  , a instância do shell invocada por sh será encerrada. Como o processo shell é filho de outra instância shell, a espera realizada nesta última retorna para que o próximo comando possa ser processado.

6.6 Inicialização

A instância do Shell na qual o usuário digita comandos é um processo filho de outro processo. A etapa final na inicialização do UNIX é criar um processo e chamar (por  execute ) um   programa chamado init . A função do init  é criar um processo para cada canal da máquina de escrever que o usuário disca. Cada subinstância de init  abre a máquina de escrever correspondente para entrada e saída. Como  nenhum arquivo está aberto quando init é chamado  , em cada processo, o descritor de arquivo 0 apontará para o teclado da máquina de escrever e o descritor de arquivo 1 apontará para a impressora. Cada processo digitaria uma mensagem solicitando que o usuário se logasse e aguardaria, lendo a resposta do usuário na máquina de escrever. A princípio, ninguém está logado, então todos os processos simplesmente travam. Por fim, alguém insere seu nome ou outro ID. A instância correspondente do init  é ativada, recebe a linha de login e lê o arquivo de senha. Se o nome de usuário for encontrado e ele puder fornecer a senha correta, init  alterna para o diretório atual padrão do usuário, define o ID do usuário do processo como o ID de login e executa  execute  para iniciar um shell. Neste ponto, o shell está pronto para receber comandos e o protocolo de login está completo.

Ao mesmo tempo, o caminho principal do init  (ou seja, o corpo pai das entidades filhas que mais tarde se tornarão Shells) executa um  wait . Se um dos processos filho terminar, seja porque o shell detectou o fim do arquivo ou porque o usuário digitou um nome ou senha incorretos, esse caminho para init  simplesmente recria o processo extinto e o processo é reiniciado. Abra a entrada apropriada e arquivos de saída e clique no prompt de login novamente. Assim, o usuário pode sair simplesmente digitando a sequência de fim de arquivo, salvando um comando shell.

6.7 Outros programas como shells

O Shell mencionado acima é projetado para permitir que os usuários aproveitem ao máximo as facilidades do sistema, pois possui um modo de proteção adequado ao executar qualquer programa. No entanto, às vezes queremos uma interface de sistema diferente, mas isso é igualmente fácil de conseguir.

Para lembrar, quando um usuário efetua login com sucesso fornecendo um nome e senha, o init  geralmente invoca um shell para interpretar a linha de comando. A entrada de um usuário no arquivo de senha pode conter o nome de um programa que será chamado após o login em vez do shell. Este programa é livre para interpretar as informações do usuário da maneira que quiser.

Por exemplo, para um usuário do sistema de edição de secretária, a entrada do arquivo de senha especifica o editor  ed  em vez de Shell. Desta forma, quando os usuários do sistema de edição efetuam login, eles estão no editor e podem começar a trabalhar imediatamente; ao mesmo tempo, são impedidos de chamar programas UNIX que não desejam usar. Provou-se ser desejável permitir a saída temporária do editor para executar formatadores e outros utilitários.

Alguns jogos no UNIX (como xadrez, blackjack, tic-tac-toe 3D) incorporam um ambiente mais restritivo. Cada jogo tem uma entrada no arquivo de senha que especifica o programa de jogo correspondente a ser invocado em vez do shell. Quando as pessoas se conectam como jogadores de um desses jogos, elas ficam presas ao jogo, incapazes de explorar as partes potencialmente mais interessantes do UNIX como um todo.

7. Armadilha

O hardware do PDP-11 pode detectar algumas falhas de programa, como referências a memória inexistente, instruções não implementadas e uso de endereços ímpares onde eram esperados endereços pares. Essas falhas podem fazer com que o processador trave nas rotinas do sistema. Quando uma operação ilegal é detectada, a menos que outras providências sejam tomadas, o sistema encerra o processo e grava a imagem do usuário no arquivo principal ( core ) no diretório atual. O depurador pode então ser usado para determinar o estado do programa quando ocorreu a falha.

Quando um programa está em loop, ou produzindo uma saída indesejada, ou seu usuário tem outras ideias, o programa pode ser encerrado usando o sinal de interrupção , que é gerado digitando o caractere "excluir". A menos que uma ação especial seja tomada, este sinal apenas faz com que o programa aborte a execução sem gerar um arquivo de imagem central.

Há também um sinal de quit ( quit ) para forçar a construção de uma imagem principal. Assim, mesmo que um programa que entra em loop inesperadamente seja abortado sem pré-arranjo, sua imagem central ainda pode ser verificada.

Falhas geradas por hardware, bem como sinais de interrupção e saída, podem ser ignoradas ou detectadas por um processo sob solicitação. Por exemplo, o shell ignora os sinais de saída para evitar que a saída faça com que o usuário efetue o logout. O editor irá capturar o sinal de interrupção e voltar para a camada de comando. Isso é útil porque as impressões longas podem ser interrompidas sem perder o trabalho em andamento (o editor opera em uma cópia do arquivo que está sendo editado). Sistemas sem hardware de ponto flutuante interceptam instruções não implementadas e interpretam instruções de ponto flutuante.

8. Perspectivas

Talvez paradoxalmente, muito do sucesso do UNIX se deva ao fato de que ele não foi projetado para nenhum propósito pretendido. Um de nós (Thompson), insatisfeito com a infra-estrutura informática existente, descobriu o pouco utilizado sistema PDP-7 e partiu para criar um ambiente mais amigável, escrevendo a primeira versão. Este esforço, em grande parte pessoal, foi suficientemente bem sucedido para interessar os restantes autores e outros que mais tarde levou à aquisição de um PDP-11/20, dedicado a suportar sistemas de edição e formatação de texto. Então foi a vez do 20/11 ser eliminado, e o UNIX provou ser útil o suficiente para convencer a administração a investir no PDP-11/45. O objetivo de todo o nosso trabalho sempre foi focar em estabelecer um relacionamento confortável com as máquinas e explorar ideias e invenções em sistemas operacionais. Não somos obrigados a fazer o que os outros querem e somos gratos por essa liberdade.

Em retrospecto, três fatores podem ser vistos que influenciaram o design do UNIX.

Primeiro, porque somos programadores, naturalmente projetamos sistemas para tornar mais fácil escrever, testar e executar programas. Para a conveniência da programação, esperamos que o desempenho mais proeminente do sistema seja para ser usado de forma interativa, embora a versão inicial suporte apenas um usuário. Acreditamos que um sistema interativo projetado adequadamente é mais eficiente e satisfatório de usar do que um sistema "em lote". E esse sistema é bastante fácil de adaptar ao uso não interativo e não vice-versa.

Em segundo lugar, sempre houve limites bastante rígidos para o tamanho do sistema e seu software. Por outro lado, devido à necessidade de razoável eficiência e expressividade, esta restrição de tamanho promove não só uma economia econômica, mas também uma elegância de design. Esta pode ser uma versão grosseira da filosofia da "redenção pelo sofrimento", mas, em nosso caso, funcionou.

Em terceiro lugar, o sistema pode e se mantém praticamente desde o início. Este fato é mais importante do que parece. Se os projetistas de um sistema são forçados a usá-lo, eles rapidamente se conscientizam de suas deficiências funcionais e cosméticas e têm um forte incentivo para corrigi-los antes que seja tarde demais. Uma vez que todos os programas fonte estão sempre disponíveis online e facilmente modificados, estamos dispostos a modificar e reescrever o sistema e seu software sempre que uma nova ideia é inventada, descoberta ou alguém a propõe.

Os aspectos do UNIX discutidos neste artigo demonstram claramente pelo menos as duas primeiras das considerações de design acima. Por exemplo, a interface para o sistema de arquivos é muito conveniente do ponto de vista da programação. A interface foi projetada para ser o mais baixa possível, eliminando assim a distinção entre vários dispositivos e arquivos e entre acesso direto e sequencial. Não há necessidade de grandes rotinas de "método de acesso" para isolar o programador das chamadas do sistema; na verdade, todos os programas de usuário chamam o sistema diretamente ou usam uma pequena rotina de biblioteca com apenas algumas dezenas de instruções, armazenando em buffer vários caracteres e lendo de uma só vez, buscar ou escrever.

Outro aspecto importante da conveniência da programação é que não há "blocos de controle" com estruturas complexas, partes das quais são mantidas e dependem do sistema de arquivos ou de outras chamadas do sistema. De um modo geral, o conteúdo do espaço de endereço do programa pertence ao próprio programa e tentamos evitar impor restrições às estruturas de dados no espaço de endereço.

Considerando que todos os programas devem ser capazes de usar qualquer arquivo ou dispositivo como entrada ou saída, também é desejável, do ponto de vista da eficiência de espaço, incorporar considerações dependentes do dispositivo no sistema operacional. A única compensação parece ser carregar as rotinas de cada dispositivo em todos os programas, o que ocupa muito espaço, ou confiar em algum método para vincular dinamicamente as rotinas de cada dispositivo quando realmente necessário, o que custa mais mais recursos de hardware.

Da mesma forma, esquemas de controle de processo e interfaces de comando provaram ser convenientes e eficazes. Como o shell é executado como um programa de usuário comum e trocável, ele não ocupa espaço inerente no sistema e pode atingir o efeito desejado com pouca sobrecarga. Em particular, sob esta estrutura, uma vez que o shell é executado como um processo que bifurca outros processos para executar comandos, redirecionamento de E/S, processos em segundo plano, arquivos de comando e interfaces de sistema selecionáveis ​​pelo usuário e outros conceitos tornaram-se comuns na realização.

8.1 Impacto

O sucesso do UNIX não se deveu tanto a novas invenções, mas sim à exploração de um rico e bem escolhido conjunto de ideias, e especialmente porque mostrou que essas ideias poderiam ser a chave para a implementação de um sistema operacional pequeno, mas poderoso.

A operação fork que implementamos   basicamente apareceu no sistema de compartilhamento de tempo de Berkeley  [8] . De muitas maneiras, fomos influenciados pelo Multics, que apresentou uma forma única de chamadas de sistema de E/S  [9] , que também surgiu com o nome do Shell e sua funcionalidade geral. O design inicial do Multics também nos deu uma dica de que o shell deveria criar um processo para cada comando, embora naquele sistema esse conceito tenha sido abandonado posteriormente por razões de eficiência. TENEX  [10]  também adota um esquema semelhante.

citar

40 Years of Unix: Yearbook of Unix_40 Years of Unix: Yearbook of Unix_haoel's Blog-CSDN Blog

Lenda do Unix (Parte 1)

Lenda do Unix (Parte 2)

Na verdade, o Unix é muito simples

the-unix-time-sharing-system-chinese/the_unix_time-sharing_system_CN.md at master · chocovon/the-unix-time-sharing-system-chinese · GitHub

https://bbs.csdn.net/topics/39060161

Acho que você gosta

Origin blog.csdn.net/qq8864/article/details/130704963
Recomendado
Clasificación