Libro Programación avanzada en C para Linux: necesito tu opinión

Como ya comenté hace un par de días estoy preparando un libro de programación avanzada Linux con C. Busco a unos voluntarios para echarle un vistazo y darme su opinión.

¿Qué necesito?

  • Gente dispuesta a leer los capítulos que les voy enviando.
  • Por cada capítulo y revisión una opinión sobre la calidad del mismo y comentarios y sugerencias.

¿Qué ofrezco a cambio?

  • Una vez el libro esté terminado regalaré una copia a cada uno de los colaboradores.
  • Su nombre aparecerá publicado en el libro.

Condiciones

  • Aportar ideas y sugerencias. Si no recibo ningún comentario de los capítulos que voy enviando no enviaré más capítulos.
  • Rellena el formulario de abajo para apuntarte. Valoraré que me contéis quiénes sois y lo que podéis aportar (sí, tú también ¡anímate hombre!)
  • Únicamente aceptaré a los veinte primeros que se ofrezcan.

Apúntate

[contact_form]

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

Nuevo libro: Ejercicios de programación en C. Resueltos y comentados

Acabo de poner a la venta un nuevo libro de ejercicios de programación en C: “Ejercicios de programación en C. Resueltos y comentados”.

Está disponible en Amazon:


Ejercicios de C resueltos y comentados

También tenéis disponible mi otro libro:

Curso de programación en C para principiantes

Procesos en C: Crear un nuevo proceso con fork()

Siguiendo con los artículos sobre procesos voy a poner aquí un sencillo ejemplo para ilustrar el funcionamiento de la función fork().

Esta función es la que se encarga de crear un nuevo proceso dentro de un proceso. El nuevo proceso creado es una copia exacta del original, con la única diferencia que cada uno de ellos tiene su propio identificador de proceso (pid).

#include <stdio.h>
 
int main() {
	int pid;
 
	printf("PADRE: Soy el proceso padre y mi pid es: %d\n", getpid());
 
	pid = fork();
 
	// En cuanto llamamos a fork se crea un nuevo proceso. En el proceso
	// padre 'pid' contendrá el pid del proceso hijo. En el proceso hijo
	// 'pid' valdrá 0. Eso es lo que usamos para distinguir si el código
	// que se está ejecutando pertenece al padre o al hijo.
 
	if (pid) // Este es el proceso padre
	{
		printf("PADRE: Soy el proceso padre y mi pid sigue siendo: %d\n", getpid());
		printf("PADRE: Mi hijo tiene el pid: %d\n", pid);
	}
	else // Proceso hijo
	{
		printf("HIJO: Soy el proceso hijo y mi pid es: %d\n", getpid());
		printf("HIJO: mi padre tiene el pid: %d\n", getppid());
	}
}

En un próximo post explicaré cómo convertir el proceso hijo en un nuevo proceso totalmente diferente (con el conjunto de funciones ‘exec’). Esto nos permitirá lanzar un programa desde dentro de otro.


Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

Procesos en C: Ejemplo de un sencillo cronómetro con SIGALRM

Continuando con el artículo de la semana pasada sobre procesos en C hoy os dejo aquí un sencillo ejemplo de un cronómetro:

// Para las funciones pause y alarm:
#include <unistd.h>
// Para las constantes SIGALRM y similares
#include <signal.h>
 
#include <stdio.h>
 
// Esta es la función que se va a ejecutar cada vez que se reciba la
// señal SIGALRM
void contar_segundos() {
	// Usamos static para que se conserve el valor de "segundos"
	// entre cada llamada a la función
	static int segundos=0;
 
	segundos++;
	printf("Han pasado %d segundos.\n", segundos);
}
 
int main() {
	// Asociamos la señal SIGALRM a la función contar_segundos
	signal(SIGALRM, contar_segundos);
 
	// Ponemos en marcha un bucle
	while(1) {
		// Establecemos una alarma para dentro de un segundo
		alarm(1);
		// Pausamos la ejecución del programa para que 
		// se quede esperando a recibir una señal.
		pause();
	}
}

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

Procesos en C: Señales (SIGINT)

Desde hace un tiempo tengo la idea de escribir sobre el tema de procesos y señales en C.

Las señales se usan para la comunicación entre procesos y manipularlos. Un ejemplo muy conocido de señal es la señal SIGINT, que se envía cuando el usuario pulsa CTRL+C durante la ejecución de un programa. Cuando el programa que estamos ejecutando recibe esta señal finalizará su ejecución.

En el siguiente ejemplo vamos a ver cómo podemos hacer para que el programa realice alguna acción especial cuando el usuario pulse CTRL+C. La acción a ejecutar va a ser mostrar el mensaje: “¿Por qué me interrumpes?”:

// Para las funciones pause y alarm:
#include <unistd.h>
// Para las constantes SIGALRM y similares
#include <signal.h>
 
#include <stdio.h>
 
// Esta función es la que vamos a usar como controlador de la señal SIGINT
void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
	raise(SIGTERM);
}
 
