domingo, 28 de diciembre de 2014

Guardar matriz en Memoria Compartida

Ya sabemos que no se pueden almacenar punteros en memoria compartida (las direcciones de memoria son diferentes para diferentes procesos). Esto acarrea que no se puedan crear vectores multidimensionales de la manera "tradicional" ( int ** ). 
La solución esta en emular las matrices al puro estilo de malloc (método index magic)

Supongamos que queremos declarar una matriz de enteros de 5x5 en memoria compartida.

El tamaño de la memoria será: el tamaño del tipo de dato multiplicado por la cantidad de columnas y la cantidad de filas.

int id_Memoria = shmget (key,sizeof(int) * 5 * 5, 0777 | IPC_CREAT);



Ahora para acceder a la memoria sería:

// nCol = Cantidad de columnas de la matriz
matriz[x][y] ----> matriz[y * nCol + x];



Ejemplo con código:



  1. int nFil = 5; // numero de filas
  2. int nCol = 5; // numero de columnas
  3. int row, column;
  4. int *matrix;
  5.  
  6. // Crea el segmento de memoria compartida
  7. id_shmem = shmget(ipc_key, sizeof(int)*nFil*nCol, IPC_CREAT|0777); 

  8. matrix = (int *)shmat(id_shmem, 0, 0);
  9.  
  10. matrix[3*nCol + 2] = 1; // Equivalente a matrix[2][3]   


Links: 

http://stackoverflow.com/questions/1961504/is-it-possible-to-allocate-a-2d-array-as-shared-memory-with-ipc

jueves, 25 de diciembre de 2014

Conectar señales de Qt con funciones en C

La nueva sintaxis de Qt 5 nos permite conectar señales de objetos de Qt con funciones estáticas comunes.

Para ello usamos la siguiente sintaxis:

connect(emisor,  &ClaseDelEmisor::señal,  funcion);

Por ejemplo:


connect(timer, &QTimer::timeout, &f );



Ejemplo:

/** Cuando se vence el tiempo del timer y se envia
     la señal timeout() se ejecuta la función f **/


void f(){
    qDebug()<<"&timer.timeout ----> f()";
}


int main(int argc, char *argv[])
{   
    QCoreApplication a(argc, argv);    
    QTimer *timer = new QTimer();


    // Se conecta la señal timeout con la función f
    timer->connect(timer, &QTimer::timeout, &f );    

    timer->start(1000);    
    return a.exec();
}

Diagrama:



Links:

viernes, 19 de diciembre de 2014

Manejo de grupos de procesos: setpgid y getpgid

Los grupos de procesos se usan para controlar a un conjunto de procesos asociados entre si a través de un identificador. De manera especifica se emplean para la distribución de señales. Así, si se le envía una señal al grupo de procesos cada uno de los miembros de ese grupo recibe dicha señal. El identificador del grupo de proceso recibe el nombre de pgid. 

Es importante saber que:
  • Todo proceso pertenece a un grupo de procesos.
  • En un grupo de procesos pueden haber uno o más procesos.
  • Cuando un proceso crea un hijo este hereda el PGID del padre.
  • Un proceso puede cambiarse de grupo usando la función setpgid()
  • Cualquier proceso puede saber a que grupo pertenece otro proceso si conoce el PID.


Asignar un PGID


int setpgid(pid_t pid, pid_t pgid);

La función setpgid asigna un pgid al proceso a un proceso. El parámetro 
pid indica el PID del proceso a modificarSi pid es cero, se emplea el PID del proceso en curso. 
Si pgid es cero, se emplea el PID del proceso especificado por pid. 
Si setpgid se utiliza para mover un proceso de un grupo de procesos a otro (como hacen algunos shells cuando crean tuberías), ambos grupos de procesos deben formar parte de la misma sesión. En este caso, pgid especifica el grupo de procesos existente en el que vamos a entrar, y el ID de sesión de ese grupo de procesos debe coincidir con el ID de sesión del proceso que quiere entrar. En  caso de éxito, setpgid y setpgrp devuelven cero. En caso de error. devuelven -1  y ponen un valor apropiado en errno

Obtener un PGID


La función getpgid devuelve el ID del grupo de proceso del especificado por pid. Si pid es cero, se emplea el PID del proceso en curso. getpgid devuelve un grupo de proceso si acaba bien; -1 en caso de error, y pone  un  valor apropiado en errno.

Enviar señales a un grupo de procesos

Se usa la función kill, con la salvedad que el pgid debe ser negativo. Esto para diferenciar del PID de algun proceso.
kill(-PGID, SIGNAL) // Note el signo negativo antes del PGID


Ejemplo #1:

En este ejemplo el proceso principal ( P1 ) creará 5 procesos hijos, los cuales heredarán su PID. 
Los hijos se dormirán al crearse y esperarán la señal del padre. 
El padre luego de crearlos duerme 2 segundos y luego le envía un mensaje a su propio grupo de procesos para despertar a todos los hijos.
Para ello usa kill(-getpgid(getpid()),SIGCONT);



#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <signal.h>

using namespace std;

// SIGCONT Signal handler
void h(int){}

int main(int argc, char *argv[])
{
    signal(SIGCONT,&h);
    int pid=0;
    cout<<"PID Padre="<<getpgid(getpid())<<endl;
    cout<<"PGID Padre="<<getpgid(getpid())<<endl;

    // El padre crea 5 hijos
    for (int i = 0; i < 5; ++i) {
        pid=fork();

        // Hijo
        if (pid==0){
            cout<<"Hijo "<<getpid()<<": Mi PGID es="<<getpgid(getpid())<<endl;

            // EL hijo se duerme
            sleep(300);

            // Cuando recibe la señal SIGCONT el hijo se despierta
            cout<<"Hijo "<<getpid()<<": Me despertaron"<<endl;
            exit(0);
        }
    }

   sleep(2);
   cout<<"Padre: envío SIGCONT al grupo "<<getpgid(getpid())<<endl;

   // Note que para enviar al grupo de procesos el valor
   // debe ser negativo
   kill(-getpgid(getpid()),SIGCONT);
}



Links de interés:


jueves, 18 de diciembre de 2014

Ejemplo de manejo de MEMORIA COMPARTIDA en Linux

Para manejar memoria compartida basta con realizar estos tres pasos:


Lo primero es crear la clave para la memoria compartida. Para ello se usa la función ftok que recibe dos parámetros: La ruta de un fichero existente y un número para generar la clave:

// Se crea la clave para la memoria compartida
clave = ftok ("/bin/ls", 34);

Luego creamos la memoria con la clave conseguida. Para ello usamos shmget
El primer parámetro es la clave
El segundo es la cantidad de bytes que se va a ocupar la memoria. En este caso queremos reservar espacio para un solo entero.
El tercero son los flags (permisos y modos). IPC_CREAT indica que debe crear la zona de memoria si esta no existe. En caso de que exista la usa de igual forma. Si se le agrega IPC_EXCL falla si el id ya existe. Esto para evitar usar una zona que ya este en uso o creada.

// Se crea la memoria compartida
id_Memoria = shmget (clave,  sizeof(int)*1,  0777 | IPC_CREAT);

Por último obtenemos la dirección de la memoria compartida y la guardamos en un puntero. para ello usamos shmat. El primer parametro es el id que obtuvimos con shmget. El resto de parámetros basta con colocarlos en cero :) 
// Se apunta a la dirección de memoria compartida 
memoriaCompartida = (int *)shmat (id_Memoria, (char *)0, 0);
Con estos tres pasos ya podemos usar la memoria compartida.
En este ejemplo vemos a dos procesos:

* Restador: Decrementa el valor de la memoria compartida.
* Sumador:  Incrementa el valor de la memoria compartida.
En la memoria compartida estará alojado un entero


Este es el código del sumador. El del restador es casi igual, solo que en vez de incrementar decrementa.
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <stdlib.h>

using namespace std;

