DEPARTAMENTO DE SISTEMAS E COMPUTAÇÃO Disciplina: Sistemas Operacionais Teste de verificação de pré-requisitos Considere as definições de tipos abaixo: type Aponta_Nodo = ^Nodo; NODO Nodo = record Info Suc. Anterior = Aponta_Nodo; Sucessor = integer; Info = Bloco_dados; End; Ant. e as seguintes declarações de variáveis: var Inicio_Lista; Fim_Lista, P : Aponta_Nodo; Fim_Lista Construa a função insere_lista para inserir um elemento na lista ordenada, duplamente encadeada, descrita acima. Proteção de objetos:
Bits de permissão agrupados - dono - grupo -público rwx rwx rwx r - read; w - write; x - execute. r w x r - x r - - ( este conjunto significaria que o dono pode ler (r), escrever (w) e executar (x), o grupo do dono pode ler e executar, o público pode apenas ler. ALGUNS COMANDOS UNIX: cd - troca de diretório. Percorrer a árvore do sistema de arquivos; ls - lista o conteúdo de um diretório; mkdir - cria um diretório; mkdir so2 /* cria o sub-diretório so2 dentro do diretório corrente */ rmdir - remove diretório; rm - remove arquivo; cp - copia arquivo(s); cp /bin/date. move/mv - move arquivo; pwd - mostra o caminho do diretório corrente; mount - monta uma unidade no sistema de arquivos; unmount - desmonta; who - relação dos usuários que estão conectados no sistema; who am I - identificação do usuário que está no terminal que executou o comando; write - escreve em um terminal; kill - envia um signal para um processo / matar um processo; kill -9 pid /* pid = process id */ exit - saída, fecha a sessão; passwd - trocar a senha (pede a senha atual e a nova senha); chmod - alterar as permissões de acesso;
chmod 700 meu_arquivo grep - buscar uma sequência de caracteres; df - disk free; lpr - envia para o spool; lpq - permite verificar a fila de impressão (line printer queue); lprm - line printer management; ps - (process status) lista os processos no sistema. ln - (link) cria um link para um arquivo. SISTEMAS OPERACIONAIS Aula prática em Laboratório - Objetivo: Fixar conteúdos básicos relativos ao ambiente operacional do Sistema UNIX. Utilizar comandos básicos para operação do sistema. Para a conexão remota com o sistema UNIX existente no laboratório PROTEM a partir dos laboratórios dos blocos G e Z, será necessária a instalação do software emulador de terminal, PUTTY. Este software encontra-se disponível no servidor deste mesmo laboratório. No endereço ftp://campeche.inf.furb.br/pub/windows/utilitarios/terminais-cliente-ssh/putty- 0.51.exe encontra-se o software para download. ftp://campeche.inf.furb.br/pub/windows/utilitarios/terminais-cliente-ssh/putty- 0.51.exe Crie um diretório um e faça download do arquivo. Exercícios: Utilizando o comando who verifique quais os usuários estão conectados ao sistema. Utilizando o comando pwd, verifique qual é seu diretório corrente. Dentro do seu diretório home, crie um diretório com o nome SistemasDois. Copie o arquivo syslog.conf existente no diretório /etc para o diretório SistemasDois, recém criado. Crie o diretório aula no seu diretório home. A partir do diretório aula, crie um link do arquivo syslog.conf no diretório SistemasDois para o diretório aula através do comando: link../sistemasdois/syslog.conf syslog.link Utilizando o comando ls -l, verifique quais são as permissões de acesso disponíveis para o diretório SistemasDois.
Ainda com o comando ls -l, verifique as permissões de acesso aos diretórios dos usuários do Laboratório PROTEM. (o diretório /lp/home possui todos os diretórios home dos usuários). Altere as permissões de acesso existentes para o diretório SistemasDois deixando permissões de leitura, escrita e execução, apenas para o dono do diretório. Execute o comando cd SistemasDois. Altere as permissões de acesso ao arquivo syslog.conf existente no diretório SistemasDois. Execute o comando "cat syslog.conf". Descreva o que foi executado. Utilizando o comando ps, verifique quantos e quais os processos (a identificação e o comando) em execução na sua sessão. SISTEMAS OPERACIONAIS * PIPE $>find * grep "furb" * EXECUÇÃO EM FOREGROUND $>cp arquivoa arquivob <enter> * EXECUÇÃO EM BACKGROUND $> cp arquivoa arquivob & <enter> Pode-se também utilizar ^z e o comando bg (background) * ESTRUTURA DO SISTEMA DE ARQUIVOS * BLOCO DE BOOT * SUPERBLOCO * I-NODO * BLOCOS DE DADOS I-NODO: nome do arquivo Número de links Data da criação Data da última modificação Data do último acesso 10 número de blocos de dados ponteiro indireto simples ponteiro indireto duplo ponteiro indireto triplo
GERÊNCIA DE PROCESSOS Quando inicializado o sistema cria o processo INIT com PID = 1, este processo será a raiz da árvore de processos. Quando um usuário executa o login o sistema cria um processo e carrega o interpretador de comandos (sh) para atender o usuário. A linha de comando digitada pelo usuário é lida pelo sh que analisa-o e então cria um processo e carrega o comando na área deste processo. Criação de Processo - Chamada fork A chamada fork do sistema operacional UNIX, cria um novo processo no sistema. O processo criado recebe uma cópia do processo que chamou. Imediatamente a criação os dois processo, pai e filho, são idênticos. A função fork retorna um valor inteiro que para o processo pai é a identificação (PID) do processo filho, no processo filho, a função retorna zero. Por exemplo: int ret; ret = fork(); printf("ret = %d\n"),ret); int ret; ret = fork(); if (ret == 0) printf("este é o processo filho.\n") else printf("este é o processo pai, PID do filho = %d\n",ret) Andrew S. Tanenbaum http://www.cs.vu.nl/~ast Chamada de sistema EXEC Passos executados para executar uma chamada EXEC [TAN87]: 1. Verifica as permissões - o arquivo é executável? 2. Lê o header do arquivo para obter o tamanhos do segmento e total. 3. Busca os argumentos e as variáveis de ambiente a partir do chamador. 4. Libera a memória antiga e aloca a nova. 5. Copia a pilha para a nova memória imagem. 6. Copia os segmentos de dados e texto para a nova memória imagem.
7. Verifica os bits para setuid e setgid. 8. Atualiza a entrada na tabela de processos. 9. Coloca o processo no estado pronto. Biblioteca utilizada para chamar EXEC com argumentos e ambiente (environment) execve(filename, argv, envp); filename é um ponteiro para o nome do arquivo a ser executado; argv é um ponteiro para um array de ponteiros, cada um deles apontando para um argumento; envp é um ponteiro para um array de ponteiros, cada um deles apontando para uma string de ambiente (variável de ambiente). execl(filename, name, arg1, arg2,..., 0); int ret; if ((ret=fork()) == 0) sleep(15); else wait((int *) 0); printf("filho acabou \n"); Execute os exemplos abaixo no UNIX e descreva o que será executado. Para compilar os programas utilize o compilador C (gcc). %> gcc nome_arquivo Será gerado um arquivo a.out com o programa executável, ou %> gcc nome_arquivo -o nome_executavel será criado o arquivo nome_executável com o programa executável, caso não hajam erros de compilação. Para executar digite: %>./nome_executavel (1) int ret1, ret2; ret1 = fork(); ret2 = fork(); printf("programa em execução.\n"); (2) int ret; ret = fork(); if (ret == 0)
execl("/bin/ls","ls",0); else printf("processo continua executando.\n"); (3) int ret; ret = fork(); if (ret == 0) execl("/bin/ls","ls",0); printf("quando este comando será executado? \n"); ; printf("por que a função printf anterior não foi executada?\n"); (4) int ret; ret = fork(); if (ret == 0) execl("/bin/ll","ll",0); printf("por que este comando foi executado? \n"); else printf("processo continua executando.\n"); #include <stdio.h> #define MAXBUFF 1024 int pipefd[2], n; char buff[100]; if( pipe(pipefd) < 0 ) printf("erro na chamada pipe\n"); printf("read fd =%d, write fd = %d\n",pipefd[0], pipefd[1]); if ( write(pipefd[1], "Hello world\n", 12)!= 12 ) printf("erro no write\n"); if ( n = read(pipefd[0], buff, sizeof(buff)) <= 0 ) printf(ërro no read\n"); write(1, buff, n); /* fd 1 = stdout */ exit(0); int pipedesc[2], contador, total, ret, n; char buff[10];
ret = pipe(pipedesc); if ( ret < 0 ) printf("erro no pipe.\n"); printf("criou pipe\n"); ret = fork(); if ( ret == 0 ) printf("está no filho\n "); close(pipedesc[1]); total = 0; while ( total < 100 ) total = total + 1; while((n=read(pipedesc[0],buff,sizeof(buff)))<= 0); write(1, buff, n); close(pipedesc[0]); else printf("está no pai\n"); close(pipedesc[0]); contador = 0; while ( contador < 100 ) write(pipedesc[1], "A", 1); contador = contador + 1; close(pipedesc[1]); int childpid, pipe1[2], pipe2[2], ret1, ret2; ret1 = pipe(pipe1); ret2 = pipe(pipe2); if ( ret1 < 0 ret2 < 0 ) printf("não criou pipes.\n"); childpid = fork(); if ( childpid < 0 ) printf("não criou processo.\n"); exit(2); else if ( childpid > 0 ) /* é o processo pai */ close(pipe1[0]); close(pipe2[1]); client(pipe2[0], pipe1[1]); wait((int *) 0);
close(pipe1[1]); close(pipe2[0]); exit(0); else close(pipe1[1]); close(pipe2[0]); server(pipe1[0], pipe2[1]); close(pipe1[0]); close(pipe2[1]); exit(0); client(readfd, writefd) int readfd, writefd; char buff[maxbuff]; int n; /* * Lê o nome do arquivo da entrada padrão, * escreve-o no pipe. */ if (fgets(buff, MAXBUFF, stdin) == NULL) printf("erro na leitura do nome do arquivo.\n"); n = strlen(buff); if ( buff[n-1] == '\n') n--; if ( write(writefd, buff, n)!= n ) printf("erro escrevendo o nome do arquivo.\n"); /* * Lê os dados a partir do pipe e escreve-os na saída * padrão. */ while (( n = read(readfd, buff, MAXBUFF)) > 0) if (write(1, buff, n)!= n ) printf("erro escrevendo dados.\n"); if ( n < 0 ) printf("erro lendo dados a partir do pipe.\n"); exit(1): server (readfd, writefd)
int readfd, writefd; char buff[maxbuff]; int n, fd; /* * lê o nome do arquivo do pipe */ if ( ( n = read(readfd, buff, MAXBUFF)) <= 0 ) printf("erro na leitura do nome do arquivo.\n"); buff[n] = '\0'; if (( fd = open(buff, 0)) < 0 ) /* * erro na abertura do arquivo. */ printf("erro na abertura do arquivo.\n"); else /* * lê os dados do arquivo e escreve-os no pipe. */ while ( (n = read(fd, buff, MAXBUFF)) > 0 ) if ( write(writefd, buff, n)!= n ) printf("erro escrevendo dados pipe.\n"); if ( n < 0 ) printf("erro lendo dados.\n"); EXERCÍCIO PRÁTICO Construir um programa usando as chamadas fork, exec, wait e exit, que leia na entrada padrão a linha de comando, crie um processo filho e coloque em execução no processo filho o comando especificado na linha lida. O processo pai deverá esperar o término do processo filho e emitir mensagem de acordo com o código de retorno do filho. Se código de retorno igual a zero deverá aparecer a mensagem "Executado com sucesso.", senão deverá aparecer a mensagem "Código de retorno = ", seguida do valor numérico do código de retorno. Algoritmo:
Início Lê linha de comando; Enquanto não fim faça Início Percorre a linha retirando o nome do comando; Executa um fork para criar um novo processo; Se processo filho então Executa execl especificando o nome do comando como parâmetro; Senão Inicio Executa wait para esperar que a execução do comando termine; Se codigo retorno = zero então Escreva "Executado com sucesso." Senão Escreva "Código de retorno = ", codigo_retorno; Fim Fim se; Lê linha de comando; Fim; Fim; Após concluída a implementação deste exercício, incluir a possibilidade de passar parâmetros para o comando a ser executado. Exercício prático utilizando as chamadas de sistema: fork, pipe, wait, exit, close. Construa um programa em C para implementar a estrutura abaixo: Temos dois processos A e B, criados a partir do processo PP (programa principal), se comunicando através de 2 pipes. No processo A estará executando uma rotina que lê uma string de caracteres no Pipe2, converte-os para maiúsculas e escreve no Pipe1. No processo B estará executando uma rotina que lê uma linha de texto digitada pelo usuário (de no máximo 80 bytes - 79 caracteres mais o terminador), escreve esta linha no Pipe2 e depois lê a string convertida para maiúsculas no Pipe1, exibindo-a na tela. Quando o usuário digitar "FIM", o processo B envia a string para o processo A e encerra (exit). O processo A quando encontra a string "FIM" também encerra (exit).
O programa principal deverá criar os pipes, criar os processos, e estabelecer a comunicação entre os mesmos conforme descrito acima. O programa principal deverá ficar esperando (wait) até os processos filhos acabem (exit). As funções para converter os caracteres de minúsculos para maiúsculos e para ler o texto digitado deverão fazer parte do programa principal. #define TAMBUFF 1024 // aluno Edson Elmar Schlei int pipe1[2], pipe2[2], retp1, retp2; int pid_proc_a, pid_proc_b, n, i; char buff[tambuff]; char linha[80]; retp1 = pipe(pipe1); retp2 = pipe(pipe2); if ( (retp1 < 0) (retp2 < 0) ) printf("erro em um dos pipe.\n"); printf("criou os pipes\n"); pid_proc_a = fork(); if ( pid_proc_a == 0 ) // Inicio do Processo A printf("está no filho A \n"); close(pipe1[0]); close(pipe2[1]); // fecha a pipe1 para leitura // fecha a pipe2 para escrita while ((strcmp(buff,"fim"))) for(i=0;i<tambuff;i++) buff[i] = '\0'; // espera vir algo no pipe2 while ((n = read(pipe2[0], buff, sizeof(buff))) <= 0); buff[n] = '\0'; for(i=0;i<n;i++) // converte para maiúsculo buff[i] = toupper(buff[i]); write(pipe1[1],buff,n+1); // retorna a string no pipe1 close(pipe1[1]); close(pipe2[0]); exit(0); // finaliza o processo pid_proc_b = fork(); if (pid_proc_b == 0) // inicio do processo B
printf("entrou no processo B"); close(pipe1[1]); close(pipe2[0]); // fecha o pipe1 para escrita // fecha o pipe2 para leitura do for(i=0;i<80;i++) linha[i] = '\0'; printf("\ndigite algo para converte : "); gets(linha); printf("\n%s",linha); write(pipe2[1],linha,strlen(linha)); while ((n = read(pipe1[0], buff, sizeof(buff))) <= 0); printf("%s\n",buff); while((strcmp(buff,"fim"))); close(pipe1[0]); close(pipe2[1]); // fecha o pipe1 para leitura // fecha o pipe2 para escrita exit(0); // fechas os pipes no processo pai close(pipe1[0]); close(pipe1[1]); close(pipe2[0]); close(pipe2[1]); wait((int *)0); wait((int *)0);