Manual Profesional de C++ para Entornos Linux

Desarrollo de sistemas, administración, DevOps y seguridad en entornos profesionales

Tabla de Contenidos

1. Introducción a C++ en Linux

1.1 Configuración del Entorno de Desarrollo

Para desarrollar en C++ en Linux necesitamos instalar las herramientas básicas:

Terminal
# Instalar compilador y herramientas básicas
sudo apt update
sudo apt install build-essential g++ cmake git

# Instalar herramientas de desarrollo adicionales
sudo apt install valgrind gdb clang-format cppcheck

1.2 Primer Programa en C++

Un programa básico en C++ para Linux:

hello_world.cpp
#include <iostream>
#include <unistd.h>  // Para funciones POSIX como getpid()

int main() {
    std::cout << "¡Hola Mundo desde Linux!" << std::endl;
    std::cout << "PID del proceso: " << getpid() << std::endl;
    return 0;
}
Compilación y ejecución
g++ -o hello_world hello_world.cpp
./hello_world
Nota: En entornos profesionales, es recomendable usar siempre las banderas de advertencia: g++ -Wall -Wextra -Werror -pedantic -std=c++17

2. C++ para SysAdmin

2.1 Manejo de Procesos

Ejemplo de cómo listar y manejar procesos:

process_management.cpp
#include <iostream>
#include <sys/types.h>
#include <dirent.h>
#include <cstdio>

void list_processes() {
    DIR *dir;
    struct dirent *ent;
    
    if ((dir = opendir("/proc")) != nullptr) {
        while ((ent = readdir(dir)) != nullptr) {
            // Solo directorios con nombres numéricos (PIDs)
            if (ent->d_type == DT_DIR && isdigit(ent->d_name[0])) {
                std::cout << "PID: " << ent->d_name << std::endl;
                
                // Leer el archivo cmdline para el nombre del proceso
                char path[256];
                snprintf(path, sizeof(path), "/proc/%s/cmdline", ent->d_name);
                
                FILE *cmdline = fopen(path, "r");
                if (cmdline) {
                    char cmd[1024];
                    if (fgets(cmd, sizeof(cmd), cmdline) {
                        std::cout << "Comando: " << cmd << std::endl;
                    }
                    fclose(cmdline);
                }
            }
        }
        closedir(dir);
    } else {
        perror("No se pudo abrir /proc");
    }
}

int main() {
    std::cout << "Listado de procesos en ejecución:" << std::endl;
    list_processes();
    return 0;
}

2.2 Manejo de Archivos y Permisos

Trabajando con el sistema de archivos:

file_operations.cpp
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>

void check_file_permissions(const char* filename) {
    struct stat file_stat;
    
    if (stat(filename, &file_stat) == -1) {
        perror("stat");
        return;
    }
    
    std::cout << "Información para: " << filename << std::endl;
    std::cout << "Tamaño: " << file_stat.st_size << " bytes" << std::endl;
    
    // Permisos del archivo
    std::cout << "Permisos: ";
    std::cout << (S_ISDIR(file_stat.st_mode) ? "d" : "-");
    std::cout << (file_stat.st_mode & S_IRUSR) ? "r" : "-");
    std::cout << (file_stat.st_mode & S_IWUSR) ? "w" : "-");
    std::cout << (file_stat.st_mode & S_IXUSR) ? "x" : "-");
    std::cout << (file_stat.st_mode & S_IRGRP) ? "r" : "-");
    std::cout << (file_stat.st_mode & S_IWGRP) ? "w" : "-");
    std::cout << (file_stat.st_mode & S_IXGRP) ? "x" : "-");
    std::cout << (file_stat.st_mode & S_IROTH) ? "r" : "-");
    std::cout << (file_stat.st_mode & S_IWOTH) ? "w" : "-");
    std::cout << (file_stat.st_mode & S_IXOTH) ? "x" : "-");
    std::cout << std::endl;
    
    // Cambiar permisos
    if (chmod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
        perror("chmod");
    }
}

int main() {
    const char* filename = "ejemplo.txt";
    
    // Crear un archivo
    int fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    const char* content = "Contenido de ejemplo\n";
    write(fd, content, strlen(content));
    close(fd);
    
    // Verificar permisos
    check_file_permissions(filename);
    
    return 0;
}

3. C++ en DevOps

3.1 Automatización de Tareas

Ejemplo de automatización con C++:

automation.cpp
#include <iostream>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <string>

class CommandExecutor {
public:
    static int execute(const std::string& command) {
        int status = system(command.c_str());
        
        if (WIFEXITED(status)) {
            return WEXITSTATUS(status);
        }
        return -1;
    }
    
    static std::string executeWithOutput(const std::string& command) {
        std::string result;
        FILE* pipe = popen(command.c_str(), "r");
        if (!pipe) return "ERROR";
        
        char buffer[128];
        while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
            result += buffer;
        }
        
        pclose(pipe);
        return result;
    }
};