int main() {
 
	// Asociamos la señal SIGINT con la funcion "senal"
	signal(SIGINT, despedida);
 
	// Comenzamos un bucle que hará que el programa muestre sin
	// parar el mensaje "Nada nuevo por aquí"
	while(1) {
		printf("Nada nuevo por aquí.\n");
	}
}

En este programa, cuando el usuario pulse CTRL+C, en lugar de cerrarse directamente, se ejecutará la función que hemos asociado con esta señal (la funcion despedida).


La función despedida muestra el mensaje “¿Por qué me interrumpes?” y genera la señal SIGTERM (“raise” envía una señal al propio proceso). Si no hiciéramos esto el proceso no se detendría nunca (hasta que lo “matemos” con kill). Si te aburres haz la prueba con esta funcion:

void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
}

Otra posibilidad es usar la función signal de nuevo para indicar al proceso que use la acción por defecto de SIGINT (SIG_DFL – Signal Default):

void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
	signal(SIGINT, SIG_DFL); // Indicamos al programa que use la acción por defecto
	raise(SIGINT);
}

¿Pero por qué no podemos simplemente llamar a raise(SIGINT);?

Se podría pensar que bastaría con llamar a raise(SIGINT):

void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
	raise(SIGINT);
}

El problema es que la señal SIGINT va a ser procesada por la función “despedida”. De esta forma, cuando pulsemos CTRL+C, se llamará a la función despedida. La función despedida genera de nuevo la señal SIGINT, que va a ser procesada de nuevo por ella misma. El resultado es que cuando pulsamos CTRL+c el proceso comenzará a ejecutar una y otra vez la función despedida mostrando el mensaje “¿Por qué me interrumpes?” sin parar.

¿Por qué? Cada vez que se pulse CTRL+C se ejecuta la función “despedida” en lugar de ejecutarse la acción por defecto (cerrar el programa).

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

Introducción a la GSL (GNU Scientific Library) – Evaluar un polinomio

Recientemente he tenido que desarrollar un pequeño programa usando la librería GSL (GNU Scientific Library). Esta es una potente librería disponible en C para realizar complejos cálculos científicos. Este artículo va a servir como introducción a dicha librería.

Lo primero que necesitamos para trabajar con esta librería en Ubuntu es el paquete libgsl0-dev. Si no me equivoco el paquete libgsl0dbl, necesario para ejecutar programas hechos con esta librería, viene “de serie” en el sistema.

Vamos a ver el funcionamiento de la librería evaluando el polinomio: 4×2+3x+2 cuando x = 2:

/* ejemplo_gsl.c */
#include <stdio.h>
#include <gsl/gsl_poly.h>
 
int main (void)
{
  double coeficientes[] = { 2, 3, 4 };
  double x = 2;
  double resultado = gsl_poly_eval (coeficientes, 3, x);
  printf ("4*x2 + 3*x + 2 = %f\n", resultado);
  return 0;
}

El polinomio se evalúa usando la función gsl_poly_eval(), que tiene como parámetros:

– coeficientes: un array que contiene los coeficientes del polinomio.
– 3: es el tamaño del array de coeficientes.
– x: contiene el valor que queremos evaluar.

Para compilar el ejemplo bastaría con hacer:

gcc -o ejemplo_gsl ejemplo_gsl.c -lgsl -lgslcblas

Este programa daría como resultado:

4*x2 + 3*x + 2 = 24.000000

Programación en C: Compilando con gcc

Voy a iniciar una serie de artículos sobre la compilación de programas en C con gcc. Esta serie de artículos estará centrada en la compilación en Linux.

NOTA: Si usas Ubuntu y no encuentras gcc en tu sistema echa un vistazo aquí.

Para el ejemplo vamos a usar el siguiente sencillo programa:

/* primero.c - Una versión del típico hola mundo */
#include <stdio.h>
 
int main()
{
	printf("Compilado con GCC\n");
	return 0;
}

Para compilar este sencillísimo programa basta con ejecutar la orden:

gcc -o primero primero.c

donde:

-o primero: la opción -o nos permite especificar el nombre del ejecutable que se va a generar. Si no especificamos nada el nombre que se genera por defecto es a.out.

primero.c: el fichero que contiene el código fuente del programa.

Compilando con dos ficheros fuente

Ahora supongamos que tenemos dos ficheros fuente:

primero.c:

#include <stdio.h>
 
int main()
{
	printf("Compilado con GCC\n");
	segundo();
	return 0;
}

segundo.c:

#include <stdio.h>
 
int segundo()
{
	printf("Función segundo\n");
}

Si probamos la orden anterior tendremos el un resultado similar a éste:

/tmp/ccuAuHGn.o: In function `main':
primero.c:(.text+0x1e): undefined reference to `segundo'

La forma correcta de compilar en este caso sería:

gcc -o primero primero.c segundo.c

Y si lo ejecutamos:

Compilado con GCC
Función segundo

Todo muy sencillito hasta ahora. En la próxima entrega veremos cómo compilar nuestro programa con librerías adicionales.

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

