relatorioLab2_so.md 5.6 KB

Sistemas Operacionais

Atividade Bônus 2

07/04/2019

NOME: Batata Frita
Matrícula: 01110000 01101111 01110100 01100001 01110100 01101111








Objetivo deste experimento

Foi proposto nesta atividade, fazer dois programas quaisquer na linguagem C:

  1. utilizando funções da biblioteca padrão do sistema (libc: glibc/musl)
  2. utilizando funções de sistema (syscalls)

O objetivo é compará-los e verificar quais dos dois possui a maior eficiência.

Ambiente utilizado

Para executar este experimento, foi utilizado o seguinte hardware: Notebook Dell Latitude 3480 (8GB de memória RAM, processador Intel Core i5-7200 x86_64 2.5GHz)
Versão do Kernel: 4.15.0-47-generic
Distribuição Linux: Ubuntu 18.04
Versão do gcc: 7.3.0

Procedimentos adotados

Junto às instruções deste experimento, está o código em C para utilizarmos no teste.
Para que não seja necessário dois arquivos (um para syscalls, outro para funções da libc) e sim, apenas um arquivo binário, modifiquei o código fonte para ficar da seguinte maneira:

./bonus2.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

/*
 * First argument: mode (libc or syscall)
 * Second argument: buffer size
 */

int main(int argc, char *argv[]) {
    char buffer[2000000];
    int i;

    if( !strcmp(argv[1], "syscall") ) {
        i = read(0, &buffer, strtol(argv[2], (char **)NULL, 10));

        while(i > 0) {
            write(1, &buffer, strtol(argv[2], (char **)NULL, 10));
            i = read(0, &buffer, strtol(argv[2], (char **)NULL, 10));
        }
    }
    else if( !strcmp(argv[1], "libc") ) {
        i = fread(&buffer, strtol(argv[2], (char **)NULL, 10), 1, stdin);

        while(i > 0) {
            fwrite(&buffer, strtol(argv[2], (char **)NULL, 10), 1, stdout);
            i = fread(&buffer, strtol(argv[2], (char **)NULL, 10), 1, stdin);
        }
    }
    else {
        printf("%s: Unknown option.\n", argv[0]);
    }

    return 0;
}

Para obter o executável, é necessário compilar utilizando o gcc:
gcc -o bonus2 arquivo.c

Observação: utilizei, também, o clang. Apesar de ser mais rápido, não achei que fosse necessário utilizá-lo como compilador principal para o experimento.

Essa modificação permite que em único binário, seja executado o teste tanto com syscalls quanto com funções da libc, com o tamanho do buffer desejado. O código deve ser executado desejado seguinde forma:

  • Com funções da libc e com tamanho de buffer 16: ./bonus2 libc 16
  • Com syscalls e com tamanho de buffer 32: ./bonus2 syscall 32

De acordo com as instruções, os tamanhos dos buffer que serão usados são: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, ..., 1048546

Para não ter que executar o binario várias vezes, criei o seguinte script:

./iterator.sh

#!/bin/bash

size=1
printf "%s\n" "glibc:"
for i in $(seq 0 20); do

    # clear cache
    sync; echo 3 > /proc/sys/vm/drop_caches
    printf "buffer size: %s - " "$size"

    output=$({ time ./bonus2 libc "$size" < datafile > /dev/null; } 2>&1)
    value_output=$?

    # print only if the above execution was successfull
    if [[ $value_output -eq 1 ]]; then
        echo "$output" | grep real | awk '{ print $2 }'
    fi

    let size=size*2
done

size=1
printf "%s\n" "Syscalls:"
for i in $(seq 0 20); do

    # clear cache
    sync; echo 3 > /proc/sys/vm/drop_caches
    printf "buffer size: %s - " "${size}"

    output=$({ time ./bonus2 syscall "${size}" < datafile > /dev/null; } 2>&1)
    value_output=$?

    # print only if the above execution was successfull
    if [[ $value_output -eq 1 ]]; then
        echo "$output" | grep real | awk '{ print $2 }'
    fi

    let size=size*2
done

Resultados

Com a execução do script acima, obteve-se o seguinte resultado:

glibc:
buffer size: 1 - 0m0,970s
buffer size: 2 - 0m0,951s
buffer size: 4 - 0m0,410s
buffer size: 8 - 0m0,476s
buffer size: 16 - 0m0,450s
buffer size: 32 - 0m0,475s
buffer size: 64 - 0m1,017s
buffer size: 128 - 0m0,776s
buffer size: 256 - 0m0,472s
buffer size: 512 - 0m0,461s
buffer size: 1024 - 0m0,852s
buffer size: 2048 - 0m0,618s
buffer size: 4096 - 0m0,450s
buffer size: 8192 - 0m0,460s
buffer size: 16384 - 0m0,404s
buffer size: 32768 - 0m0,449s
buffer size: 65536 - 0m0,458s
buffer size: 131072 - 0m0,572s
buffer size: 262144 - 0m0,459s
buffer size: 524288 - 0m0,559s
buffer size: 1048576 - 0m0,618s

Syscalls:
buffer size: 1 - 0m8,326s
buffer size: 2 - 0m4,351s
buffer size: 4 - 0m2,439s
buffer size: 8 - 0m1,404s
buffer size: 16 - 0m0,854s
buffer size: 32 - 0m0,594s
buffer size: 64 - 0m1,590s
buffer size: 128 - 0m0,473s
buffer size: 256 - 0m0,449s
buffer size: 512 - 0m0,869s
buffer size: 1024 - 0m0,966s
buffer size: 2048 - 0m0,672s
buffer size: 4096 - 0m0,477s
buffer size: 8192 - 0m0,541s
buffer size: 16384 - 0m0,475s
buffer size: 32768 - 0m0,673s
buffer size: 65536 - 0m0,849s
buffer size: 131072 - 0m0,397s
buffer size: 262144 - 0m0,452s
buffer size: 524288 - 0m0,484s
buffer size: 1048576 - 0m0,610s

Transformado-os em um gráfico, fica da seguinte forma:

syscalls libc

Conclusões

De acordo com os resultados, a conclusão foi que utilizar funções da libc é mais eficiente e mais rápida que funções do sistema (syscalls), por isso devemos utilizá-la ao invés de funções do sistema.