Mudanças entre as edições de "SOP29005-2020-1"
Linha 214: | Linha 214: | ||
#Destruir todos | #Destruir todos | ||
killall yes | killall yes | ||
+ | |||
+ | |||
+ | {{collapse top| bg=lightyellow | expandir=true | Processos no Linux}} | ||
+ | |||
+ | == Processos no Linux - Modificado por Eraldo == | ||
+ | |||
+ | <blockquote style="background: lime; border: 1px solid black; padding: 1em;"> | ||
+ | Processos no Linux | ||
+ | </blockquote> | ||
+ | |||
+ | *Exercícios propostos pelo Prof.Arliones e Modificados por Eraldo | ||
+ | |||
+ | |||
+ | [[imagem:DesenhoForkExec.png|border|650px]] | ||
+ | |||
+ | |||
+ | ;Syscall FORK | ||
+ | |||
+ | * Em um terminal, execute "man fork" | ||
+ | ** A função da API POSIX '''fork()''' aciona uma chamada de sistema (system call - syscall) que cria um novo processo duplicando o processo que realiza a chamada. O novo processo, chamado de filho, é uma cópia exata do processo criador, chamado de pai, exceto por alguns detalhes listados na manpage. O principal destes detalhes para nós agora é o fato de terem '''PIDs diferentes'''. | ||
+ | ** O '''código''' dos dois processos (pai e filho) são '''idênticos'''; | ||
+ | ** Os '''dados''' dos dois processos (pai e filho) são '''idênticos NO MOMENTO DA CRIAÇÃO'''; | ||
+ | ** Execução do processo filho inicia na próxima instrução do programa (no retorno da chamada FORK); | ||
+ | ** Não é possível saber qual dos processos (pai ou filho) retormará a execução primeiro - isto fica a cargo do excalonador do SO; | ||
+ | ** Valores de retorno da chamada FORK: | ||
+ | *** (-1): erro na criação do processo (ex.: memória insuficiente); | ||
+ | *** (0): em caso de sucesso, este é o valor de retorno recebido pelo processo filho; | ||
+ | *** (>0): em caso de sucesso, este é o valor de retorno recebido pelo processo pai; | ||
+ | |||
+ | ;Syscall JOIN | ||
+ | |||
+ | * A syscall JOIN é implementada no POSIX pela função '''wait()'''. Execute "man wait". | ||
+ | ** Além da função '''wait()''', há também '''waitpid()''' e '''waitid()'''; | ||
+ | ** Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme); | ||
+ | ** A chamada wait também libera os recursos do processo filho que termina; | ||
+ | ** '''wait()''': esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize; | ||
+ | ** '''waitpid()''': suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize; | ||
+ | |||
+ | ;Syscall EXEC | ||
+ | |||
+ | * A syscall EXEC é implementada no POSIX pela família de funções '''exec()'''. Execute "man exec". | ||
+ | ** As principais funções da família são '''execl()''', '''execlp()''' e '''execvp()'''; | ||
+ | ** Todas estas funções são, na realidade, front-ends (abstrações) para a syscall '''execve'''. Esta syscall substitui a imagem do processo corrente (aquele que chama a syscall) pela a imagem de um novo processo; | ||
+ | ** Os parâmetros passados a estas funções são, basicamente, o nome de um arquivo com a imagem do programa a ser executado (um binário de um programa), e uma lista de parâmetros a serem passados a este novo programa; | ||
+ | |||
+ | |||
+ | ;Exemplos POSIX utilizando fork/wait/exec | ||
+ | |||
+ | *Exemplo 1: fork/wait básico | ||
+ | <syntaxhighlight lang=c> | ||
+ | // ex1: fork/wait básico | ||
+ | #include <sys/types.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/wait.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int pid, status; | ||
+ | pid = fork(); | ||
+ | |||
+ | if(pid == -1) // fork falhou | ||
+ | { | ||
+ | perror("fork falhou!"); | ||
+ | exit(-1); | ||
+ | } | ||
+ | else if(pid == 0) // Este é o processo filho | ||
+ | { | ||
+ | printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid()); | ||
+ | exit(0); | ||
+ | } | ||
+ | else // Este é o processo pai | ||
+ | { | ||
+ | wait(&status); | ||
+ | printf("processo pai\t pid: %d\t pid pai: %d\n", getpid(), getppid()); | ||
+ | exit(0); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | arliones@socrates:~/tmp$ gcc ex1.c -o ex1 | ||
+ | arliones@socrates:~/tmp$ ./ex1 | ||
+ | processo filho pid: 27858 pid pai: 27857 | ||
+ | processo pai pid: 27857 pid pai: 5337 | ||
+ | arliones@socrates:~/tmp$ | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | * Exemplo 2: processos pai e filho compartilham código, mas não dados. | ||
+ | <syntaxhighlight lang=c> | ||
+ | // ex2: fork/wait "compartilhando" dados | ||
+ | // ex2: fork/wait "compartilhando" dados | ||
+ | #include <sys/types.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/wait.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int pid, status, k=0; | ||
+ | printf("processo %d\t antes do fork\n", getpid()); | ||
+ | pid = fork(); | ||
+ | printf("processo %d\t depois do fork\n", getpid()); | ||
+ | |||
+ | if(pid == -1) // fork falhou | ||
+ | { | ||
+ | perror("fork falhou!"); | ||
+ | exit(-1); | ||
+ | } | ||
+ | else if(pid == 0) // Este é o processo filho | ||
+ | { | ||
+ | k += 1000; | ||
+ | printf("processo filho\t pid: %d\t K: %d \t endereço K: %p\n", getpid(), k, &k); | ||
+ | exit(0); | ||
+ | } | ||
+ | else // Este é o processo pai | ||
+ | { | ||
+ | wait(&status); // espera o filho terminar | ||
+ | k += 10; | ||
+ | printf("processo pai\t pid: %d\t K: %d \t endereço K: %p\n", getpid(), k, &k); | ||
+ | exit(0); | ||
+ | } | ||
+ | k += 1000; | ||
+ | printf("FIM processo %d\t K: %d\n", getpid(), k); | ||
+ | exit(0); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | processo 17056 antes do fork | ||
+ | processo 17056 depois do fork | ||
+ | processo 17057 depois do fork | ||
+ | processo filho pid: 17057 K: 1000 endereço K: 0x7ffd8923e318 | ||
+ | processo pai pid: 17056 K: 10 endereço K: 0x7ffd8923e318 | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | * Modificação no código: comentar linhas 23 e 30 | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | * Analise os resultados e busque entender a diferença. | ||
+ | *Perguntas e desafios.: | ||
+ | #Analisando os valores impressos de k pode-se dizer que os dados são compartilhados entre os dois processos? | ||
+ | #O endereço de k impresso é o mesmo nos dois processos. ISto não contradiz a afirmação anterior? | ||
+ | #Substitua o k por uma área criada dinâmicamente com o malloc (área de HEAP). Verifique se esta área é ou não compartilhada pelos processos. | ||
+ | |||
+ | |||
+ | ;Exercício fork/wait | ||
+ | |||
+ | Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um ''fork()'' criando um processo B, o processo B, por sua vez, faz um ''fork()'' criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar ''wait()'' para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar ''sleep()'' (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa. | ||
+ | |||
+ | *Use o comando pstree para verificar a árvore de processos criada. | ||
+ | |||
+ | {{collapse top|Solução}} | ||
+ | <code> | ||
+ | /* | ||
+ | ex3: Excrever um programa C que cria uma arvore de 3 processos, onde o processo | ||
+ | A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork() | ||
+ | criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o | ||
+ | processo XXX, filho de YYY", onde XXX e YYY sao PIDs de processos. Utilizar | ||
+ | wait() para garantir que o processo C imprima sua resposta antes do B, e que o | ||
+ | processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para | ||
+ | haver um intervalo de 1 segundo entre cada mensagem impressa. | ||
+ | */ | ||
+ | |||
+ | #include <sys/types.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/wait.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int pid, status; | ||
+ | pid = fork(); | ||
+ | |||
+ | if(pid == -1) // fork falhou | ||
+ | { | ||
+ | perror("fork falhou!"); | ||
+ | exit(-1); | ||
+ | } | ||
+ | else if(pid == 0) // Este é o processo filho | ||
+ | { | ||
+ | pid = fork(); | ||
+ | if(pid == -1) | ||
+ | { | ||
+ | perror("fork falhou!"); | ||
+ | exit(-1); | ||
+ | } | ||
+ | else if(pid == 0) // Este é o filho do filho | ||
+ | { | ||
+ | sleep(1); | ||
+ | printf("Eu sou o processo C (PID %d), filho de %d\n", getpid(), getppid()); | ||
+ | exit (0); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | wait(&status); | ||
+ | sleep(1); | ||
+ | printf("Eu sou o processo B (PID %d), filho de %d\n", getpid(), getppid()); | ||
+ | exit(0); | ||
+ | } | ||
+ | } | ||
+ | else // Este é o processo pai | ||
+ | { | ||
+ | wait(&status); | ||
+ | sleep(1); | ||
+ | printf("Eu sou o processo A (PID %d), filho de %d\n", getpid(), getppid()); | ||
+ | exit(0); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} |
Edição das 07h28min de 21 de fevereiro de 2020
AULA 1 - Dia 11/02/2020
Objetivos/Conteúdos
- Apresentação do Plano de Ensino
- Objetivos e Conteúdo Programático da Disciplina (ver SIGAA)
- Forma de Avaliação (ver SIGAA)
- Introdução a Sistemas Operacionais (Cap1. do Silberschatz)
- O que faz um sistema operacional (1.1)
- Organização e Arquitetura de um Sistema Computacional (1.2 e 1.3)
- Importância da Interrupção e Timers (1.2.1)
- Estrutura de Armazenamento (1.2.2)
- Estrutura de IO (1.2.3)
- Estrutura e Operações de um Sistema Operacional (1.4 e 1.5)
Material de Referência
- Apresentação sobre histórico visão geral e estruturas básicas de um SO
- Slides Silberschatz Oitava Edição
- Tradução Slides Silberschatz Cap1
- Tradução Slides Silberschatz Cap2
- Tradução Slides Silberschatz Cap3
- Tradução Slides Silberschatz Cap4
- Livro do Prof.Maziero
- Université Nice Sophia Antipolis
Arliones:
Leitura Recomendada
- Cap.1 do Silberschatz principalmente:
- 1.1 a 1.9
Exercícios Práticos de Apoio a Aula
Na sequência. com fins motivacionais, são apresentados alguns exercícios ilustrando conceitos de processos, arquivos e permissionamento.
-
Comando para ver todos os processos no linux:
$ ps aux
-
Colocar 2 processos se executando no contexto de um terminal e verificar número dos processos e então "destruí-los":
$ yes > /dev/null $ yes > /dev/null $ ps $ kill -9 <pid_yes> $ kill -9 <pid_yes>
Observe que dois processos foram criados a partir do programa "yes". Os processos associados ao terminal são visualizados e então destruídos. Tente destruir também o interpretador de comando (shell) associado ao terminal.
-
Criar um terminal adicional
$ xterm
Ir no terminal criado e listar os processos que se executam associados a este terminal. Verificar qual o dispositivo de entrada e saída associado a ele. Voltar ao terminal original e enviar uma mensagem. Destruir o terminal criado:
$ echo Alo Terminal > /dev/pts/2 $ kill -9 <pid_terminal>
-
Comunicação entre processos:
$ cat /etc/passwd | grep home | wc -l
-
Retirando a permissão de leitura de um arquivo em nível de usuário proprietário:
$ echo Alo Mundo > alfa.txt $ ls -l alfa.txt $ cat alfa.txt $ chmod u-r alfa.txt $ cat alfa.txt $ chmod u+r alfa.txt $ cat alfa.txt
AULA 2 - Dia 14/02/2020
Objetivos/Conteúdos
- Estruturas do Sistema Operacional (cap.2)
- Serviços do Sistema Operacional (2.1)
- Interfaces com Usuários (2.2)
- Chamadas do Sistema (2.3)
- Tipos de Chamadas de Sistema (2.4) ver Fig.2.8
- Chamadas de Controle de Processos
Material de Referência
- [http://docente.ifsc.edu.br/arliones.hoeller/sop/slides/SOP29005-parte1.pdf Apresentação sobre histórico visão geral
- [3]
Leitura Recomendada
- Cap.2 do Silberschatz principalmente:
- 2.1 a 2.8
Exercícios
- Estudar e executar o código em http://cs.lmu.edu/~ray/notes/gasexamples/ Detalhes das chamadas na arquitetura x86_64 ver em https://lwn.net/Articles/604287/
- Estudar e executar o código usando a função (em conformidade com a API POSIX) write() para o hello world:
#include <unistd.h> main() { write(1,"Alo Mundo\n",10); }
- Use o comando strace para verificar todas as chamadas de sistema dos programas acima.
-
DESAFIO 1: Estude a seção "Mixing C and Assembly Language" da http://cs.lmu.edu/~ray/notes/gasexamples/ e construa uma função meu_hello_world() usando o código em assembly do exercício inicial. Estude como poderia disponibilizar esta e outras funções de interface (a sua API) em uma biblioteca. Note que esta função deve ser chamada da forma:
main() { meu_hello_world(); }
Gere o assembly do código em C e discuta a diferença entre uma chamada de função e uma chamada de sistema.
-
DESAFIO 2: Estude o link http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ e melhore a função meu_hello_world para suportar uma mensagem adicional da forma:
#include <string.h> main() { char *p="Tudo bem com vocês?"; meu_hello_world(p, strlen(p)); }
Solução:
Solução modificado de http://cs.lmu.edu/~ray/notes/gasexamples/ .global meu_hello_world .text meu_hello_world: push %rdi # salva primeiro parâmetro push %rsi # salva segundo parâmetro mov $1, %rax # system call 1 é write mov $1, %rdi # file handle 1 é stdout mov $message, %rsi # endereço da string mov $13, %rdx # número de bytes da string syscall # chamada ao sistema pop %rdx # o que havia sido passado em rsi (número de bytes) é colocado em rdx pop %rsi # o que havia sido colocado em rdi (endereço da string) é colocado em rsi mov $1, %rax # system call 1 is write mov $1, %rdi # file handle 1 é stdout syscall # nova chamada ao sistema ret message: .ascii "Hello, World\n"
AULA 3 - Dia 18/02/2020
Objetivos/Conteúdos
- Interfaces com Usuários (2.2)
- Chamadas do Sistema (2.3)
- Tipos de Chamadas de Sistema (2.4) ver Fig.2.8
- Chamadas de Controle de Processos
AULA 4 - Dia 21/08/2019
Objetivos
- PARTE 2A: Gerenciamento de Processos (cap.3)
- 3.1.Conceito de Processo
- 3.2.Escalonamento de Processos
- 3.3.Operações sobre Processos (Laboratório Operações sobre Processos no Linux: Fork/Exec/Wait)
Material de Referência
- Slides do Cap.3 - Silberschatz
- Laboratório Fork/Exec/Wait
Exercícios de Demonstração
- Executar em um terminal o comando top. Em um outro terminal executar:
yes > /dev/null & yes > /dev/null &
- Parar um dos processos yes
kill -STOP <pid_yes>
- Continuar o processo
kill -CONT <pid_yes>
- Destruir todos
killall yes
Processos no Linux Processos no Linux - Modificado por Eraldo
Processos no Linux
- Exercícios propostos pelo Prof.Arliones e Modificados por Eraldo
- Syscall FORK
- Em um terminal, execute "man fork"
- A função da API POSIX fork() aciona uma chamada de sistema (system call - syscall) que cria um novo processo duplicando o processo que realiza a chamada. O novo processo, chamado de filho, é uma cópia exata do processo criador, chamado de pai, exceto por alguns detalhes listados na manpage. O principal destes detalhes para nós agora é o fato de terem PIDs diferentes.
- O código dos dois processos (pai e filho) são idênticos;
- Os dados dos dois processos (pai e filho) são idênticos NO MOMENTO DA CRIAÇÃO;
- Execução do processo filho inicia na próxima instrução do programa (no retorno da chamada FORK);
- Não é possível saber qual dos processos (pai ou filho) retormará a execução primeiro - isto fica a cargo do excalonador do SO;
- Valores de retorno da chamada FORK:
- (-1): erro na criação do processo (ex.: memória insuficiente);
- (0): em caso de sucesso, este é o valor de retorno recebido pelo processo filho;
- (>0): em caso de sucesso, este é o valor de retorno recebido pelo processo pai;
- Syscall JOIN
- A syscall JOIN é implementada no POSIX pela função wait(). Execute "man wait".
- Além da função wait(), há também waitpid() e waitid();
- Todas estas syscalls são utilizadas para aguardar por mudanças no estado de um processo filho e obter informações sobre o processo filho cujo estado tenha mudado. São consideradas mudanças de estado: o filho terminou; o filho foi finalizado por um sinal (ex.: kill); o filho foi retomado por um sinal (ex.: alarme);
- A chamada wait também libera os recursos do processo filho que termina;
- wait(): esta função suspende a execução do processo chamador até que UM DOS SEUS FILHOS finalize;
- waitpid(): suspende a execução do processo chamador até que UM FILHO ESPECÍFICO finalize;
- Syscall EXEC
- A syscall EXEC é implementada no POSIX pela família de funções exec(). Execute "man exec".
- As principais funções da família são execl(), execlp() e execvp();
- Todas estas funções são, na realidade, front-ends (abstrações) para a syscall execve. Esta syscall substitui a imagem do processo corrente (aquele que chama a syscall) pela a imagem de um novo processo;
- Os parâmetros passados a estas funções são, basicamente, o nome de um arquivo com a imagem do programa a ser executado (um binário de um programa), e uma lista de parâmetros a serem passados a este novo programa;
- Exemplos POSIX utilizando fork/wait/exec
- Exemplo 1: fork/wait básico
// ex1: fork/wait básico #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main() { int pid, status; pid = fork(); if(pid == -1) // fork falhou { perror("fork falhou!"); exit(-1); } else if(pid == 0) // Este é o processo filho { printf("processo filho\t pid: %d\t pid pai: %d\n", getpid(), getppid()); exit(0); } else // Este é o processo pai { wait(&status); printf("processo pai\t pid: %d\t pid pai: %d\n", getpid(), getppid()); exit(0); } }
arliones@socrates:~/tmp$ gcc ex1.c -o ex1 arliones@socrates:~/tmp$ ./ex1 processo filho pid: 27858 pid pai: 27857 processo pai pid: 27857 pid pai: 5337 arliones@socrates:~/tmp$
- Exemplo 2: processos pai e filho compartilham código, mas não dados.
// ex2: fork/wait "compartilhando" dados // ex2: fork/wait "compartilhando" dados #include <sys/types.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main() { int pid, status, k=0; printf("processo %d\t antes do fork\n", getpid()); pid = fork(); printf("processo %d\t depois do fork\n", getpid()); if(pid == -1) // fork falhou { perror("fork falhou!"); exit(-1); } else if(pid == 0) // Este é o processo filho { k += 1000; printf("processo filho\t pid: %d\t K: %d \t endereço K: %p\n", getpid(), k, &k); exit(0); } else // Este é o processo pai { wait(&status); // espera o filho terminar k += 10; printf("processo pai\t pid: %d\t K: %d \t endereço K: %p\n", getpid(), k, &k); exit(0); } k += 1000; printf("FIM processo %d\t K: %d\n", getpid(), k); exit(0); }
processo 17056 antes do fork processo 17056 depois do fork processo 17057 depois do fork processo filho pid: 17057 K: 1000 endereço K: 0x7ffd8923e318 processo pai pid: 17056 K: 10 endereço K: 0x7ffd8923e318
- Modificação no código: comentar linhas 23 e 30
- Analise os resultados e busque entender a diferença.
- Perguntas e desafios.:
- Analisando os valores impressos de k pode-se dizer que os dados são compartilhados entre os dois processos?
- O endereço de k impresso é o mesmo nos dois processos. ISto não contradiz a afirmação anterior?
- Substitua o k por uma área criada dinâmicamente com o malloc (área de HEAP). Verifique se esta área é ou não compartilhada pelos processos.
- Exercício fork/wait
Excrever um programa C que cria uma árvore de 3 processos, onde o processo A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork() criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY são PIDs de processos. Utilizar wait() para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.
- Use o comando pstree para verificar a árvore de processos criada.
Solução /* ex3: Excrever um programa C que cria uma arvore de 3 processos, onde o processo A faz um fork() criando um processo B, o processo B, por sua vez, faz um fork() criando um processo C. Cada processo deve exibir uma mensagem "Eu sou o processo XXX, filho de YYY", onde XXX e YYY sao PIDs de processos. Utilizar wait() para garantir que o processo C imprima sua resposta antes do B, e que o processo B imprima sua resposta antes do A. Utilizar sleep() (man 3 sleep) para haver um intervalo de 1 segundo entre cada mensagem impressa.
- /
- include <sys/types.h>
- include <stdlib.h>
- include <stdio.h>
- include <unistd.h>
- include <sys/wait.h>
int main() {
int pid, status; pid = fork();
if(pid == -1) // fork falhou { perror("fork falhou!"); exit(-1); } else if(pid == 0) // Este é o processo filho { pid = fork(); if(pid == -1) { perror("fork falhou!"); exit(-1); } else if(pid == 0) // Este é o filho do filho { sleep(1); printf("Eu sou o processo C (PID %d), filho de %d\n", getpid(), getppid()); exit (0); } else { wait(&status); sleep(1); printf("Eu sou o processo B (PID %d), filho de %d\n", getpid(), getppid()); exit(0); } } else // Este é o processo pai { wait(&status); sleep(1); printf("Eu sou o processo A (PID %d), filho de %d\n", getpid(), getppid()); exit(0); }
}
</syntaxhighlight>