QT+Mysql (3ª parte): Conectando a una base de datos MySQL

Siguiendo con este minicurso de utilización de MySQL con QT vamos a ver un ejemplo de cómo conectar a una base de datos MySQL:

#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel resultado("");
    if (QSqlDatabase::isDriverAvailable("QMYSQL")) {
        QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
        db.setHostName("localhost");
        db.setDatabaseName("nombreBD");
        db.setUserName("usuarioBD");
        db.setPassword("claveBD");
        if ( db.open() )
           resultado.setText("Bien, base de datos cargada");
        else {
             QSqlError mensaje = db.lastError();
            resultado.setText(mensaje.text());
            }
    }
    else
        resultado.setText("No hay driver");
    resultado.show();
    return app.exec();
}

No creo necesario indicar (pero por si acaso lo hago) que hay que sustituir los valores: nombreBD, usuarioBD y claveBD por los valores adecuados.

Se puede indicar al programa que se conecte a un puerto diferente del habitual mediante:

        db.setPort(3306);

QT+MySQL (2ª parte): Comprobar si está disponible el driver MySQL para QT

Una sencilla aplicación que nos permite comprobar si está disponible el driver MySQL para usarlo con QT.

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel resultado("");
    if (QSqlDatabase::isDriverAvailable("QMYSQL")) {
       resultado.setText("Driver Mysql disponible");
    }
    else
        resultado.setText("Mala suerte, driver Mysql NO disponible");
    resultado.show();
    return app.exec();
} 

y para compilar no hay que olvidar editar el fichero *.pro y añadir la línea:

qt += sql

En este sencillo programa todo el trabajo importante recae sobre la función estática isDriverAvailable (de la clase QSqlDatabase), que es la que comprueba si está disponible el driver.

QT+MySQL (1ª parte): Compilar el driver MySQL para QT Windows

Si usas la versión de Windows XP de la QT seguramete habrás descubierto que el driver para MySQL no viene “de serie” por lo que es necesario compilarlo.

El problema a la hora de compilarlo es que las librerías libmysql.dll y libmysql.lib están compiladas con el compilador de Microsoft y no son compatibles con el compilador MinGW (el compilador para el que vienen preparadas las QT Open Edition). De tal forma que cuando intentemos crear nuestro driver nos encontraremos con un simpático mensaje similar a éste:

tmpobjrelease_sharedqsql_mysql.o(.text+0x4273):qsql_mysql.cpp: undefined refe
rence to `mysql_init@4'
tmpobjrelease_sharedqsql_mysql.o(.text+0x436e):qsql_mysql.cpp: undefined refe
rence to `mysql_real_connect@32'
tmpobjrelease_sharedqsql_mysql.o(.text+0x441d):qsql_mysql.cpp: undefined refe
rence to `mysql_select_db@8'
tmpobjrelease_sharedqsql_mysql.o(.text+0x449d):qsql_mysql.cpp: undefined refe
rence to `mysql_get_client_version@0'
tmpobjrelease_sharedqsql_mysql.o(.text+0x44b7):qsql_mysql.cpp: undefined refe
rence to `mysql_get_server_version@4'
tmpobjrelease_sharedqsql_mysql.o(.text+0x4642):qsql_mysql.cpp: undefined refe
rence to `mysql_close@4'
...
collect2: ld returned 1 exit status
mingw32-make[1]: *** [........pluginssqldriversqsqlmysql.dll] Error 1
mingw32-make[1]: Leaving directory `C:/Qt/4.1.3/src/plugins/sqldrivers/mysql'
mingw32-make: *** [release] Error 2

Afortunadamente existe una solución: convertir las librerías a un formato válido para MinGW. Esto se hace con unos sencillos pasos:


cd c:\mysql\lib\opt
reimp -d libmysql.lib
dlltool -k --input-def libmysql.def --dllname libmysql.dll --output-lib  libmysql.a

(*) reimp viene en el paquete “MinGW-utililities”, que puede encontrarse en: http://sourceforge.net/project/showfiles.php?group_id=2435

(**) se supone que MySQL está instalado en (c:\mysql)

Ahora tenemos una librería llamada libmysql.a compatible con MinGW, ya sólo nos queda compilar el driver Mysql. Ahora podemos hacerlo tal y como se indican en las instrucciones de las QT:

  1. cd c:\qt\src\plugins\sqldrivers\mysql
  2. qmake -o Makefile "INCLUDEPATH+=C:\MYSQL\INCLUDE"  "LIBS+=-LC:\MYSQL\LIB\OPT -lmysql"  mysql.pro
  3. make (por si acaso haz un “make clean” antes de make).

¡Listo! ya podemos disfrutar de nuestro nuevo y flamante driver para MySQL.

Fuente: Foros de las QT (un buen sitio para resolver tus dudas).

Ten en cuenta que la librería libmysql.dll (C:\mysql\lib\opt\libmysql.dll) tiene que estar disponible para el programa (tiene que estar en la variable PATH o en el mismo directorio que nuestro programa). En caso contrario el driver no se cargará correctamente.