Manipulação de Arquivos e Processos em Linux com C

Preparação do Ambiente

Para realizar os exercícios a seguir, é necessário um sistema Linux com o compilador gcc instalado. No Ubuntu, pode-se instalar as ferramentas esssenciais com:

sudo apt update
sudo apt install build-essential

Exercício 1: Combinação de Dois Arquivos em um Único Destino

Este programa lê dois arquivos de entrada e grava seus conteúdos sequencialmente em um arquivo de saída, inserindo uma quebra de linha entre eles.

Código-fonte

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define BUFFER_SIZE 1024

void copiar_conteudo(int origem, int destino) {
    char buffer[BUFFER_SIZE];
    ssize_t bytes_lidos;

    while ((bytes_lidos = read(origem, buffer, BUFFER_SIZE)) > 0) {
        write(destino, buffer, bytes_lidos);
    }
}

int main(int argc, char **argv) {
    int descritor_entrada1, descritor_entrada2, descritor_saida;
    const char separador = '\n';

    if (argc != 4) {
        fprintf(stderr, "Uso: %s <arquivo_origem_1> <arquivo_origem_2> <arquivo_destino>\n", argv[0]);
        return 1;
    }

    descritor_entrada1 = open(argv[1], O_RDONLY);
    if (descritor_entrada1 < 0) {
        perror("Erro ao abrir primeiro arquivo");
        return 1;
    }

    descritor_entrada2 = open(argv[2], O_RDONLY);
    if (descritor_entrada2 < 0) {
        perror("Erro ao abrir segundo arquivo");
        close(descritor_entrada1);
        return 1;
    }

    descritor_saida = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (descritor_saida < 0) {
        perror("Erro ao criar arquivo de saida");
        close(descritor_entrada1);
        close(descritor_entrada2);
        return 1;
    }

    copiar_conteudo(descritor_entrada1, descritor_saida);
    write(descritor_saida, &separador, 1);
    copiar_conteudo(descritor_entrada2, descritor_saida);

    close(descritor_entrada1);
    close(descritor_entrada2);
    close(descritor_saida);

    return 0;
}

Compilação e Execução

gcc combinador.c -o combinador
echo "Conteudo do primeiro arquivo" > entrada1.txt
echo "Conteudo do segundo arquivo" > entrada2.txt
./combinador entrada1.txt entrada2.txt resultado.txt
cat resultado.txt

Resultado esperado:

Conteudo do primeiro arquivo
Conteudo do segundo arquivo

Exercício 2: Execução de Comandos com fork e exec

Este exemplo cria um processo filho que substitui sua imagem por um novo programa utilizando execl, enquanto o processo pai aguarda sua conclusão.

Código-fonte

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
    pid_t processo_filho;

    processo_filho = fork();

    if (processo_filho == 0) {
        execlp("ls", "ls", "-l", NULL);
        perror("Falha na execucao de execlp");
        exit(EXIT_FAILURE);
    } else if (processo_filho > 0) {
        wait(NULL);
        printf("Processo filho finalizado\n");
        exit(EXIT_SUCCESS);
    } else {
        perror("Erro ao criar processo com fork");
        exit(EXIT_FAILURE);
    }
}

Compilação e Execução

gcc exec_comando.c -o exec_comando
./exec_comando

O programa listará os arquivos do diretório atual em formato longo e exibirá a mensagem de conclusão.

Exercício 3: Registro de Identificadores de Processo em Arquivo

Este programa demonstra a criação de processos com fork, onde tanto o processo pai quanto o filho gravam suas informações de PID em um arquivo compartilhado.

Código-fonte

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

#define ARQUIVO_DADOS "dados_processo"

void registrar_info(int fd, const char *mensagem) {
    write(fd, mensagem, strlen(mensagem));
}

int main(void) {
    int descritor_arquivo;
    pid_t pid_filho;
    char texto_pai[80];
    char texto_filho[80];

    descritor_arquivo = open(ARQUIVO_DADOS, O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (descritor_arquivo < 0) {
        perror("Nao foi possivel abrir o arquivo");
        exit(EXIT_FAILURE);
    }

    pid_filho = fork();

    if (pid_filho == 0) {
        printf("=== Processo Filho ===\n");
        printf("PID: %d\n", getpid());
        printf("PID do Pai: %d\n", getppid());
        snprintf(texto_filho, sizeof(texto_filho), "Escrito pelo filho (PID %d)\n", getpid());
        registrar_info(descritor_arquivo, texto_filho);
        close(descritor_arquivo);
        exit(EXIT_SUCCESS);
    } else if (pid_filho > 0) {
        printf("=== Processo Pai ===\n");
        printf("PID: %d\n", getpid());
        printf("PID do Filho: %d\n", pid_filho);
        snprintf(texto_pai, sizeof(texto_pai), "Escrito pelo pai (PID %d)\n", getpid());
        registrar_info(descritor_arquivo, texto_pai);
        close(descritor_arquivo);
        wait(NULL);
        exit(EXIT_SUCCESS);
    } else {
        perror("Falha no fork");
        close(descritor_arquivo);
        exit(EXIT_FAILURE);
    }
}

Compilação e Execução

gcc registro_pid.c -o registro_pid
./registro_pid
cat dados_processo

Saída no temrinal:

=== Processo Pai ===
PID: 1024
PID do Filho: 1025
=== Processo Filho ===
PID: 1025
PID do Pai: 1024

Conteúdo do arquivo dados_processo:

Escrito pelo pai (PID 1024)
Escrito pelo filho (PID 1025)

Tags: Linux C gcc fork exec

Publicado em 7-16 21:04