Sistemas operativos: tres pezas fáciles. Parte 3: API de proceso (tradución)

Introdución aos Sistemas Operativos

Ola Habr! Gustaríame chamarlle á súa atención unha serie de artigos-traducións dunha literatura interesante na miña opinión: OSTEP. Este material discute con bastante profundidade o traballo dos sistemas operativos tipo Unix, é dicir, o traballo con procesos, varios programadores, memoria e outros compoñentes similares que constitúen un sistema operativo moderno. Podes ver o orixinal de todos os materiais aquí aquí. Teña en conta que a tradución foi feita de forma non profesional (con bastante liberdade), pero espero manter o significado xeral.

Os traballos de laboratorio sobre este tema pódense consultar aquí:

Outras partes:

Tamén podes consultar a miña canle en telegrama =)

Alarma! Hai un laboratorio para esta charla! Mirar github

API de proceso

Vexamos un exemplo de creación dun proceso nun sistema UNIX. Ocorre a través de dúas chamadas ao sistema garfo () и exec().

Garfo de chamada ()

Sistemas operativos: tres pezas fáciles. Parte 3: API de proceso (tradución)

Considere un programa que fai unha chamada fork(). O resultado da súa execución será o seguinte.

Sistemas operativos: tres pezas fáciles. Parte 3: API de proceso (tradución)

En primeiro lugar, introducimos a función main() e imprimimos a cadea na pantalla. A liña contén o identificador do proceso que no orixinal se chama PID o identificador de proceso. Este identificador úsase en UNIX para referirse a un proceso. O seguinte comando chamará a fork(). Neste punto, créase unha copia case exacta do proceso. Para o sistema operativo, parece que hai 2 copias do mesmo programa en execución no sistema, que á súa vez sairá da función fork(). O proceso fillo recén creado (en relación co proceso pai que o creou) xa non se executará, a partir da función main(). Cómpre lembrar que un proceso fillo non é unha copia exacta do proceso pai; en particular, ten o seu propio espazo de enderezos, os seus propios rexistros, o seu propio punteiro a instrucións executables e similares. Así, o valor devolto ao chamador da función fork() será diferente. En particular, o proceso pai recibirá o valor PID do proceso fillo como devolución, e o fillo recibirá un valor igual a 0. Usando estes códigos de retorno, pode entón separar os procesos e obrigar a cada un deles a facer o seu propio traballo. . Non obstante, a execución deste programa non está estrictamente definida. Despois de dividirse en 2 procesos, o SO comeza a supervisalos, así como a planificar o seu traballo. Se se executa nun procesador dun só núcleo, un dos procesos, neste caso o pai, seguirá funcionando e, a continuación, o proceso fillo recibirá o control. Ao reiniciar, a situación pode ser diferente.

Chamar espera ()

Sistemas operativos: tres pezas fáciles. Parte 3: API de proceso (tradución)

Considere o seguinte programa. Neste programa, debido á presenza dunha chamada esperar () O proceso principal sempre agardará a que se complete o proceso fillo. Neste caso, obteremos unha saída de texto estrictamente definida na pantalla

Sistemas operativos: tres pezas fáciles. Parte 3: API de proceso (tradución)

chamada exec().

Sistemas operativos: tres pezas fáciles. Parte 3: API de proceso (tradución)

Considere o reto exec(). Esta chamada ao sistema é útil cando queremos executar un programa completamente diferente. Aquí chamaremos execvp() para executar o programa wc que é un programa de conta de palabras. Que pasa cando se chama exec()? A esta chamada pásase o nome do ficheiro executable e algúns parámetros como argumentos. Despois, cárganse o código e os datos estáticos deste ficheiro executable e sobrescríbese o seu propio segmento co código. As áreas de memoria restantes, como a pila e o montón, reiniciízanse. Despois de que o SO simplemente executa o programa, pasándolle un conxunto de argumentos. Polo tanto, non creamos un proceso novo, simplemente transformamos o programa en execución noutro programa en execución. Despois de executar a chamada exec() no descendente, parece que o programa orixinal non se executase en absoluto.

Esta complicación de inicio é completamente normal para un shell Unix e permite que ese shell execute código despois de chamalo garfo (), pero antes da chamada exec(). Un exemplo dese código sería axustar o ambiente de shell ás necesidades do programa que se está a lanzar, antes de lanzalo.

Concha - só un programa de usuario. Ela móstrache a liña de invitación e agarda a que escribas algo nela. Na maioría dos casos, se escribe o nome dun programa alí, o shell atopará a súa localización, chamará ao método fork() e despois chamará a algún tipo de exec() para crear un novo proceso e agardará a que se complete usando un wait() chamada. Cando o proceso fillo saia, o shell volverá da chamada wait() e imprimirá de novo o aviso e agardará a que se introduza o seguinte comando.

A división fork() & exec() permite que o shell faga as seguintes cousas, por exemplo:
ficheiro wc > ficheiro_novo.

Neste exemplo, a saída do programa wc redirixe a un ficheiro. A forma en que o shell logra isto é bastante sinxela: creando un proceso fillo antes de chamar exec(), o shell pecha a saída estándar e abre o ficheiro novo_ficheiro, polo tanto, toda a saída do programa en execución posterior wc será redirixido a un ficheiro en lugar dunha pantalla.

Tubo Unix implícanse dun xeito similar, coa diferenza de que usan unha chamada pipe(). Neste caso, o fluxo de saída do proceso conectarase a unha cola de canalización situada no núcleo, á que se conectará o fluxo de entrada doutro proceso.

Fonte: www.habr.com

Engadir un comentario