Monday 17 July 2017

Comunicação assíncrona waitforexit


Esta pergunta já tem uma resposta aqui: Eu preciso gerar um processo filho que é um aplicativo de console, e capturar sua saída. Eu escrevi o seguinte código para um método: No entanto, isso não retorna nada. Eu não acredito que o evento OutputDataReceived está sendo chamado de volta, ou o comando WaitForExit () pode estar bloqueando o segmento para que ele nunca retorno de chamada. EDIT: Parece que eu estava tentando muito com o retorno de chamada. Fazendo: Parece funcionar bem. Perguntou Nov 12 08 às 23:14 marcado como duplicado por Lu Vnh Ph250c. Poke c Os usuários com o crachá c podem fechar sozinhos as perguntas c como duplicatas e reabri-las conforme necessário. Esta questão foi feita antes e já tem uma resposta. Se essas respostas não abordarem completamente a sua pergunta, faça uma nova pergunta. Se você não estiver indo interagir com o aplicativo e apenas se preocupar com sua saída, não use a maneira Async BeginOutputReadLine () e Start () de fazê-lo. Descobri que estes não são muito fiáveis ​​e, por vezes, podem truncar o início da saída do aplicativo. Ndash Michael Graczyk Jul 16 12 at 23:47 Isn39t que um impasse à espera de acontecer MSDN Docs dizer que você arrisca um deadlock se ouvir a saída e erro ao mesmo tempo. O aplicativo será interrompido se o buffer de erro estiver cheio e aguarde que ele seja esvaziado. Mas você não está esvaziando o buffer de erro até que o buffer de saída esteja concluído (o que não será como o aplicativo está aguardando o buffer de erro). Ndash Michael Bisbjerg Mar 31 13 at 15:42 A resposta de Judah não funcionou para mim (ou não está completo) como a aplicação estava saindo após o primeiro BeginOutputReadLine () Isso funciona para mim como um trecho completo, lendo a saída constante de Um ping: Apresentando Pipes Justin Van Patten O Orcas October Community Technology Preview (CTP) inclui novos tipos que tornam mais fácil para os desenvolvedores usar pipes de código gerenciado. Os pipes são usados ​​para comunicação entre processos (IPC) entre processos em execução na mesma máquina ou processos em execução em qualquer outra máquina Windows dentro de uma rede. We8217ve adicionou suporte para pipes anônimos e nomeados. Os novos tipos de pipe podem ser encontrados em System. Core. dll dentro do namespace System. IO. Observe que após o CTP de outubro saiu, movemos os tipos de pipe de System. IO para System. IO. Pipes, assim os tipos serão no novo namespace em CTPs futuro. Tubulações anônimas Tubulações anônimas são baseadas em caracteres e são half-duplex. Eles não podem se comunicar através da rede e suportam apenas uma instância de servidor único. Esses pipes são mais úteis para comunicação entre threads ou entre processos pai e filho onde os identificadores pipe pode ser facilmente passados ​​quando o processo filho é criado. Exemplo 1: pipes Anonymous O exemplo a seguir demonstra o envio de uma seqüência de caracteres de um processo pai para um processo filho. Uma linha é lida do console do pai e enviada para o processo filho. O processo filho, em seguida, grava a seqüência de caracteres que ele recebeu do processo pai para o console. Processo processo novo Processo () usando (AnonymousPipeServerStream pipeStream novo AnonymousPipeServerStream (PipeDirection. Out, HandleInheritability. Inheritable)) Tubulações anônimas são doces Echo: pipes Anonymous são doces pipes nomeados Tubos nomeados são muito mais poderosos do que pipes anônimos. Eles podem ser duplex, através da rede e podem suportar várias instâncias de servidor de um único nome, tornando-os ideais para quick-to-build e fáceis de se conectar a servidores multithreaded. Além disso, eles suportam a comunicação baseada em mensagens de modo que um processo de leitura possa ler mensagens de comprimento variável exatamente como enviado pelo processo de escrita. Finalmente, os pipes nomeados suportam a representação permitindo que os processos de conexão usem seu próprio conjunto de permissões em servidores remotos. Exemplo 2: pipes nomeados Nos casos em que o segundo processo não pode herdar o identificador de pipe, pipes nomeado pode ser usado. O exemplo a seguir demonstra o envio de strings do processo do cliente para o processo do servidor. Usando (NamedPipeServerStream pipeStream new NamedPipeServerStream (8220testpipe8221)) usando (StreamReader sr novo StreamReader (pipeStream)) enquanto ((temp sr. ReadLine ()) null) Console. WriteLine (8220: 8221. DateTime. Now, temp) using (NamedPipeClientStream pipeStream Novo (NamedPipeClientStream 8220testpipe8221)) usando (StreamWriter sw novo StreamWriter (pipeStream)) enquanto ((temp Console. ReadLine ()) null) Exemplo 3: pipes nomeados com mensagens pipes Named também suporte comunicações com base em mensagem. Isso permite que um processo de leitura para ler mensagens de comprimento variável precisamente como enviado pelo processo de escrita. O exemplo a seguir exibe como essas mensagens são enviadas e lidas. UTF8Encoding codificação new UTF8Encoding () string mensagem1 8220Named Pipe Message Exemplo.8221 string message2 8220Another Named Pipe Message Exemplo.8221 using (NamedPipeServerStream pipeStream new NamedPipeServerStream (8220messagepipe8221. PipeDirection. InOut, 1, PipeTransmissionMode. Message, PipeOptions. None)) // Letrsquos Enviar duas mensagens. O TaskMaster, Objective-C, ICpak e Software-IC são marcas registradas da The Stepstone Corporation. Introdução (toc) O TaskMaster suporta uma das várias camadas em uma arquitetura orientada a objetos de vários níveis que a Stepstone vem construindo de forma ascendente desde a sua criação. Este documento irá descrever os tipos de sistemas programáveis ​​pelo usuário que nós imaginamos, uma arquitetura de software multinível para construí-los, o papel que TaskMaster desempenha dentro desta arquitetura e como TaskMaster se relaciona com camadas adjacentes, como C eo sistema operacional de plataformas. TaskMaster é uma biblioteca de funcionalidade baseada em C que suporta multitarefa leve e manipulação de exceções, de tal forma que os aplicativos cliente são portáteis em plataformas de hardware / software. Por portáteis, queremos dizer que os aplicativos cliente acessam recursos do TaskMasters através de uma interface de programa de aplicativo (API) que é independente de plataforma. Essa API é suportada por uma camada de código dependente da plataforma (PDL) que adapta a API ao hardware e ao software do host (sistema operacional). Para plataformas que já suportam alguns dos recursos do TaskMasters (Mach, OS / 2), o PDL usa as capacidades das plataformas. Para plataformas que não possuem esses recursos (Unix, MS / DOS), o PDL fornece-los manipulando diretamente o hardware (registros de máquina) que está subjacente ao ambiente de tempo de execução C. Para as plataformas Sun Unix, que atualmente é a única plataforma para a qual o TaskMaster foi portado, o PDL é escrito em C com uma sub-rotina pequena (8 instruções) no assembler. As perguntas dos clientes são bem-vindas sobre o TaskMaster, na sua plataforma atual do Sun ou portadas para plataformas adicionais. Entrar em contato com K. K. Tan em 203 426 1875. Sistemas programáveis ​​pelo usuário (toc) Os documentos complementares, Planejando o Software Revolução Industrial e Há uma Bala de Prata2 descrevem a visão de longo alcance por trás desse trabalho. Eles vêem a crise do software como um obstáculo para passar da Idade da Manufatura para a Idade da Informação, onde os computadores se tornam veículos pessoais de todos em uma rede de informação global, a Ford Modelo T da Era da Informação. Eles vêem a crise de software como sendo resolvido como a escassez de telefonista foi uma vez resolvido, tornando cada usuário de computador um programador. Naturalmente, o programador não significará o que faz hoje. A palavra vai adquirir muitos significados diferentes apropriados às habilidades e interesses de diversas classes de usuários em diferentes níveis de uma arquitetura de software multinível. Por exemplo, Tom, Dick e Harry são programadores típicos com habilidades e interesses inteiramente diferentes: Tom é um programador no sentido C desta palavra. Tom desempenha o papel que as empresas de fabricação de silício desempenham em hardware, fabricando componentes de silício de nível de gate e bloco em componentes de nível de chip que outros com habilidades menos especializadas, como Dick, podem usar. As tecnologias de nível de gate e bloco, sejam elas de hardware ou software, são atividades acopladas em que a otimização do produto, não seu desenvolvedor, é primordial. Dick é um programador no sentido Smalltalk ou Objective-C da palavra. Dick joga o papel que os vendedores de placa jogam em hardware, montando componentes de nível de chip que Tom produz em componentes de nível de cartão que outros com habilidades menos especializadas, como Harry, podem usar. As tecnologias de nível de chip, seja em hardware ou software, são atividades acopladas livremente nas quais as preocupações dos desenvolvedores, tais como plugabilidade, permutabilidade e reutilização, são primordiais. As tecnologias de nível de chip suportam essa demarcação crucial entre tecnologias de fabricação fortemente acopladas que somente especialistas altamente qualificados usam, como fundições de silício e as tecnologias de montagem muito mais simples e pouco acopladas que os não especialistas podem usar, como chaves de fenda e ferros de soldar. Harry é um usuário final, um do resto de nós. Harry é um especialista em problemas de domínio, sem especialização em software especializado. O objetivo da arquitetura a ser discutida neste artigo é fazer de Harry um programador de nível de cartão precisamente nesse sentido limitado, mas mesmo assim muito real, que os usuários finais são designers de hardware em nível de cartão quando escolhem quais cartões comprarão para personalizar um arquivo pessoal computador. Harry pode ser um funcionário de um escritório de seguros, um gerente de uma agência bancária ou, no exemplo a seguir, um proprietário usando um computador pessoal para gerenciar as finanças domésticas. Hoje, Harry pode estar usando um programa de finanças pessoais como o ManagingYourMoney (MYM) no Macintosh. Esses programas permitem que Harry gerencie seus ativos, passivos, receitas e despesas para fornecer uma leitura instantânea do valor pessoal líquido. O que os programas e sistemas de hoje não fazem é ajudar essa classe de usuários a criar suas próprias soluções personalizadas para suas necessidades específicas de problemas. O Macintosh não suporta a programação do usuário final. Por exemplo, suponha que alguns dos ativos da Harrys estão em ações, títulos e fundos mútuos, cuja contribuição para o patrimônio líquido varia diariamente. Quando Harry se cansa de digitar os preços das ações do jornal, ele pode se aventurar na Era da Informação, programando seu emulador de terminal para adquirir informações de preços de um serviço de informação como Compuserve, eletronicamente. Embora Harry provavelmente nunca programe em C ou até mesmo em Smalltalk, ele poderia muito bem chegar ao ponto de usar uma planilha como o Excel para gerenciar um banco de dados de tendências de preços ao longo do tempo, talvez usando Excels cartografando instalações para mostrar as tendências graficamente. Soluções não programáveis ​​de hoje são adequadas somente enquanto Harry permanecer persuadido de que todo esse trabalho em torno de 3 para abrir e fechar documentos, iniciar e parar aplicativos e cortar e colar números é uma grande melhoria em vez de digitar cada número manualmente Se Harry insistir em um Maneira de construir seu próprio aplicativo para calcular, com um único clique, um gráfico de seu patrimônio líquido pessoal como ele muda ao longo do tempo, seus desejos têm excedido o que o Macintosh em particular, ea indústria de software como um todo, pode entregar hoje. A solução visualizada neste documento é mostrada na Figura 1. Figura 1: Um programa que pode ser criado por um usuário final, escrito em uma linguagem de programação orientada a objetos icônica não-processual de nível de cartão do tipo previsto neste documento. Esta figura é composta de ícones Macintosh e despejos de tela para indicar que cada ícone se comporta individualmente muito como no Macintosh hoje. No entanto, existem diferenças significativas. A imagem não é uma coleção de ícones que você vê é tudo que você pode obter, que só pode ser usado manualmente e (exceto para cortar e colar manualmente) individualmente. A figura é a lista de fontes de um programa que Harry escreveu em uma icônica linguagem de programação orientada a objetos em nível de cartão, cujos objetos são baseados nas instalações de multitarefas leves deste artigo. Os ícones representam tarefas leves ou objetos de nível de cartão. Harry pode salvar seu programa Compute Net Worth como um novo componente de nível de cartão e usá-lo ao lado dos mostrados aqui. No entanto, Harry não construiu os mostrados nesta figura. Comprou-os em outra parte, de Dick, que os montou dos componentes do nível de microplaqueta fabricados por Tom. As tarefas são inicialmente inativas, cada entrada aguardando nas setas de entrada. Quando o botão Compute Net Worth (em outra tela não mostrada aqui) é clicado, isso produz o sinal que o ícone do emulador de terminal está aguardando. Isso dispara a tarefa que o ícone do emulador de terminal controla, que pode ser uma encarnação de nível de cartão de um programa como o SmartComII, para discar o telefone, entrar no Compuserve e digitar comandos para baixar informações financeiras. Os três ícones da busca da corda são maneira de Harrys de tratar do fato que o Compuserve não fornece a informação financeira em um formato que sua planilha possa aceitar. Ele usou um processo de divisão de fluxo (o pequeno círculo preto) para enviar os dados para três processos de busca de seqüência de caracteres e programou-os para encontrar preços de ações específicos e formatá-los como esperam as três planilhas de histórico. E assim por diante. Os programadores Unix reconhecerão isso como uma variação no esquema de pipes e filtros do Unix. Mas há uma grande diferença que meu uso de programas familiares do Macintosh não traz claramente. Ao contrário dos tubos Unix que só podem transportar fluxos de bytes, as setas nesta figura podem carregar fluxos de objetos em nível de chip. Ao contrário das planilhas Macintosh que aceitam somente texto, os programas nesta figura são escritos para aceitar dados expressos como objetos. Os objetos podem representar qualquer tipo de dados estruturado, incluindo estruturas baseadas em ponteiros, como listas e árvores. A seta que representa o fluxo de entrada para o ícone da planilha não precisa ser cadeias delimitadas por tabulações que a planilha deve analisar e reformatar internamente. A seta para o objeto de planilha pode carregar um fluxo de objetos. Instâncias de classe Record, não um fluxo de caracteres delimitado por tabulações como planilhas exigem hoje. Arquitetura de Software Multinível (toc) Este documento usará essa palavra de moda, mas mal entendida buzzword, objeto, com precisamente a falta de significado que essa palavra tem no hardware. Ou seja, objeto não vai ter qualquer significado técnico, a menos que qualificado para tornar o contexto arquitetônico claro. Assim como um objeto de hardware de nível de gate não tem nada em comum com um objeto de hardware de nível de chip, cartão ou rack, assim será com os diversos objetos de software na arquitetura de software multinível. A arquitetura foi motivada pela apreciação das arquiteturas multiníveis da engenharia de hardware, onde objetos de hardware de alto nível, como equipamentos de escritório, são construídos a partir de objetos de nível inferior, como cartões, objetos de nível de cartão de objetos de nível inferior, como chips de silício e Objetos de nível de chip de objetos de nível inferior, como blocos de silício e portões. Espera-se que a solução para a crise de software evolua como ocorreu no hardware, estratificando usuários / programadores de acordo com níveis de habilidade e interesses, assim como o mercado de suprimentos de encanamento é estratificado em proprietário, canalizadores, varejistas, fábricas, refinarias e minas. Usuários finais, como Harry, usarão tecnologias simplificadas de modularidade / vinculação suficientes para categorias limitadas de problemas, como os objetos em nível de cartão aqui descritos. Eles vão adquirir os objetos necessários subcontratando o trabalho a mais especializados trabalhadores de nível inferior, como Tom e Dick. A Figura 2 mostra os cinco níveis dessa arquitetura. Embora os objetos em cada nível satisfaçam a definição clássica de objetos como unidades inseparáveis ​​de estado e comportamento, os mecanismos de modularidade / vinculação pelos quais isso é realizado é totalmente diverso nos diferentes níveis: Gate e objetos de nível de bloco são a expressão e Objetos de sub-rotina de linguagens de programação convencionais como Cobol, Pascal e C e de linguagens orientadas a objetos fortemente acopladas como Ada e C. Com a modularidade gate-level (expressões, macros, etc.), a ligação entre objetos é feita inteiramente Em tempo de compilação pelo compilador. Block-level modularidade (bibliotecas de sub-rotina), atrasa ligação até link tempo, onde o trabalho ocorre no vinculador. Objetos de nível de chip são acoplados de forma livre, vinculados dinamicamente, objetos como apresentados em linguagens como Smalltalk e Objective-C. Com modularidade em nível de chip, a vinculação é adiada até o último momento possível. A ligação é feita pelo próprio programa no interesse do acoplamento frouxo, do reusability, do interchangeability, e do pluggability. Todas as camadas subseqüentes compartilham esta propriedade de ligação tardia, embora usando uma diversidade de meios para realizá-la. A figura mostra que C, ao contrário de Ada, fornece suporte limitado para programação em nível de chip em seu mecanismo de função virtual, uma forma de ligação dinâmica. O suporte de Cs para o acoplamento frouxo é limitado por sua ênfase no acoplamento apertado, a verificação de tipo de tempo de compilação baseada em herança e a ligação estática. Os objetos de nível de cartão são objetos de nível de tarefa de sistemas multitarefa leves como Fabrik, Metaphor e Capataz. Eles serão o foco principal deste artigo. A modularidade de nível de cartão estende o significado tradicional de encapsulamento para também fornecer cada objeto com seu próprio segmento de controle, seu próprio relógio interno e sentido de história, além de objetos de nível inferior, que mantêm seu estado e comportamento, mas não sua história . Os objetos de nível de rack são a unidade de modularidade mais antiga e mais encapsulada de todos, a noção familiar de um programa como uma unidade de funcionalidade desenvolvida independentemente que escolhemos quando compramos software na loja de software local ou quando escolhemos o programa a ser executado Um sistema de computador pessoal ou tempo-compartilhado. Figura 2: Objeto-orientado significa coisas diferentes em níveis diferentes de integração. Fatias de torta representam a extensão em que vários idiomas populares suporte trabalho em cada nível. Assim como no hardware, o que distingue a modularidade de nível de chip de modularidade de porta ou bloco é o acoplamento frouxo, a ligação dinâmica ou a capacidade de conexão. Considerando que as tecnologias gate e block-level são tecnologias de fabricação, formas de fabricar coisas de primeiros princípios, chip e tecnologias de alto nível são tecnologias de montagem maneiras de montar as coisas, conectando componentes off-the-shelf de bibliotecas de peças prontas para usar. Nenhum destes níveis são panacéias que eliminam a necessidade de outros níveis. Na escala de qualquer sistema significativo, gate, block e mesmo nível de chip são objetos extremamente pequenos unidades de granularidade - grãos de areia onde os tijolos são necessários. Mas a modularidade de nível de rack de sistemas operacionais como Macintosh e Unix ir muito longe na direção oposta. O encapsulamento rigoroso que torna a modularidade em rack tão útil para embalar pacotes de software como programas independentes, obstrui o intercâmbio livre de informações entre esses objetos de forma tão rigorosa que a programação do usuário final não é possível. A modularidade no nível do cartão fornece um meio termo, intermediário entre os objetos de nível de rack firmemente encapsulados de sistemas operacionais como Unix e os objetos de nível de chip de linguagens de programação como Smalltalk. Exemplo: Fabrik e Smalltalk (toc) A relação entre Fabrik4 e Smalltalk é um excelente exemplo de como objetos de nível de cartão se relacionam com objetos de nível inferior. Fabrik foi escrito em Smalltalk, então ele consiste internamente de objetos de nível de chip. Programadores Smalltalk manipulá-los através da linguagem de programação Smalltalk uma interface de texto orientada a objeto textual apropriado para especialistas em software como Dick. Fabrik projeta um tipo de objeto de nível mais alto na interface do usuário, um objeto de nível de cartão. Não programadores manipulá-los via interface de usuário Fabriks icônica, que é uma linguagem de programação não-textual adequada para não-especialistas como Harry. A partir da perspectiva Harrys, esses novos objetos são intuitivamente atraentes para eles são passíveis de as habilidades de raciocínio da vida cotidiana. Eles se comunicam, não de forma síncrona através da invocação procedural (algo que apenas os programadores acham natural), mas por um modelo assíncrono que está mais intimamente relacionado com a maneira como os objetos familiares da experiência cotidiana se comportam. Objetos em nível de cartão encapsulam uma cópia do segmento de controle de máquinas em um cartão de software, juntamente com os objetos de nível de chip usados ​​para construir esse cartão. São objetos do tipo que os programadores chamam de objetos de processos leves que operam como co-rotinas de um outro, e não sub-rotinas. Eles se comunicam de forma assíncrona enviando objetos de nível de chip através de objetos de canal de comunicação, como fluxos. Como os cartões de software parecem ser executados simultaneamente, sua interface de usuário é exclusivamente intuitiva. Por essa razão, eles são mais fundamentalmente orientados a objetos do que as linguagens processuais de linguagem única de hoje. Como objetos tangíveis da experiência cotidiana, objetos de nível de cartão fornecem seu próprio segmento de controle internamente, eles não se comunicam por invocação procedural, eles não suportam herança, e sua interface de usuário é icônica, não textual. Os objetos de nível de cartão são semelhantes aos pipes e filtros Unix Eles compartilham a mesma noção não processual de processos simultâneos que se comunicam através de objetos de canal de comunicação assíncronos ou pipes. Eles diferem em que os objetos de nível de cartão são processos leves. Eles compartilham um espaço de endereço comum e, portanto, não estão restritos a comunicar fluxos de valor, ou bytes. Os fluxos entre eles podem ser fluxos de referência que podem conter ponteiros incorporados, como listas vinculadas ou árvores. Considerando que os tubos só transportar bytes, córregos podem transportar objetos. Exemplo: Metaphor (toc) Metaphor5 é um exemplo relacionado com diferenças significativas. Fabrik e Metáfora são semelhantes em que ambos apoiam uma linguagem de programação icônica orientada para não-programadores. Ambos são baseados em um não-procedural bolhas e setas paradigma de programação como o Unix pipes e filtros paradigma. Ambos diferem do Unix pelo fato de que as bolhas podem trocar fluxos de referência (estruturas baseadas em ponteiros), como objetos, em vez de apenas fluxos de valor, como com os pipes Unix. A diferença significativa é a ausência da arquitetura aberta que Fabrik adquire de ser baseada em um padrão aberto como Smalltalk. É meu entendimento, baseado em informações possivelmente falhas6. Que a metáfora é agora, ou foi originalmente, com base em hardware proprietário e um sistema operacional proprietário. Isso implica a ausência de um padrão aberto para o nível arquitetônico correspondente ao Unix (integração em rack). Como a Metaphor foi desenvolvida antes que as linguagens de programação orientadas a objetos se tornassem comuns, não há nada, além dos padrões internos de codificação, para suportar o papel de nível de chip que os objetos Smalltalks desempenham em Fabrik. Se os rumores persistentes forem verdadeiros, alguns ou todos os objetos de nível de cartão da Metáfora são codificados em assembler, mesmo os padrões abertos de gate e block que os programadores C tomam para concedidos (sub-rotinas e expressões) estão faltando completamente, ou mais plausivelmente, São fornecidos apenas por normas internas fechadas. Eu entendo que as normas internas de codificação existem porque os objetos de nível de cartão Metaphors se comunicam por uma representação tabular orientada para dados de planilha. Eu entendo que a Metaphor agora vê essa representação como um obstáculo para o crescimento contínuo, presumivelmente porque a dificuldade de forçar dados não tabulares que ocasionalmente ocorrem em aplicativos de clientes para se ajustarem ao modelo de dados tabelares da Metaphors. Eu entendo que aliviar esta e outras barreiras, como a não portabilidade, é a motivação para a recente joint venture Metaphor / IBM Patriot Partners. A abordagem multi-camadas defendida neste documento aborda os problemas que a Metáfora encontrou ao tentar representar todos os mais baixos - estruturas de dados de nível com uma única representação padrão fechada para dados tabulares. Uma linguagem de programação orientada a objetos de nível de chip como Objective-C fornece um padrão aberto para representar dados estruturados. Eles permitem que os programadores desenvolvam novas representações de dados definindo novas classes de objetos, usando recursos orientados a objetos como encapsulamento, herança e polimorfismo. As linguagens são suportadas por ferramentas de programação de última geração como navegadores e por bibliotecas abrangentes de classes dignas de confiança, prontas para usar e pré-testadas. Uma vez que os objetos de nível de chip são, por definição, acoplados de forma solta, acopláveis, polimórficos e verificados de modo dinâmico, os objetos recém-criados podem ser imediatamente reconhecidos e processados ​​por aplicativos pré-existentes sem ter que recompilar ou modificar os aplicativos. Exemplo: TaskMaster e Objective-C (toc) A experiência do Stepstones com Objective-C é um exemplo final de por que uma arquitectura de software multinível é desejável. Considerando que a experiência Metáforas mostra que os programadores qualificados precisam de padrões arquitetônicos de menor nível do que os objetos de nível de cartão Metaphors, Stepstones experiência mostra que os usuários precisam de tipos de objetos de nível mais alto do que os objetos de texto, síncrono Objective-C objetos que se comunicam enviando mensagens uns aos outros Onde o receptor calcula como uma sub-rotina do remetente, e não como uma co-rotina. Os conceitos-chave que distinguem os sistemas programáveis ​​pelo usuário de nível mais alto das linguagens de programação especializadas de hoje, dos quais o Objective-C é um exemplo típico, são interfaces de usuário icônicas (em vez de uma linguagem de programação textual) e concorrentes (assíncronos, Processuais) objetos que operam como coroutinas um do outro, não sub-rotinas. A Stepstone não tomou, e é improvável que tome, a etapa final da construção de sistemas programáveis ​​pelo usuário. Vemos nosso papel como o provedor de componentes horizontais que os clientes usarão para construir soluções verticais para usuários finais. O ICpak 101 é uma biblioteca de classes de fundação que tendem a ser usadas em quase todas as aplicações. O residente mais usado dessa biblioteca é a classe Object, que como a classe raiz da maioria das hierarquias de herança, fornece funcionalidade para todas as outras classes, como o storeOn: / readFrom: mecanismo para converter automaticamente objetos (fluxos de referência) em um caractere (Fluxo de valor) para armazenamento ou transmissão através de redes ou tubos. Esta biblioteca também fornece classes de estrutura de dados comumente úteis como Conjuntos, Dicionários, Arrays, Listas e Strings. Esta biblioteca não é vendida separadamente, mas é fornecida com o compilador. ICpak 201 é uma biblioteca muito maior de classes para a construção de interfaces de usuário icônico de uma maneira independente da plataforma. Sua portabilidade também é devido a uma interface distinta API / PDL. Em muitas plataformas, o PDL do ICpak 201s é baseado no X-Windows, mas este PDL pode ser, e tem sido, baseado em outras bibliotecas de gráficos, incluindo hardware de gráficos nus. TaskMaster é uma biblioteca de classes para a construção de objetos de nível de cartão assíncronos que operam como co-rotinas de um outro, não sub-rotinas. Essas bibliotecas são baseadas no Objective-C, que a Stepstone também comercializa como a tecnologia de ligação de nível de chip, de forma livremente acoplada, para fornecer grandes bibliotecas de classes que estão suficientemente ligadas ao seu ambiente e que podem ser usadas em diversas aplicações de clientes. Objective-C é uma linguagem de programação híbrida orientada a objetos que suporta o modelo de objeto de nível de chip Smalltalk como uma extensão estritamente compatível com os objetos de nível de gate e bloco de uma linguagem de programação convencional como C ou C: Usuários de Stepstones Objective - C compilador, que é baseado em ANSI C, expresso portão e bloco de nível de operações em C. Uma versão baseada em C é planejada, mas não está disponível no momento. Os usuários do compilador Objective-C do NeXTs, que é baseado no GNU C, expressam operações de gate e block-level em C ou C. Stepstone não se vê como um fornecedor de linguagem, mesmo que um dos nossos produtos mais populares seja um compilador Para a linguagem de programação Objective-C. Nós nos vemos como um fornecedor de componentes de software, com a linguagem como tecnologia habilitadora para colar componentes juntos. Não vemos Objective-C como concorrentes com outras linguagens de programação populares, sejam C, Ada, Smalltalk, C ou o shell Unix. Objective-C é uma extensão de C. Na medida em que C é um melhor C, é uma plataforma melhor do que C para a construção de um melhor Objective-C. TaskMaster Descrição técnica (toc) Tanto para o destino. O resto deste documento é um passeio de um veículo para ir lá, de cabine para sala de caldeira. Ele mostrará como os sistemas de nível de cartão programáveis ​​pelo usuário podem ser montados a partir de componentes de software de nível de chip do Objective-C, e estes a partir das tecnologias gate ou block de C ou C. Uma vez que a apresentação será progressivamente mais estreita e mais técnica Daqui em diante, aqueles desinteressados ​​em assuntos de caldeira talvez desejem referir as partes mais técnicas a um especialista. TaskMasters objetivo é fornecer um modelo de objeto leve multitarefa com baixo o suficiente sobrecarga que os programadores não será relutante em usá-lo tão extensivamente como eles usam sub-rotina chamadas e mensagens de hoje. Como as tarefas leves compartilham o mesmo espaço de endereço, elas podem trocar livremente dados estruturados com ponteiros intensivos, como objetos Objective-C. Um sinal dessa filosofia de baixa sobrecarga é que a maioria das facilidades do TaskMasters são acessíveis através de um par de APIs. Além de uma interface de passagem de mensagens resultante da implementação TaskMasters como uma biblioteca de classes Objective-C, cada classe métodos também são embalados para que eles também podem ser chamados como sub-rotinas C comuns, permitindo assim que os programas comuns C invocar TaskMasters funcionalidade completa no Ausência de Objective-C. Em outras palavras, o TaskMaster não é específico do Objective-C e pode ser usado por programas C comuns. No entanto, para maior clareza no que se segue, vou descrever TaskMaster em termos de sua API Objective-C. Tarefas Leves e Alternância de Contexto (toc) As premissas externas de programas C (e Objective-C) normais sobre seu ambiente de execução são bastante simples. Um programa encontra-se carregado em um domínio de computador (um espaço de endereço), dividido em quatro arenas código, dados, pilha e pilha. A arena de código contém o código dos programas, a arena de dados contém os dados estáticos dos programas e as arenas da pilha e do heap estão inicialmente vazias. O contador de programas de máquinas (PC) aponta para o endereço de início de programas na arena de código e seu ponteiro de pilha (SP) aponta para a pilha, que está inicialmente vazia, exceto para argumentos de linha de comando (argc, argv e similares). Registros adicionais também estão normalmente envolvidos, mas, desde que sejam salvos e restaurados corretamente durante a troca de tarefas, será útil ignorá-los. Esta situação inicial é mostrada na Figura 3. Um domínio (talvez um de vários em um sistema de compartilhamento de tempo) é mostrado adjacente a outros dois. O objeto de domínio é pré-inicializado para uso como arenas de código, dados e heap. Estes não são desenhados como regiões separadas, uma vez que são melhor considerados como subdivididos entre os objetos desenhados como pequenos quadrados. A pilha inicial é o retângulo arredondado na parte inferior. The machines registers are shown at the bottom, initialized so that the PC points to the first instruction and the SP points to the first slot of the (empty) stack arena. When TaskMaster is linked into such a program, it is automatically invoked at start up time via Objective-Cs class initialization mechanism. The initialization allocates several TaskMaster objects to formalize the situation as objects. It creates an instance of Domain, theDomain . to model the address space as a whole, and an instance of Task, rootTask . to formalize the root task represented by the initial register settings. The initialization also initializes several global and static variables for its subsequent use, the primary ones being a currentTask variable, initialized to rootTask, and readyQueue . initialized to empty (since no other tasks exist yet). Additional tasks are created when rootTask requests it through any of a variety of ways, differing only in syntactic convenience. What follows will be presented in terms of a uniform syntactic mechanism, Actions . which are modeled after Smalltalks Block mechanism. The Action mechanism will be, but is not yet, supported by the Objective-C compiler. Until it is described later, think of actions as a more powerful, easier to use and understand, replacement for many of the things function pointers are used for in C. The simplest way to create additional tasks (remember, other ways are also provided, including invoking the task creation logic by ordinary C function calls) is to send a fork message to an instance of Action. Actions incorporate a C function that plays the role that main(argc, argv) played for the domain as a whole: aTask anAction fork The message returns a new instance of Task. This involves allocating a stack arena7 for the new task from the heap initialized such that, when the task is executed for the first time, the actions initial C subroutine will find its arguments in the normal fashion for any C subroutine. The newly created task is automatically linked onto the readyQueue for eventual execution. The rootTask might create several such tasks, sending messages and calling functions as it does so, thus modifying the stack space of rootTask. Figure 4 shows rootTasks situation at an arbitrary point in its computation, three levels deep into a subroutine call or message send: Since TaskMaster does not provide preemptive scheduling, rootTask will remain in control until it performs some action that will put it to sleep and let one of the queued tasks proceed. Normally, a task does not do this by explicitly invoking the low-level primitives to be described here. It does so implicitly, by invoking a lightweight I/O operation on one of the I/O channel mechanisms described in the next section. The highest-level kernel entry that a user might access directly is the Semaphore class. Semaphores are a queue on which tasks can to wait for a resource and a count of available resources8. Suppose that some task does a lightweight read operation (anObject aStream get) on a communication channel, such as a stream, whose internal buffer is initially empty. Streams use semaphores to monitor the status of this buffer, so the emptySemaphores resource count variable will indicate that currentTask must wait for another task to add something to the buffer. Therefore, the semaphore will delay the task by linking it onto the semaphores queue of waiting tasks and call taskScheduler to put the current task to sleep and start another one running. The task scheduler chooses the topmost task in the readyQueue9 as nextTask, and calls the context switching routine as follows: contextSwitch(ampcurrentTask-gtregisters, ampnextTask-gtregisters, nextTask) This is where the magic happens. Whereas it was currentTask that called this function, a different task is running when it returns. This new task has its very own execution history (stack area) exactly as it was when the task was last put to sleep by contextSwitch. The magic is quite simple. A lightweight task is simply the machines execution state along with the call history that produced that state. Both are represented by the machines SP register. Calling a subroutine pushes these registers into the address in the SP register, and returning restores them from the address in the same register. The contextSwitch routine accomplishes its magic by saving the current tasks execution state, the SP register, in currentTask-gtregisters and loads another tasks SP from nextTask-gtregisters10. where the new task was saved when it was put to sleep. New tasks are handled the same way by initializing the stack arena and the tasks register save area such that contextSwitch will start the task as if its entry subroutine had been called via an ordinary subroutine call. The size of a tasks stack arena is established when the task is created, either with a reasonable default size or a size specified by the programmer. This arena cannot be enlarged thereafter because of deep-seated assumptions in Cs run-time model that are beyond TaskMasters control. If a task uses more than this amount, perhaps because of unforeseen recursion, the results would be unpredictable. Since providing a more robust subroutine-call mechanism in C is not practical, TaskMaster takes a less drastic approach. It initializes each stack arena with a block of guard information, which taskScheduler checks before every context switch to detect whether the task that has just finished running has overrun its allocation. Lightweight I/O (across tasks) (toc) The previous section showed how TaskMaster supports semaphores within an vanilla C environment, without depending on platform facilities whose absence would lead to non-portability. Semaphores are an extremely low-level scheduling primitive, too powerful for non-specialists in the same way that assembly language is too powerful. Although TaskMaster does provide semaphores, they should be regarded as a low-level mechanism for experts to use in building higher-level mechanisms that are not already available off-the-shelf. TaskMaster applications are concurrent applications (in the restricted sense of the previous section). Concurrently active tasks must be careful to synchronize the tasks access to shared data so that race conditions do not occur. The computer literature is full of ways for doing this, ranging from extremely low-level and general mechanisms like semaphores and event counts at the one extreme, to intermediate-level mechanisms like the Ada rendezvous mechanism at the other, to the ultra-high-level but ultra restrictive pipes and filters mechanism of Unix. The synchronization mechanisms that are most highly developed in TaskMaster today are oriented toward supporting the higher-level of object-oriented granularity that was described earlier as card-level integration libraries of loosely-coupled, iconic, concurrent modules that non-programmers can plug together (at runtime, not compile time) to create custom applications. The higher-level task mechanisms to be described next follow the Unix pipes and filters model, modified to assume lightweight tasks (card-level modules) that communicate by sending chip-level objects through lightweight communication channels such as streams11 . This philosophy departs from other workers in this field. For example, Grady Boochs library of Ada components takes a different approach, in which every reusable object (say, a Collection class), must allow for the fact that it may be accessed concurrently by multiple tasks. In practice, this means that every collection class must exist in multiple versions, one with guard logic to support concurrent access and others that do not for non-shared applications. Card-level objects reflect a different philosophy. Just as hardware cards do not share chips, software card-level objects (tasks) do not share chip-level objects such as collections. Rather, they communicate through specialized communication channel objects such as Streams a software analog for hardware busses between cards. Of course, hardware analogies are seldom perfect in software. As we shall see, the signals that these software buses carry are the same kind of chip-level objects that comprise the cards themselves. Whereas Booch would speak of two tasks sharing a common object, we view them as each owning the object at different times, passing it through a communication channel object that provides whatever sharing paradigm is needed. Streams are the simplest sharing paradigm of all I own it unless I give it to you. Since the stream sharing paradigm is simple to explain and to use, we expect that it will be the prevalent, but never the only, synchronization mechanism in user-programmable systems12. For example, Harrys program (Figure 1) is built entirely of task instances (icons) connected by stream instances (arrows). The large icons are tasks programmed to operate on objects flowing through streams and the small black dots represent generalized stream operations such as fork and join. Each of the tasks in such a system are programmed in a similar fashion, as a loop that reads objects from an incoming stream, processes them, and emits objects on an outgoing stream. For example, here is how a task might be coded in ordinary Objective-C, without using Action expressions: inObject s-gtinStream get process inObject compute outObject. The two streams in this example, inStream and outStream, may have been preallocated by the rootTask and passed to this task as formal parameters as argc and argv are passed to Unix programs. It is also possible to pass such information in global variables (all tasks share the same global address space), or to create them within the task itself. The following larger example creates three tasks that communicate via two streams13. using action expressions to avoid the syntactic clutter that would result had this been written using functions and function pointers: main(int argc, char argv, char envp) Stream stream1 Stream forObjects Stream stream2 Stream forObjects In this example, the rootTask is creating three sub-tasks, which are automatically enrolled them on the readyQueue for eventual execution. When this task executes the currentTask waitForExit. message, it will be suspended14 and the first task on the readyQueue, task1, will begin executing. The Stream forObjects message creates streams that, by default15. support buffered communication. In this respect, streams are similar to the formatted I/O of C stdio functions like fopen(), fread(), printf() and so forth. Stream I/O is buffered I/O, as distinct from the unbuffered I/O of primitives like open(), read(), write(), etc. Since task1s output is buffered, it will loop repeatedly, putting an additional object reference into stream1s internal buffer each time. Eventually the semaphore that stream1 uses to detect a full buffer will determine that task1 must wait. The semaphore will stop the task by linking it onto a queue for consideration once more room becomes available. The next task in the readyQueue (task2) will be scheduled to begin emptying stream1s buffer. And so forth. Heavyweight I/O (across domains) (toc) The preceding section has outlined an architecture in which card-level objects (tasks) communicate by a card-level communication mechanism (streams). And it has showed how card-level objects are constructed from the chip-level mechanisms of Objective-C and these, in turn, from the gate - and block-level mechanisms of C. Now we turn to showing how lightweight tasks couple with the heavyweight processes and I/O mechanisms of a time-sharing operating system, for which Unix is a typical example (Figure 4). Harrys program in Figure 1 contains many examples of where a lightweight task must synchronize with events outside the domain in which the task resides. Three examples are mouse clicks, activity on the modem or the keyboard, and disk I/O. The simplicity that makes Unix so convenient for C and shell programming makes it a worst-case environment for the kind of lightweight tasking extensions described here, far more difficult than environments like VMS or MS/DOS for example. Our success in implementing the TaskMaster PDL within the restrictive Unix environment shows that the TaskMaster API can be implemented on less restrictive platforms like OS/2, MS/DOS, Windows, Mach and other Unix platforms. This section will describe how the TaskMaster API deals with heavy-weight I/O, and then look beneath the covers at how this API was implemented under Sun Unix. The code within a card-level object (a task) should be independent of whether the task happens to be connected to another task, or to an external I/O device, which TaskMaster models as instances of a special class, Port. Whereas streams support a buffered I/O model as in Cs stdio library, ports model unbuffered I/O devices, as in I/O system calls. The logic for filling and emptying a streams buffer depends on whether the stream is connected to a task or to a Port. TaskMaster handles this by providing two stream classes. The Stream class is used only for connecting tasks to tasks. The PortStream class is used for connecting tasks to ports. The two streams offer the same message interface to their clients. By default under Unix, all I/O operations are blocking the whole domain is suspended until I/O is complete. This means that if any task within a domain invokes Unix kernel I/O directly, the kernel will block the domain and every sub-task until the operation has completed. Since this is usually not what is wanted, lightweight tasks avoid doing kernel I/O operations directly by using Streams (for buffered I/O) or Ports (for unbuffered I/O). Streams provide a message protocol that supports all stdio I/O operations, including formatted I/O as in printf(), and ports provide a similar protocol for all kernel I/O operations. Although invoking stdio or kernel I/O directly does no harm apart from the blocking behavior mentioned above, tasks generally use streams or ports for all I/O. Non-blocking I/O is supported by the Port and Sensor classes. The Port class tracks the tasks that have I/O outstanding and schedules them so that other tasks can run while the I/O is pending. It is supported by Sensor, a platform-dependent class in the TaskMaster PDL, whose sole instance, theSensor . provides platform-specific logic for non-blocking I/O. Under Sun Unix, Sensor is based on the Unix select(2) system call. A complication is that streams can carry either references (objects) or values (characters) to other tasks in the same domain, whereas PortStreams can only pass values to Ports. This arises from the fundamental limitation that card-level (lightweight) processes can exchange references (pointers) to chip - and lower-level objects freely, while rack-level (heavyweight) processes can only exchange values (character strings). The PortStream class helps to hide, but of course can never eliminate, the distinction between lightweight and heavyweight I/O16. It implements its reference-stream I/O methods (put: and get) in terms of the storeOn: and readFrom: methods that all objects inherit from the Objective-C root class, Object. The put:method uses storeOn: to represent the argument (and everything it references) as a string of ASCI characters. The get method uses the complementary method, readFrom. to reconstruct the object from this encoding. Exception handling (toc) Consider a subroutine, main(), which calls foo(), which calls bar(). The runtime stack that underlies the C runtime environment extends to record that main has called foo and that foo has called bar. Then it retracts as bar returns to foo and as foo returns to main. The same call/return path is used unconditionally, regardless of whether the subroutines ran successfully or failed because of an exception. The absence of a way of handling exceptions explicitly, independently from normal processing, is a severe obstacle to software quality and reusability. Since subroutines routines return the same way, regardless of whether they succeed or fail, a method that should return a handle to a new object might instead return an error code to indicate that it could not, perhaps because of insufficient memory. Since any such call might fail, the caller must check every return value, reporting any failures to higher levels with similar means. This is sufficiently tedious that it is neglected, allowing unhandled exceptions to crash the application. An exception is a situation where a computation does not proceed as planned, perhaps because of I/O errors, inability to allocate a new object because of insufficient memory, or defects in the software itself. Exception handling is a language/environmental feature that reserves the normal subroutine/message return channel exclusively for normal processing. Routines that return normally can be assumed to have succeeded, since those that fail will return via a independent channel reserved for exceptions. Low-level routines never fail by returning an error code for the caller to decipher, but by raising an exception. Higher level routines establish code fragments, exception handlers, that will receive control if the exception they are to handle is raised by a lower-level routine. The following shows how exception handling might be done in C, in order to show the limitations of this solution and how these limitations are addressed in TaskMaster. TRY() is a C macro that uses setjmp() to record the machines register settings before entering the computation that might fail the foo subroutine. handle low memory exceptions. handle IO failure exceptions.

No comments:

Post a Comment