int main() {
    // Ejemplo: Actualizar el sistema
    std::cout << "Actualizando paquetes..." << std::endl;
    int update_status = CommandExecutor::execute("sudo apt update && sudo apt upgrade -y");
    
    if (update_status == 0) {
        std::cout << "Actualización completada con éxito" << std::endl;
    } else {
        std::cerr << "Error en la actualización" << std::endl;
        return 1;
    }
    
    // Ejemplo: Obtener información del sistema
    std::cout << "\nInformación del sistema:" << std::endl;
    std::string uname = CommandExecutor::executeWithOutput("uname -a");
    std::cout << uname << std::endl;
    
    // Ejemplo: Verificar espacio en disco
    std::string disk = CommandExecutor::executeWithOutput("df -h");
    std::cout << "\nUso de disco:" << std::endl << disk << std::endl;
    
    return 0;
}

3.2 Integración Continua - Ejemplo con CMake

Configuración de un proyecto profesional con CMake:

CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(DevOpsAutomation LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Configuraciones de compilación
option(ENABLE_TESTING "Habilitar pruebas unitarias" ON)
option(ENABLE_COVERAGE "Habilitar cobertura de código" OFF)

# Configuraciones de warnings
add_compile_options(
    -Wall
    -Wextra
    -Werror
    -pedantic
    -Wconversion
    -Wsign-conversion
)

# Configuración para cobertura
if(ENABLE_COVERAGE)
    add_compile_options(--coverage -O0 -g)
    add_link_options(--coverage)
endif()

# Archivos fuente
set(SOURCES
    src/main.cpp
    src/automation.cpp
    src/command_executor.cpp
)

# Ejecutable principal
add_executable(devops-automation ${SOURCES})

# Pruebas unitarias
if(ENABLE_TESTING)
    enable_testing()
    add_subdirectory(tests)
endif()

# Instalación
install(TARGETS devops-automation DESTINATION bin)

4. Integración con Git

4.1 Interacción con Git desde C++

Ejemplo de cómo interactuar con Git mediante libgit2:

git_integration.cpp
#include <iostream>
#include <git2.h>

class GitRepository {
private:
    git_repository* repo;
    
public:
    GitRepository(const std::string& path) {
        git_libgit2_init();
        if (git_repository_open(&repo, path.c_str()) != 0) {
            const git_error* err = giterr_last();
            std::cerr << "Error al abrir repositorio: " << err->message << std::endl;
            throw std::runtime_error("Error al abrir repositorio Git");
        }
    }
    
    ~GitRepository() {
        git_repository_free(repo);
        git_libgit2_shutdown();
    }
    
    void get_status() {
        git_status_options opts = GIT_STATUS_OPTIONS_INIT;
        opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
        opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | 
                    GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
                    GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
        
        git_status_list* status_list;
        if (git_status_list_new(&status_list, repo, &opts) != 0) {
            const git_error* err = giterr_last();
            std::cerr << "Error al obtener estado: " << err->message << std::endl;
            return;
        }
        
        size_t count = git_status_list_entrycount(status_list);
        std::cout << "Estado del repositorio (" << count << " cambios):" << std::endl;
        
        for (size_t i = 0; i < count; ++i) {
            const git_status_entry* entry = git_status_byindex(status_list, i);
            
            if (entry->status == GIT_STATUS_CURRENT) continue;
            
            std::string status_str;
            if (entry->status & GIT_STATUS_INDEX_NEW) status_str += "A";
            if (entry->status & GIT_STATUS_INDEX_MODIFIED) status_str += "M";
            if (entry->status & GIT_STATUS_INDEX_DELETED) status_str += "D";
            if (entry->status & GIT_STATUS_INDEX_RENAMED) status_str += "R";
            if (entry->status & GIT_STATUS_INDEX_TYPECHANGE) status_str += "T";
            
            status_str += " | ";
            
            if (entry->status & GIT_STATUS_WT_NEW) status_str += "A";
            if (entry->status & GIT_STATUS_WT_MODIFIED) status_str += "M";
            if (entry->status & GIT_STATUS_WT_DELETED) status_str += "D";
            if (entry->status & GIT_STATUS_WT_RENAMED) status_str += "R";
            if (entry->status & GIT_STATUS_WT_TYPECHANGE) status_str += "T";
            if (entry->status & GIT_STATUS_WT_UNREADABLE) status_str += "?";
            if (entry->status & GIT_STATUS_IGNORED) status_str += "I";
            if (entry->status & GIT_STATUS_CONFLICTED) status_str += "C";
            
            const char* old_path = entry->head_to_index->old_file.path;
            const char* new_path = entry->head_to_index->new_file.path;
            const char* wt_path = entry->index_to_workdir->old_file.path;
            
            std::cout << status_str << " " << (old_path ? old_path : 
                         new_path ? new_path : 
                         wt_path ? wt_path : "???") << std::endl;
        }
        
        git_status_list_free(status_list);
    }
};

int main() {
    try {
        GitRepository repo(".");
        repo.get_status();
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}
Nota: Para compilar este código necesitas instalar libgit2: sudo apt install libgit2-dev Y luego compilar con: g++ -std=c++17 git_integration.cpp -lgit2 -o git_integration

5. Bases de Datos SQL

5.1 Conexión a MySQL/MariaDB

Ejemplo de conexión y consultas a MySQL:

mysql_example.cpp
#include <iostream>
#include <mysql/mysql.h>

class MySQLConnection {
private:
    MYSQL* conn;
    
public:
    MySQLConnection(const std::string& host, const std::string& user, 
                   const std::string& pass, const std::string& db) {
        conn = mysql_init(nullptr);
        if (!conn) {
            throw std::runtime_error("Error al inicializar MySQL");
        }
        
        if (!mysql_real_connect(conn, host.c_str(), user.c_str(), pass.c_str(), 
                               db.c_str(), 0, nullptr, 0)) {
            std::string err = mysql_error(conn);
            mysql_close(conn);
            throw std::runtime_error("Error al conectar a MySQL: " + err);
        }
    }
    
    ~MySQLConnection() {
        mysql_close(conn);
    }
    
    void executeQuery(const std::string& query) {
        if (mysql_query(conn, query.c_str())) {
            throw std::runtime_error("Error en la consulta: " + std::string(mysql_error(conn)));
        }
        
        MYSQL_RES* result = mysql_store_result(conn);
        if (!result) {
            if (mysql_field_count(conn) == 0) {
                // No hay resultados (INSERT, UPDATE, DELETE, etc.)
                std::cout << "Filas afectadas: " << mysql_affected_rows(conn) << std::endl;
                return;
            } else {
                throw std::runtime_error("Error al obtener resultados: " + 
                                        std::string(mysql_error(conn)));
            }
        }
        
        // Mostrar resultados
        MYSQL_ROW row;
        MYSQL_FIELD* fields = mysql_fetch_fields(result);
        int num_fields = mysql_num_fields(result);
        
        // Encabezados de columnas
        for (int i = 0; i < num_fields; i++) {
            std::cout << fields[i].name << "\t";
        }
        std::cout << std::endl;
        
        // Filas de datos
        while ((row = mysql_fetch_row(result))) {
            for (int i = 0; i < num_fields; i++) {
                std::cout << (row[i] ? row[i] : "NULL") << "\t";
            }
            std::cout << std::endl;
        }
        
        mysql_free_result(result);
    }
};

int main() {
    try {
        // Configuración de conexión
        std::string host = "localhost";
        std::string user = "usuario";
        std::string pass = "contraseña";
        std::string db = "basedatos";
        
        MySQLConnection conn(host, user, pass, db);
        
        // Crear tabla
        conn.executeQuery("CREATE TABLE IF NOT EXISTS empleados ("
                         "id INT AUTO_INCREMENT PRIMARY KEY, "
                         "nombre VARCHAR(50) NOT NULL, "
                         "departamento VARCHAR(50), "
                         "salario DECIMAL(10,2))");
        
        // Insertar datos
        conn.executeQuery("INSERT INTO empleados (nombre, departamento, salario) VALUES "
                         "('Juan Pérez', 'TI', 4500.00), "
                         "('Ana Gómez', 'RH', 3800.00), "
                         "('Carlos Ruiz', 'TI', 5200.00)");
        
        // Consultar datos
        std::cout << "\nEmpleados en TI:" << std::endl;
        conn.executeQuery("SELECT nombre, salario FROM empleados WHERE departamento = 'TI'");
        
        // Actualizar datos
        conn.executeQuery("UPDATE empleados SET salario = salario * 1.1 WHERE departamento = 'TI'");
        
        // Verificar actualización
        std::cout << "\nEmpleados en TI después del aumento:" << std::endl;
        conn.executeQuery("SELECT nombre, salario FROM empleados WHERE departamento = 'TI'");
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}
Nota: Para compilar este código necesitas instalar la biblioteca de desarrollo de MySQL: sudo apt install libmysqlclient-dev Y luego compilar con: g++ -std=c++17 mysql_example.cpp -lmysqlclient -o mysql_example

6. Bases de Datos NoSQL

6.1 MongoDB con C++

Conexión y operaciones básicas con MongoDB:

mongodb_example.cpp
#include <iostream>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/json.hpp>

using namespace mongocxx;
using bsoncxx::builder::stream::document;
using bsoncxx::builder::stream::open_document;
using bsoncxx::builder::stream::close_document;
using bsoncxx::builder::stream::finalize;

class MongoDBClient {
private:
    instance instance{}; // Debe mantenerse durante toda la aplicación
    client conn;
    database db;
    
public:
    MongoDBClient(const std::string& uri, const std::string& db_name) 
        : conn{uri::create(uri)}, db{conn[db_name]} {}
        
    void insert_document(const std::string& collection_name, const std::string& json) {
        auto collection = db[collection_name];
        auto doc = bsoncxx::from_json(json);
        collection.insert_one(doc.view());
    }
    
    void find_documents(const std::string& collection_name, const std::string& query_json = "{}") {
        auto collection = db[collection_name];
        auto query = bsoncxx::from_json(query_json);
        auto cursor = collection.find(query.view());
        
        std::cout << "Documentos encontrados:" << std::endl;
        for(auto&& doc : cursor) {
            std::cout << bsoncxx::to_json(doc) << std::endl;
        }
    }
};

int main() {
    try {
        MongoDBClient mongo("mongodb://localhost:27017", "empresaDB");
        
        // Insertar documentos
        mongo.insert_document("empleados", R"({
            "nombre": "Ana García",
            "departamento": "TI",
            "skills": ["C++", "Linux", "MongoDB"],
            "salario": 5200.00,
            "fecha_ingreso": { "$date": "2020-05-15T00:00:00Z" }
        })");
        
        mongo.insert_document("empleados", R"({
            "nombre": "Carlos Méndez",
            "departamento": "DevOps",
            "skills": ["Docker", "Kubernetes", "AWS"],
            "salario": 5800.00,
            "fecha_ingreso": { "$date": "2019-11-20T00:00:00Z" }
        })");
        
        // Consultar documentos
        std::cout << "\nTodos los empleados:" << std::endl;
        mongo.find_documents("empleados");
        
        std::cout << "\nEmpleados de TI:" << std::endl;
        mongo.find_documents("empleados", R"({"departamento": "TI"})");
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}
Nota: Para compilar necesitas instalar libmongocxx y libbsoncxx: sudo apt install libmongocxx-dev libbsoncxx-dev Compilar con: g++ -std=c++17 mongodb_example.cpp -lmongocxx -lbsoncxx -o mongodb_example

6.2 Redis con C++

Ejemplo de uso de Redis como base de datos clave-valor:

redis_example.cpp
#include <iostream>
#include <hiredis/hiredis.h>

class RedisClient {
private:
    redisContext* conn;
    
public:
    RedisClient(const std::string& host, int port) {
        conn = redisConnect(host.c_str(), port);
        if (conn == nullptr || conn->err) {
            if (conn) {
                throw std::runtime_error("Error de conexión: " + std::string(conn->errstr));
            } else {
                throw std::runtime_error("Error al asignar contexto Redis");
            }
        }
    }
    
    ~RedisClient() {
        redisFree(conn);
    }
    
    void set(const std::string& key, const std::string& value) {
        redisReply* reply = (redisReply*)redisCommand(conn, "SET %s %s", key.c_str(), value.c_str());
        if (reply == nullptr) {
            throw std::runtime_error("Error en SET: " + std::string(conn->errstr));
        }
        freeReplyObject(reply);
    }
    
    std::string get(const std::string& key) {
        redisReply* reply = (redisReply*)redisCommand(conn, "GET %s", key.c_str());
        if (reply == nullptr) {
            throw std::runtime_error("Error en GET: " + std::string(conn->errstr));
        }
        
        if (reply->type == REDIS_REPLY_NIL) {
            freeReplyObject(reply);
            return "";
        }
        
        std::string result = reply->str;
        freeReplyObject(reply);
        return result;
    }
    
    void publish(const std::string& channel, const std::string& message) {
        redisReply* reply = (redisReply*)redisCommand(
            conn, "PUBLISH %s %s", channel.c_str(), message.c_str());
        if (reply == nullptr) {
            throw std::runtime_error("Error en PUBLISH: " + std::string(conn->errstr));
        }
        freeReplyObject(reply);
    }
};

int main() {
    try {
        RedisClient redis("localhost", 6379);
        
        // Almacenamiento clave-valor
        redis.set("servidor:nombre", "servidor_produccion");
        redis.set("servidor:carga", "45%");
        
        std::cout << "Nombre del servidor: " << redis.get("servidor:nombre") << std::endl;
        std::cout << "Carga del servidor: " << redis.get("servidor:carga") << std::endl;
        
        // Pub/Sub
        redis.publish("canal_monitoreo", "Alerta: Servidor sobrecargado");
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

7. Desarrollo para el Kernel de Linux

Advertencia: El desarrollo del kernel tradicionalmente se hace en C, no en C++. Sin embargo, existen formas de interactuar con el kernel desde C++.

7.1 Módulos del Kernel

Ejemplo básico de un módulo del kernel (en C, llamado desde C++):

kernel_module/hello_kernel.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tu Nombre");
MODULE_DESCRIPTION("Un módulo simple del kernel");

static int __init hello_init(void) {
    printk(KERN_INFO "Hola Kernel!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Adiós Kernel\n");
}

module_init(hello_init);
module_exit(hello_exit);
kernel_module/Makefile
obj-m := hello_kernel.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules
    
clean:
    make -C $(KDIR) M=$(PWD) clean

7.2 Interacción con el Kernel desde C++

Ejemplo de cómo interactuar con módulos del kernel:

kernel_interaction.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

class KernelInterface {
private:
    int fd;
    
public:
    KernelInterface(const std::string& device_path) {
        fd = open(device_path.c_str(), O_RDWR);
        if (fd < 0) {
            throw std::runtime_error("No se pudo abrir el dispositivo");
        }
    }
    
    ~KernelInterface() {
        if (fd >= 0) close(fd);
    }
    
    std::string read_kernel_message() {
        char buffer[1024];
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer)-1);
        if (bytes_read < 0) {
            throw std::runtime_error("Error al leer del dispositivo");
        }
        buffer[bytes_read] = '\0';
        return std::string(buffer);
    }
    
    void write_to_kernel(const std::string& message) {
        if (write(fd, message.c_str(), message.size()) < 0) {
            throw std::runtime_error("Error al escribir al dispositivo");
        }
    }
    
    void ioctl_command(unsigned long command, void* data = nullptr) {
        if (ioctl(fd, command, data) < 0) {
            throw std::runtime_error("Error en ioctl");
        }
    }
};

int main() {
    try {
        // Ejemplo con /proc/kmsg (necesita permisos)
        KernelInterface kmsg("/proc/kmsg");
        std::cout << "Mensaje del kernel: " << kmsg.read_kernel_message() << std::endl;
        
        // Ejemplo con un dispositivo ficticio /dev/mi_dispositivo
        KernelInterface dev("/dev/mi_dispositivo");
        dev.write_to_kernel("Hola desde espacio de usuario");
        std::cout << "Respuesta: " << dev.read_kernel_message() << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

8. Desarrollo de Drivers

8.1 Estructura básica de un driver de caracteres

Ejemplo de un driver simple (en C, llamado desde C++):

driver/mi_driver.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "mi_driver"
#define BUFFER_SIZE 1024

static int major;
static char msg_buffer[BUFFER_SIZE];
static int msg_size;

static int device_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "Dispositivo abierto\n");
    return 0;
}

static int device_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "Dispositivo cerrado\n");
    return 0;
}