int main()
{
    key_t clave=0;
    int id_Memoria=0;    
    
    int *memoriaCompartida = NULL;

    // Se crea la clave para la memoria compartida
    clave = ftok ("/bin/ls", 34);   
    if (clave == -1){
        cout << "No consigo clave para memoria compartida" << endl;
        exit(0);
    }

    // Creamos la memoria con la clave recién conseguida. Para ello llamamos
    // a la función shmget pasándole la clave, el tamaño de memoria que
    // queremos reservar (100 enteros en nuestro caso) y unos flags.
    // Los flags son  los permisos de lectura/escritura/ejecucion
    // para propietario, grupo y otros (es el 777 en octal) y el
    // flag IPC_CREAT para indicar que cree la memoria.
    // La función nos devuelve un identificador para la memoria recién
    // creada.
    //
    id_Memoria = shmget (clave, sizeof(int)*1, 0777 | IPC_CREAT);
    if (id_Memoria == -1)    {
        cout << "No consigo Id para memoria compartida" << endl;
        exit (0);
    }

    //
    // Una vez creada la memoria, hacemos que uno de nuestros punteros
    // apunte a la zona de memoria recién creada. Para ello llamamos a
    // shmat, pasándole el identificador obtenido anteriormente y un
    // par de parámetros extraños, que con ceros vale.
    //
    memoriaCompartida = (int *)shmat (id_Memoria, (char *)0, 0);
    if (memoriaCompartida == NULL)
    {
        cout << "No consigo memoria compartida" << endl;
        exit (0);
    }

    // Ya podemos utilizar la memoria.
    // Escribimos cosas en la memoria. Los números de 1 a 10 esperando
    // un segundo entre ellos. Estos datos serán los que lea el otro
    // proceso.
    //
    while (true)
    {
        // Incrementa el valor en memoria compartida
        memoriaCompartida[0]++;
        cout<<"Valor del entero en Memoria compartida = "<<memoriaCompartida[0]<<endl;
        fflush(stdout);
        sleep(1);
    }

    // shmdt desasocia una region de memoria compartida del espacio
    //  de direcciones virtuales del proceso
    //
    shmdt ((char *)memoriaCompartida);

    // Marca el ID de la memoria para ser destruido:
    // NO DESTRUYE la memoria, solo la marca.
    // Cuando se muere el ultimo proceso asociado a esa
    // memoria entonces el sistema se encarga de borrar la
    // memoria
    shmctl (id_Memoria, IPC_RMID, (struct shmid_ds *)NULL);
}
Puedes descargar el ejemplo DESDE AQUI.




¿Como funciona la función ftok?

La función ftok crea identificadores "únicos" para ser usados por funciones IPC (semgetshmgetmsgget). 

Recibe dos parámetros:
* Una ruta a un fichero existente y accesible
* Un número

ftok ( char *pathname, char proj );

Ejemplo: ftok ( "/bin/ls", 34 );


La función busca el inodo del fichero indicado y lo combina con el número del segundo parámetro.

Note que si se pasa el mismo fichero y el mismo número a dos procesos que estén corriendo en la misma máquina entonces e generará la misma clave. Esto puede ser indeseado en muchos casos. Para evitar colisiones de clave es habitual usar el directorio actual de trabajo (.) como primer parámetro. El directorio actual es representado por un punto.

ftok ( ".", 89 );

miércoles, 17 de diciembre de 2014

Mover objetos con transiciones en Qt con QPropertyAnimation

Aqui explicaremos como mover un objeto de Qt de un lugar a otro de la pantalla y que este se valla deslizando:
Basta con crear una animación de movimiento con QPropertyAnimation.


// Se crea la animación 
// el parámetro OBJETO es el objeto que queremos mover
// Puede ser un QLabel, QGraphicsWidget, etc
QPropertyAnimation *animation = new QPropertyAnimation(OBJETO, "pos"); 


// Duración de la animación. El tiempo desde que sale hasta que llega
animation->setDuration(1000); 

// Posición inicial de donde va a partir
animation->setStartValue(QPointF(this->x(), this->y()));

// Posición final a donde va a llegar
// En este caso le estoy diciendo que se mantenga en el mismo
// valor de x pero que varie el valor de Y en 100 px
animation->setEndValue(QPointF(this->x(), this->y()+100));

// Inicia la animación
animation->start(QAbstractAnimation::DeleteWhenStopped);


Este es un ejemplo compilable:

#include <QtCore>
#include <QtWidgets>
#include <QObject>
#include <QCoreApplication>

/** @brief Representa una figura cuadrada **/
class Cuadrado : public QGraphicsWidget
{
public:

    /** Constructor
     *  @param colorDeseado Color que el usuario desea
     *         Puede ser "azul" o "rojo". En caso contrario
     *         la pieza se dibujará gris
     **/
    Cuadrado(QString colorDeseado):QGraphicsWidget(){

        // Asigna el color segun el color deseado
        color= colorDeseado=="rojo"? Qt::red : (colorDeseado=="azul"?
               Qt::blue : Qt::gray);
    }

    ~Cuadrado(){}

    /** Pinta la pieza segun el color **/
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, 
               QWidget *){
        painter->fillRect(rect(), color);
    }

    void mover(qreal xEnd,qreal yEnd){
 // Aqui crea la animación
        QPropertyAnimation *animation = new QPropertyAnimation(this
                                                               "pos"); 
        animation->setDuration(8000); // 8 Segundos
        animation->setStartValue(QPointF(this->x(), this->y()));
        animation->setEndValue(QPointF(xEnd, yEnd));
        animation->start(QAbstractAnimation::DeleteWhenStopped);
    }

private:

    /** Color del cuadrado **/
    Qt::GlobalColor color;
};