static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) {
    int bytes_read = 0;
    
    if (*offset >= msg_size) return 0;
    
    if (*offset + length > msg_size)
        length = msg_size - *offset;
    
    if (copy_to_user(buffer, msg_buffer + *offset, length))
        return -EFAULT;
    
    *offset += length;
    return length;
}

static ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset) {
    if (length > BUFFER_SIZE) length = BUFFER_SIZE;
    
    if (copy_from_user(msg_buffer, buffer, length))
        return -EFAULT;
    
    msg_size = length;
    return length;
}

static struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release
};

static int __init mi_driver_init(void) {
    major = register_chrdev(0, DEVICE_NAME, &fops);
    if (major < 0) {
        printk(KERN_ALERT "Error al registrar el driver\n");
        return major;
    }
    printk(KERN_INFO "Driver registrado con major number %d\n", major);
    return 0;
}

static void __exit mi_driver_exit(void) {
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "Driver desregistrado\n");
}

module_init(mi_driver_init);
module_exit(mi_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tu Nombre");
MODULE_DESCRIPTION("Un driver de caracteres simple");

8.2 Interacción con el driver desde C++

driver_interaction.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

class DeviceDriver {
private:
    int fd;
    std::string device_path;
    
public:
    DeviceDriver(const std::string& path) : device_path(path) {
        fd = open(device_path.c_str(), O_RDWR);
        if (fd < 0) {
            throw std::runtime_error("No se pudo abrir el dispositivo " + device_path);
        }
    }
    
    ~DeviceDriver() {
        if (fd >= 0) close(fd);
    }
    
    std::string read_from_device() {
        char buffer[1024];
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer)-1);
        if (bytes_read < 0) {
            throw std::runtime_error("Error al leer del dispositivo");
        }
        buffer[bytes_read] = '\0';
        return std::string(buffer);
    }
    
    void write_to_device(const std::string& data) {
        if (write(fd, data.c_str(), data.size()) < 0) {
            throw std::runtime_error("Error al escribir al dispositivo");
        }
    }
    
    void test_driver() {
        std::string test_msg = "Mensaje de prueba para el driver";
        std::cout << "Escribiendo en el dispositivo: " << test_msg << std::endl;
        write_to_device(test_msg);
        
        std::cout << "Leyendo del dispositivo:" << std::endl;
        std::cout << read_from_device() << std::endl;
    }
};