int main(int argc, char **argv)
{ 
    QApplication app(argc, argv);

    // Creamos un cuadro rojo y uno azul
    Cuadrado *cuadroAzul=new Cuadrado("azul");
    Cuadrado *cuadroRojo=new Cuadrado("rojo");

    // Los movemos a la posición inicial
    cuadroAzul->moveBy(60,10);
    cuadroRojo->moveBy(10,10);

    // Se crea la escena
    QGraphicsScene scene(0, 0,600, 600);

    // Se agrega los cuadros a la escena
    scene.addItem(cuadroAzul);
    scene.addItem(cuadroRojo);

   // Se mueve con animación
    cuadroAzul->mover(200,500);
    cuadroRojo->mover(100,500);

    QGraphicsView window(&scene);
    window.show();
    return app.exec();
}




Manejo de Procesos y señales POSIX con Qt


Qt no integra ninguna solución que encapsule y simplifique la gestión de señales POSIX, puesto que éstas no están disponibles en sistemas operativos que no soporten dicha especificación.

Aun así en la documentación de Qt se describe una forma de usar manejadores de señales POSIX. Realmente basta con que el manejador de señal haga algo que provoque que Qt emita una señal y después retorne. Al volver a la secuencia normal de ejecución del programa, se emitiría la señal de Qt invocando el slot al que está conectada, desde donde se ejecutarían de forma segura las operaciones que fueran necesarias.

Para esta entrada he creado una clase en C++ llamada QPosixSignalListener que permite abstraer todo el proceso de gestión de señales. Me basé en el artículo de la documentación oficial que trata sobre manejo de señales.

Diagrama de uso de la clase:



Interfaz de la clase:

class QPosixSignalListener : public QObject
{
    Q_OBJECT
public:
    /** Constructor. Setea las señales y crea
     *  los sockets correspondientes
     *
     *  @param parent Padre del objeto
     ***/
    QPosixSignalListener(QObject *parent = 0);

    ~QPosixSignalListener(){}

    // Unix signal handlers.
    static void hupSignalHandler(int);
    static void termSignalHandler(int);
    static void usr1SignalHandler(int);
    static void usr2SignalHandler(int);

    // Set the Unix signal handlers
    static int setup_unix_signal_handlers();

private slots:

    // Qt signal handlers.
    void handlerQt_SIGHUP();
    void handlerQt_SIGTERM();
    void handlerQt_SIGUSR1();
    void handlerQt_SIGUSR2();

signals:

    // Signals
    void sigHUP();
    void sigTERM();
    void sigUSR1();
    void sigUSR2();

private:

    // sockets
    static int socketSIGHUP[2];
    static int socketSIGTERM[2];
    static int socketSIGUSR1[2];
    static int socketSIGUSR2[2];

    // Socket Notifiers
    QSocketNotifier *skNotifierHUP;
    QSocketNotifier *skNotifierTERM;
    QSocketNotifier *skNotifierUSR1;
    QSocketNotifier *skNotifierUSR2;
};

La clase puede manejar las señales SIGHUP, SIGTERM, SIGUSR1 y SIGUSR2. Puedes modificarla para que soporte otras.


USO:





Cuando el proceso recibe una señal (soportada) el objeto emite una señal de Qt.

Para usar la clase solo basta con instanciar un objeto y conectar una de sus señales a un slot cualquiera. Ejemplo:


    QPosixSignalListener *signalListener=new QPosixSignalListener();
    
    QObject::connect(signalListener,SIGNAL(sigUSR1()),cuadroAzul,SLOT(mover()));


En este caso cuando se reciba la señal SIGUSR1 entonces se ejecuta el slot mover()




Para probar la clase te puedes descargar este proyecto de Qt que ya tiene un main listo para correr: 
https://github.com/Eitol/QPosixSignalListener/archive/master.zip

Abrelo con QtCreator (recuerda eliminar antes el archivo QPosixSignalListener.pro.user)

Córrelo y envíale señales desde una terminal. 

kill -s SIGUSR1 PID // donde PID es el pid del proceso




Si quieres enviarle señales desde otro programa en Qt puedes ver como se hace aqui:
http://wannabe-programmer.blogspot.com/2014/12/enviar-senales-unix-con-qt.html

Este es el vídeo de la corrida del proyecto



















Links relacionados:

martes, 16 de diciembre de 2014

Enviar señales Unix con Qt

En esta entrada haremos un ejemplo ultra sencillo de un programa gráfico que envía señales Unix cuando se pulsan sus botones.



Cuando pulsamos un botón se ejecuta la función kill con el pid indicado y la señal que corresponde al botón


void MainWindow::on_boton_SIGTERM_clicked()
{
    kill(ui->lineEdit_PID->text().toInt(),SIGTERM);

}


Puedes descargar el proyecto desde aqui