int main() {
    try {
        // Necesitas crear el nodo del dispositivo primero:
        // mknod /dev/mi_driver c 250 0 (usando el major number correcto)
        
        DeviceDriver driver("/dev/mi_driver");
        driver.test_driver();
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

9. Comunicación Serial y Puertos COM

9.1 Configuración de puerto serial

serial_communication.cpp
#include <iostream>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <cstring>

class SerialPort {
private:
    int fd;
    std::string port;
    
public:
    SerialPort(const std::string& port_name) : port(port_name), fd(-1) {}
    
    ~SerialPort() {
        if (is_open()) close();
    }
    
    bool is_open() const { return fd >= 0; }
    
    bool open_port(int baud_rate = B9600) {
        fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
        if (fd < 0) return false;
        
        struct termios options;
        tcgetattr(fd, &options);
        
        // Configuración básica
        cfsetispeed(&options, baud_rate);
        cfsetospeed(&options, baud_rate);
        
        options.c_cflag |= (CLOCAL | CREAD);
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        options.c_cflag &= ~CSIZE;
        options.c_cflag |= CS8;
        options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        options.c_oflag &= ~OPOST;
        
        options.c_cc[VMIN] = 1;  // Bloquear hasta que llegue al menos 1 byte
        options.c_cc[VTIME] = 0; // Sin timeout
        
        if (tcsetattr(fd, TCSANOW, &options) < 0) {
            close();
            return false;
        }
        
        return true;
    }
    
    void close() {
        if (is_open()) {
            ::close(fd);
            fd = -1;
        }
    }
    
    ssize_t write_data(const std::string& data) {
        if (!is_open()) return -1;
        return write(fd, data.c_str(), data.size());
    }
    
    std::string read_data(size_t max_length = 256) {
        if (!is_open()) return "";
        
        char buffer[max_length + 1];
        ssize_t bytes_read = read(fd, buffer, max_length);
        if (bytes_read < 0) return "";
        
        buffer[bytes_read] = '\0';
        return std::string(buffer);
    }
};

int main() {
    SerialPort serial("/dev/ttyS0"); // Cambiar por el puerto correcto
    
    if (!serial.open_port()) {
        std::cerr << "Error al abrir el puerto serial" << std::endl;
        return 1;
    }
    
    std::cout << "Puerto serial abierto. Enviando datos..." << std::endl;
    
    // Enviar comando AT básico
    serial.write_data("AT\r\n");
    
    // Esperar respuesta
    usleep(100000); // 100ms de espera
    std::string response = serial.read_data();
    
    std::cout << "Respuesta: " << response << std::endl;
    
    serial.close();
    return 0;
}

9.2 Comunicación con dispositivos RS-232/485

modbus_example.cpp
#include <iostream>
#include <vector>
#include <cstdint>
#include "serial_port.h" // Usar la clase SerialPort anterior

class ModbusRTU {
private:
    SerialPort port;
    
    uint16_t calculate_crc(const std::vector<uint8_t>& data) {
        uint16_t crc = 0xFFFF;
        
        for (uint8_t byte : data) {
            crc ^= byte;
            
            for (int i = 0; i < 8; i++) {
                if (crc & 0x0001) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        
        return crc;
    }
    
public:
    ModbusRTU(const std::string& port_name) : port(port_name) {}
    
    bool connect(int baud_rate = 19200) {
        return port.open_port(baud_rate);
    }
    
    std::vector<uint16_t> read_holding_registers(uint8_t slave_id, 
                                               uint16_t start_addr, 
                                               uint16_t reg_count) {
        std::vector<uint8_t> request = {
            slave_id,       // Dirección del esclavo
            0x03,           // Función: Leer registros de holding
            (uint8_t)(start_addr >> 8),
            (uint8_t)(start_addr & 0xFF),
            (uint8_t)(reg_count >> 8),
            (uint8_t)(reg_count & 0xFF)
        };
        
        // Calcular CRC
        uint16_t crc = calculate_crc(request);
        request.push_back(crc & 0xFF);
        request.push_back(crc >> 8);
        
        // Enviar solicitud
        port.write_data(std::string(request.begin(), request.end()));
        
        // Leer respuesta (ajustar según timeout esperado)
        usleep(200000); // 200ms
        std::string response_str = port.read_data(256);
        std::vector<uint8_t> response(response_str.begin(), response_str.end());
        
        // Verificar CRC
        if (response.size() < 2) return {};
        
        uint16_t received_crc = (response[response.size()-1] << 8) | 
                               response[response.size()-2];
        uint16_t calculated_crc = calculate_crc(
            std::vector<uint8_t>(response.begin(), response.end()-2));
        
        if (received_crc != calculated_crc) {
            std::cerr << "Error de CRC" << std::endl;
            return {};
        }
        
        // Procesar datos
        std::vector<uint16_t> registers;
        for (size_t i = 3; i < response.size()-2; i += 2) {
            registers.push_back((response[i] << 8) | response[i+1]);
        }
        
        return registers;
    }
};

int main() {
    ModbusRTU modbus("/dev/ttyUSB0");
    
    if (!modbus.connect()) {
        std::cerr << "Error al conectar con el dispositivo Modbus" << std::endl;
        return 1;
    }
    
    // Leer 2 registros de holding empezando en la dirección 0
    auto registers = modbus.read_holding_registers(1, 0, 2);
    
    if (!registers.empty()) {
        std::cout << "Registros leídos:" << std::endl;
        for (size_t i = 0; i < registers.size(); ++i) {
            std::cout << "Registro " << i << ": " << registers[i] << std::endl;
        }
    } else {
        std::cerr << "Error al leer registros" << std::endl;
    }
    
    return 0;
}

10. Criptografía: Encriptación, Hashes y Certificados

10.1 Encriptación AES

aes_encryption.cpp
#include <iostream>
#include <string>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

class AESEncryptor {
private:
    AES_KEY encrypt_key;
    AES_KEY decrypt_key;
    unsigned char iv[AES_BLOCK_SIZE];
    
public:
    AESEncryptor(const std::string& key) {
        if (key.size() != 16 && key.size() != 24 && key.size() != 32) {
            throw std::runtime_error("La clave debe ser de 128, 192 o 256 bits");
        }
        
        // Configurar claves de encriptación y desencriptación
        AES_set_encrypt_key((const unsigned char*)key.c_str(), key.size()*8, &encrypt_key);
        AES_set_decrypt_key((const unsigned char*)key.c_str(), key.size()*8, &decrypt_key);
        
        // Generar IV aleatorio
        if (RAND_bytes(iv, AES_BLOCK_SIZE) != 1) {
            throw std::runtime_error("Error al generar IV");
        }
    }
    
    std::string encrypt(const std::string& plaintext) {
        // Asegurar que el texto plano sea múltiplo del tamaño de bloque
        size_t padded_size = ((plaintext.size() + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
        std::vector<unsigned char> padded(padded_size);
        memcpy(padded.data(), plaintext.data(), plaintext.size());
        
        // Buffer para el texto cifrado
        std::vector<unsigned char> ciphertext(padded_size);
        
        // Realizar la encriptación
        AES_cbc_encrypt(padded.data(), ciphertext.data(), padded_size, 
                       &encrypt_key, iv, AES_ENCRYPT);
        
        // Devolver el texto cifrado como string (incluyendo el IV al principio)
        std::string result(iv, iv + AES_BLOCK_SIZE);
        result.append(ciphertext.begin(), ciphertext.end());
        
        return result;
    }
    
    std::string decrypt(const std::string& ciphertext) {
        if (ciphertext.size() <= AES_BLOCK_SIZE || (ciphertext.size() % AES_BLOCK_SIZE) != 0) {
            throw std::runtime_error("Texto cifrado inválido");
        }
        
        // Extraer el IV (primeros 16 bytes)
        unsigned char local_iv[AES_BLOCK_SIZE];
        memcpy(local_iv, ciphertext.data(), AES_BLOCK_SIZE);
        
        // Obtener el texto cifrado real
        const unsigned char* actual_ciphertext = 
            (const unsigned char*)ciphertext.data() + AES_BLOCK_SIZE;
        size_t ciphertext_size = ciphertext.size() - AES_BLOCK_SIZE;
        
        // Buffer para el texto plano
        std::vector<unsigned char> plaintext(ciphertext_size);
        
        // Realizar la desencriptación
        AES_cbc_encrypt(actual_ciphertext, plaintext.data(), ciphertext_size, 
                       &decrypt_key, local_iv, AES_DECRYPT);
        
        // Eliminar padding y devolver como string
        return std::string(plaintext.begin(), plaintext.end());
    }
};

int main() {
    try {
        // Clave AES-256 (32 bytes)
        std::string key = "mi_clave_secreta_de_32_bytes_123456";
        
        AESEncryptor aes(key);
        
        std::string plaintext = "Este es un mensaje secreto para encriptar con AES";
        std::cout << "Texto original: " << plaintext << std::endl;
        
        // Encriptar
        std::string ciphertext = aes.encrypt(plaintext);
        std::cout << "Texto encriptado (base64): " << base64_encode(ciphertext) << std::endl;
        
        // Desencriptar
        std::string decrypted = aes.decrypt(ciphertext);
        std::cout << "Texto desencriptado: " << decrypted << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

10.2 Generación de Hashes y Certificados SSL

crypto_utils.cpp
#include <iostream>
#include <string>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/err.h>

class CryptoUtils {
public:
    static std::string sha256_hash(const std::string& input) {
        unsigned char hash[SHA256_DIGEST_LENGTH];
        SHA256_CTX sha256;
        
        SHA256_Init(&sha256);
        SHA256_Update(&sha256, input.c_str(), input.size());
        SHA256_Final(hash, &sha256);
        
        return std::string(hash, hash + SHA256_DIGEST_LENGTH);
    }
    
    static void generate_self_signed_cert(const std::string& cert_path, 
                                        const std::string& key_path) {
        RSA* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
        if (!rsa) {
            throw std::runtime_error("Error al generar clave RSA");
        }
        
        X509* x509 = X509_new();
        if (!x509) {
            RSA_free(rsa);
            throw std::runtime_error("Error al crear certificado X509");
        }
        
        // Configurar versión y número de serie
        X509_set_version(x509, 2);
        ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
        
        // Configurar validez (1 año)
        X509_gmtime_adj(X509_get_notBefore(x509), 0);
        X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
        
        // Configurar clave pública
        X509_set_pubkey(x509, EVP_PKEY_new());
        EVP_PKEY_set1_RSA(X509_get_pubkey(x509), rsa);
        
        // Configurar nombre
        X509_NAME* name = X509_get_subject_name(x509);
        X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, 
                                  (const unsigned char*)"US", -1, -1, 0);
        X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, 
                                  (const unsigned char*)"Mi Empresa", -1, -1, 0);
        X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, 
                                  (const unsigned char*)"localhost", -1, -1, 0);
        
        // El emisor es el mismo que el sujeto (autofirmado)
        X509_set_issuer_name(x509, name);
        
        // Firmar el certificado
        if (!X509_sign(x509, EVP_PKEY_new(), EVP_sha256())) {
            X509_free(x509);
            RSA_free(rsa);
            throw std::runtime_error("Error al firmar certificado");
        }
        
        // Guardar clave privada
        FILE* key_file = fopen(key_path.c_str(), "wb");
        if (!key_file) {
            X509_free(x509);
            RSA_free(rsa);
            throw std::runtime_error("Error al abrir archivo de clave");
        }
        PEM_write_RSAPrivateKey(key_file, rsa, nullptr, nullptr, 0, nullptr, nullptr);
        fclose(key_file);
        
        // Guardar certificado
        FILE* cert_file = fopen(cert_path.c_str(), "wb");
        if (!cert_file) {
            X509_free(x509);
            RSA_free(rsa);
            throw std::runtime_error("Error al abrir archivo de certificado");
        }
        PEM_write_X509(cert_file, x509);
        fclose(cert_file);
        
        // Liberar memoria
        X509_free(x509);
        RSA_free(rsa);
    }
};

int main() {
    try {
        // Ejemplo de hash SHA-256
        std::string password = "mi_contraseña_secreta";
        std::string hash = CryptoUtils::sha256_hash(password);
        
        std::cout << "SHA-256 de '" << password << "': ";
        for (unsigned char c : hash) {
            printf("%02x", c);
        }
        std::cout << std::endl;
        
        // Generar certificado autofirmado
        CryptoUtils::generate_self_signed_cert("certificado.pem", "clave_privada.pem");
        std::cout << "Certificado y clave generados con éxito" << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

11. Técnicas Avanzadas

11.1 Programación Multihilo con C++17

multithreading.cpp
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <queue>
#include <functional>

class ThreadPool {
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    
    std::mutex queue_mutex;
    std::condition_variable condition;
    std::atomic<bool> stop;
    
public:
    ThreadPool(size_t threads) : stop(false) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock, [this] {
                            return this->stop || !this->tasks.empty();
                        });
                        
                        if (this->stop && this->tasks.empty())
                            return;
                            
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    
                    task();
                }
            });
        }
    }
    
    template<class F>
    void enqueue(F&& f) {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            tasks.emplace(std::forward<F>(f));
        }
        condition.notify_one();
    }
    
    ~ThreadPool() {
        stop = true;
        condition.notify_all();
        for (std::thread &worker : workers) {
            worker.join();
        }
    }
};

int main() {
    ThreadPool pool(4);
    std::mutex cout_mutex;
    
    for (int i = 0; i < 8; ++i) {
        pool.enqueue([i, &cout_mutex] {
            {
                std::lock_guard<std::mutex> lock(cout_mutex);
                std::cout << "Tarea " << i << " ejecutada por el hilo " 
                          << std::this_thread::get_id() << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::seconds(1));
        });
    }
    
    // Esperar a que todas las tareas terminen
    std::this_thread::sleep_for(std::chrono::seconds(3));
    
    return 0;
}

11.2 Uso avanzado de CMake para proyectos complejos

CMakeLists.txt avanzado
cmake_minimum_required(VERSION 3.12)
project(AdvancedCppProject LANGUAGES CXX)

# Configuración estándar
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Opciones de configuración
option(BUILD_TESTS "Build tests" ON)
option(BUILD_BENCHMARKS "Build benchmarks" OFF)
option(ENABLE_COVERAGE "Enable code coverage" OFF)
option(USE_SANITIZERS "Enable sanitizers" ON)

# Configuraciones de compilación
add_compile_options(
    -Wall
    -Wextra
    -Werror
    -pedantic
    -Wconversion
    -Wsign-conversion
)

# Configuración para cobertura
if(ENABLE_COVERAGE)
    add_compile_options(--coverage -O0 -g)
    add_link_options(--coverage)
endif()

# Configuración de sanitizers
if(USE_SANITIZERS)
    add_compile_options(
        -fsanitize=address
        -fsanitize=undefined
        -fno-omit-frame-pointer
    )
    add_link_options(
        -fsanitize=address
        -fsanitize=undefined
    )
endif()

# Configuración de subdirectorios
add_subdirectory(src)

# Pruebas unitarias
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

# Benchmarks
if(BUILD_BENCHMARKS)
    add_subdirectory(benchmarks)
endif()

# Instalación
install(TARGETS advanced_cpp_app DESTINATION bin)
install(DIRECTORY include/ DESTINATION include)