feb
13

Implementazione dinamica di una funzione definita ricorsivamente

Implementazione dinamica di una funzione definita ricorsivamente (Appello Aprile 2010)

Sia data la funzione F così definita :

Si richiede di implementare in linguaggio C:

  • Una funzione Fr che calcoli F (n, k);
  • Una funzione Frd che calcoli F (n, k) in O(n2 );

Implementazione ricorsiva

int F(int n, int k)
{
    if(n*k==0)return n+k;
    return F(n,k-1)+ F(n-1,k)+ F(n-1,k-1);
}

La fuzione viene implementata applicando semplicemente la formula di ricorsione

Implementazione iterativa

int Fi(int n, int k)
{
    int **v=malloc(sizeof(int)*(n+1)*(k+1));
    int x=Fi_implementation(n,k,n+1,k+1,v);
    free(v);
    return x;
}

int Fi_implementation(int n, int k, int a, int b,int v[a][b])
{
    int r,c,x;

    for(r=0; r<=n; r++)
    {
        for(c=0; c<=k; c++)
        {
            if(r*c==0)v[r]1=r+c;
            else
                v[r]1=v[r]1+v[r-1]1+v[r-1]1;
        }
    }
    x=v[n][k];
    return v[n][k];
}

Il calcolo di ogni elemento n,k richiede il calcolo di tutti gli elementi y<n , n<k e questi elementi vengono usati più di una da tutti gli elementi superiori e a destra, salvandoli in una matrice è possibile ridurre la complessità a O(n*k) occupando O(n*k) memoria.
per facilitare la leggibilità del codice si usa una funzione di appoggio che permette di definire il vettore con le sue dimensioni, altrimenti passando semplicemente un puntatore doppio si dovrebbero calcolare gli indirizzamenti esplicitamente.

feb
09

Contare le coppie adiacenti in una sequenza in tempo nLog(n)

Contare le coppie adiacenti in una sequenza in tempo nLog(n) (Appello Settembre 2010)

il testo recita:

Sia data una sequenza di interi a1 , a2 , . . . , an . Diciamo che la sequenza contiene una coppia di numeri consecutivi se esistono due interi ai e aj tali che ai = aj + 1. Implementare un algoritmo per contare il numero di coppie consecutive contenute nella sequenza, che impieghi una complessità di tempo complessiva di n log n e una quantità di memoria aggiuntiva di O(n).

Implementazione attraverso un albero binario

Il problema può essere risolto implementando una treesort e facendo in modo che in fase di inserimento si verifichi l’esistenza delle coppie adiacenti

#define N 10
#include "util.h"
#include "strutture.h"
#include <string.h>  // NULL è definito qui

int piazza(nodo *base, nodo *nodo, int coppie){
    int x=nodo->val-base->val;
    if(x==1 || x==-1){//ho trovato una coppia
        coppie++;
        printf("coppia %d <-> %d (%d) \n",nodo->val,base->val, coppie);
    }
    if(x>0){//maggiore, vado a destra
        if(base->r==NULL){//è vuoto, lo piazzo
            base->r=nodo;
        }else{//non è vuoto, richiamo
            coppie=piazza(base->r, nodo, coppie);
        }
    }else{//è minore, vado a sinistra
        if(base->l==NULL){//è vuoto, lo piazzo
            base->l=nodo;
        }else{//non è vuoto, richiamo
            coppie=piazza(base->l, nodo, coppie);
        }
    }
    return coppie;
}

void trovacoppie(int len, int v[len])
{
    int coppie=0,n;
    nodo *nodi=malloc(sizeof(nodo)*len);

    for(n=0; n<len; n++)
    {
        nodi[n].val=v[n];
        nodi[n].l=NULL;
        nodi[n].r=NULL;
        if(n>0)coppie=piazza(nodi, &nodi[n], coppie);
    }
    free(nodi);
    printf("Un totale di %d coppie trovate\n", coppie);
}

Il contatore delle coppie viene passato a ogni chiamata ricorsiva e viene ritornato in output dalla funzione di ordinamento, in questo modo l’output finale conterrà il conto totale, questo approccio ha complessità O(n*log(n)) , non possono esistere soluzioni con un ordine di complessità minore inquanto non esistono ordinamenti più veloci essendo n*log(n) la dimensione dell’albero generato dalle decisioni necessarie per un ordinamento.
L’albero viene allocato dentro un vettore per facilitarne la deallocazione.

feb
06

Determinare la lunghezza della più lunga sottostringa palindroma

Determinare la lunghezza della più lunga sottostringa palindroma ( Appello Aprile 2010)

Il testo recita:

Data una stringa S = a1 a2 . . . an di n caratteri, si propone il problema di determinare la lunghezza della più lunga palindroma sottostringa di S.

Suggerimento:

per la risoluzione: Sia l[i, j] la lunghezza della più lunga palindroma contenuta nella stringa S[i . . . j] = ai ai+1 . . . aj . Il caso base consiste in una stringa di 0 caratteri, cioè il caso in cui i > j. Altrimenti se i caratteri S[i] ed S[j] sono uguali allora posso controllare se la stringa S[i + 1 . . . j − 1] e palindroma ed eventualmente aggiungere i due caratteri S[i] ed S[j]. In tutti gli altri casi devo risolvere ricorsivamente i problemi l[i + 1, j] e l[i, j − 1] e prendere il massimo. La definizione ricorsiva di una soluzione ottima potrebbe essere così definita

La risoluzione del problema mediante l’utilizzo della programmazione dinamica prevede quindi la costruzione di una tabella di dimensione n × n.Si richiede di implementare in linguaggio C:

  1. a) Una funzione lr che calcoli l[1, n] ossia la lunghezza della piùlunga palindroma contenuta nell’intera stringa ;
  2. b) Una funzione lrd che calcoli l[n, k] in O(n2 );

Implementazione Ricorsiva

int m(char s[], int da, int a)
{
    int x;
    if(da>a)return 0;
    if(da==a)return 1;
    if (s[a]==s[da]){
        x=m(s,da+1, a-1);
        if(x==a-da-1)return x+2;
    }
    int r=m(s,da+1,a);
    int l=m(s,da,a-1);
    x=r-l;
    if(x>0)
        return r;
    else
        return l;
}

La fuzione viene implementata applicando semplicemente la formula di ricorsione

Implementazione dinamica

int m_d(char s[], int da, int a,int n,int k, int v[n][k])
{
    int x;
    if(da>a)return 0;
    if(da==a)return 1;
    if (s[a]==s[da]){
        x=v[da+1][a-1];
        if(x==a-da-1)return x+2;
    }
    int r=v[da+1][a];
    int l=v[da][a-1];
    x=r-l;
    if(x>0)
        return r;
    else
        return l;
}

int m_iterativa(char stringa[])
{
    int len=strlen(stringa);
    int **v=malloc(sizeof(int)*len*len);
    int i,j;
    for(i=0;i<len;i++){
        for(j=0;j<(len-i);j++){
            int pal=m_d(stringa,j,i+j,len,len,v);
            v[j*len+(i+j)]=pal;
        }
    }
    len=v[len-1];
    free(v);
    return len;
}

L’implementazione dinamica viene creata modificando la funzione ricorsiva in modo che i valori già calcolati vengano letti da una matrice invece che ricalcolati. I valori per cui c<r (quelli sopra la diagonale principale) sono necessariamente 0 e non vanno calcolati.

L’unico modo di percorrere la matrice senza creare dipendenze da valori non ancora calcolati è per diagonali.

Esempi di matrici dei valori parziali










[ 0 0 0 0 0 0 1 ]
[ 0 0 0 0 0 1 1 ]
[ 0 0 0 0 1 1 1 ]
[ 0 0 0 1 1 1 1 ]
[ 0 0 1 1 3 3 3 ]
[ 0 1 1 1 3 5 5 ]
[ 1 1 1 1 3 5 5 ]
pabcbaq -> 5

[ 0 0 0 0 0 0 0 0 0 0 0 0 1 ]
[ 0 0 0 0 0 0 0 0 0 0 0 1 1 ]
[ 0 0 0 0 0 0 0 0 0 0 1 1 1 ]
[ 0 0 0 0 0 0 0 0 0 1 1 3 3 ]
[ 0 0 0 0 0 0 0 0 1 1 3 3 3 ]
[ 0 0 0 0 0 0 0 1 1 1 3 3 3 ]
[ 0 0 0 0 0 0 1 1 3 3 3 3 3 ]
[ 0 0 0 0 0 1 1 1 3 3 3 3 3 ]
[ 0 0 0 0 1 2 2 2 3 3 3 3 3 ]
[ 0 0 0 1 1 2 4 4 4 4 4 4 4 ]
[ 0 0 1 1 1 2 4 4 4 4 4 4 4 ]
[ 0 1 2 2 2 2 4 4 4 4 4 4 4 ]
[ 1 1 2 4 4 4 4 4 4 4 4 4 4 ]
ammaccabanane -> 4

feb
04

Ricerca del minimo numero di caratteri da aggiungere a una stringa per renderla palindroma

Ricerca del minimo numero di caratteri da aggiungere a una stringa per renderla palindroma ( Compito del 28 Settembre 2011)

Il testo recita:

Una stringa e palindroma se non cambia leggendola da destra a sinistra e viceversa. Ad esempio anna e osso sono due palindromi.

Risolvere il seguente problema: data una stringa S = {a 1 , a2 , . . . , an } di n caratteri, calcolare il minimo numero di caratteri che occorre inserire per renderla un palindromo.

Suggerimento per la risoluzione:

Sia P [i, j] il numero di caratteri necessari per trasformare la stringa S[i, j] in una stringa palindroma. Il caso base consiste in una stringa di 1 (o meno) caratteri, cioè il caso in cui i = j.

In tal caso ovviamente non serve nessun carattere per rendere la stringa palindroma perch lo gi. Altrimenti se i caratteri S[i] ed S[j] sono uguali allora posso semplicemente risolvere il sottoproblema P [i + 1; j − 1]. Viceversa dovrò inserire almeno un carattere per rendere la stringa palindroma e calcolare il minimo tra i sottoproblemi P [i + 1; j] e P [i; j + 1], risolti ricorsivamente. La definizione ricorsiva di una soluzione ottima potrebbe essere così definita

La risoluzione del problema mediante l’utilizzo della programmazione dinamica prevede quindi la costruzione di una tabella di dimensione n × n.

Implementazione ricorsiva

int p(char s[], int da, int a){
  if(da>=a)return 0;
  if (s[a]==s[da])return p(s, da+1, a-1);
  int r=p(s,da+1,a);
  int l=p(s,da,a-1);
  int x=r-l;
  if(x>0)
    return l+1;
  else
    return r+1;
}

int palindromizza(char stringa[]){
  return p(stringa, 0, strlen(stringa)-1);
}

La fuzione viene implementata applicando semplicemente la formula di ricorsione

Implementazione Dinamica

int p_d(char s[], int da, int a, int r, int c, int v[ r ][ c ] )
{
    if (da >= a)
    {
        return 0;
    }
    if (s[a] == s[da])
    {
        return v[da + 1][a - 1];
    }
    int destra = v[da + 1][a];
    int sinistra = v[da][a - 1];
    int delta = destra - sinistra;
    if (delta > 0)
    {
        return sinistra + 1;
    }
    else
    {
        return destra + 1;
    }
}

int p_iterativa(char stringa[])
{
    int len=strlen(stringa);
    int **v=malloc(sizeof(int)*len*len);
    int i,j;
    for(i=0;i<len;i++){
        for(j=0;j<(len-i);j++){
            int pal=p_d(stringa,j,i+j,len,len,v);
            v[j*len+(i+j)]=pal;
        }
    }
    len=v[len-1];
    free(v);
    return len;
}

L’implementazione dinamica viene creata modificando la funzione ricorsiva in modo che i valori già calcolati vengano letti da una matrice invece che ricalcolati. I valori per cui c>r sono necessariamente 0 (quelli sopra la diagonale principale) e non vanno calcolati.

L’unico modo di percorrere la matrice senza creare dipendenze da valori non ancora calcolati è per diagonali.

Esempi di matrici dei valori parziali

[ 0 0 0 0 0 ]
[ 0 0 0 0 1 ]
[ 0 0 0 1 2 ]
[ 0 0 1 0 1 ]
[ 0 1 2 1 0 ]ABCBA -> 0
[ 0 0 0 0 0 ]
[ 0 0 0 0 1 ]
[ 0 0 0 0 1 ]
[ 0 0 1 1 0 ]
[ 0 1 0 1 1 ]pappa -> 1
[ 0 0 0 0 0 ]
[ 0 0 0 0 1 ]
[ 0 0 0 1 2 ]
[ 0 0 1 2 3 ]
[ 0 1 2 3 4 ]abcde -> 4

gen
05

Come creare una sottorete sottoposta a sniffing utilizzando un router supplementare

In questo post descriverò la procedura che ho utilizzato per creare una sottorete all’interno della mia rete domestica da utilizzare per eseguire analisi delle cominicazioni di rete ( per motivi di sviluppo o di sicurezza) o per creare Honeypot. L’idea era quella di creare una seconda rete collegata alla rete domestica e quindi a internet attraverso un host che fa da proxy ( nel senso etimologico del termine) che chiameremo Routing server e logga tutte le conversazioni verso l’esterno per poterle analizzare in seguito.

I requisiti sono quindi:

  • La sottorete creata deve aver accesso diretto e trasparente ad internet
  • Tutto il traffico che attraversa il gateway della sottorete deve essere loggato ( quindi sia quello che esce che quello che entra)
  • Il traffico della sottorrete esistente ( incluso quello generato dal server di routing su eth1 che non è di forwarding) non deve essere loggato
  • Il traffico strettamente interno alla sottorete è irrilevante
  • L’interazione della sottorete con la rete interna preesistente deve essere minimo

La topologia di rete utilizzata è

Descrivendo la topologia dal punto di vista del Routing Server si ha la configurazione seguente:

  • All’interfaccia Eth1 è collegato il router che ha l’accesso ad internet, ad esso è collegata anche la sottorete preesistente
  • L’indirizzo del Routing server in Eth0 è 192.168.1.64
  • L’indirizzo del Routing server in Eth0 è 192.168.0.5, dovrà fare da gateway per il router interno
  • L’indirizzo (interno) del router connesso ad internet è 192.168.1.254
  • L’indirizzo del router interno è 192.168.0.1
  • All’interfaccia Eth0 è collegato un secondo router ( in particolare un router wireless visto che la funzione principale del progetto è studiare applicazioni Android)
  • Il router connesso ad internet ha una sottorrete definita dalla netmask 192.168.1.0/24
  • il router interno una sottorete definita dalla netmask 192.168.0.0/24

Routing Server collega le due sottoreti attraverso funzioni di forwarding implementate attraverso iptables, è quindi un host Linux

Configurazione del Routing server

Come dicevo il routing server è un host linux con due schede di rete che collega le due sottoreti forwardando i pacchetti con iptables. Per rendere possibile questo dobbiamo innanzitutto terminare il servizio network manager ( /etc/init.d/network-manager stop )

La configurazione viene eseguita attraverso uno script e viene quindi annullata al riavvio. lo script è il seguente:

#parte1
pkill dhclient
ifconfig eth0 192.168.0.5
route add -net 192.168.0.0 netmask 255.255.255.0 dev eth0 gw 192.168.1.64
route del default
route add default gw 192.168.1.254

#parte2
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -F
iptables -t nat -P PREROUTING ACCEPT
iptables -t nat -P POSTROUTING ACCEPT
iptables -t nat -P OUTPUT ACCEPT
iptables -t mangle -P PREROUTING ACCEPT
iptables -t mangle -P OUTPUT ACCEPT
iptables -A POSTROUTING -t nat -o eth1 -j MASQUERADE
iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT
iptables -A FORWARD -o eth1 -i eth0 -j ACCEPT

#parte3
iptables -A FORWARD -p tcp -s 192.168.0.5/24 --sport 135 -j DROP #microsoft epmap
iptables -A FORWARD -p udp -s 192.168.0.5/24 --sport 137 -j DROP #netbios ns
iptables -A FORWARD -p tcp -s 192.168.0.5/24 --sport 138 -j DROP #netbios dgm
iptables -A FORWARD -p udp -s 192.168.0.5/24 --sport 1900 -j DROP #SSDP
iptables -A FORWARD -p tcp -s 192.168.0.5/24 --sport 445 -j DROP #microsoft-ds

vediamo di descrivere lo script:

la #parte1

termina i servizi di dhcp ancora in vita e poi setta in maniera statica l’indirizzo di Eth0 e setta il gateway di Eth0 a 192.168.1.64 e il gateway di default a 192.168.1.254 (il router connesso)

la #parte2

attiva e configura il forwarding tra le due interfacce, la prima riga lo attiva nel caso fosse disattivato

la #parte3

filtra i pacchetti provenienti da protocolli tipici di windows e provenienti dalla rete preesistente . questi pacchetti gonfierebbero inutilmente la dimensione dei file di log

Configurazione del router interno

Il router interno dovrà utilizzare 192.168.0.5 come gateway ( indirizzo di Router Server su Eth0) ma utilizzare un dns esterno ( nel mio caso opendns)

L’indirizzamento dhcp deve essere configurato facendo in modo che siano dinamici solo gli indirizzi superiori a 192.168.0.5

Sniffing!

Una volta configurato il tutto lo sniffing è molto semplice, basta avviare uno sniffer qualunque ( tcpdump, wireshark) e metterlo in ascolto su Eth0

In questo modo possiamo anche continuare a navigare senza che il nostro traffico compaia nei log

nov
25

le Condition, implementazione delle code di attesa. il problema del barbiere addormentato (C++0x)

Le Condition

Le Condition sono un meccanismo di sincronizzazione che permette ai thread di sospendersi in attesa che avvenga un evento e di essere risvegliati quando l’evento accade, permettono quindi di creare delle code di attesa.

Nelle STL le condition sono oggetti della classe std::condition_variable , i metodi fondamentali per usarle sono tre:

  • wait() permette di accodarsi alla condition
  • notify_one() notify_all() permettono di risvegliare uno o tutti i thread in attesa sulla coda (nell’implementazione tradizionale dei pthread si chiamavano signal e broadcast)

A una Condition è sempre associato un mutex per evitare race condition che potrebbe creare un thread che si sta preparando a una wait mentre un altro thread esegue una signal, la signal potrebbe infatti avvenire contemporaneamente alla wait e venire quindi persa creando un deadlock, può essere usato un mutex qualunque o si può creare un mutex proprio della condition

Vediamone il funzionamento con un semplice codice: 10 thread si mettono in coda su una condition e un altro thread li sveglia uno per volta

#include <iostream>
#include <thread>
#include <cstdlib>
#include <mutex>
#include <condition_variable>

using namespace std;

class Worker
{
public :

    int numero;
    std::condition_variable *coda;
    mutex *m;
    Worker(int n, std::condition_variable *cond, mutex *mtx)
    {
        coda=cond;
        numero=n;
        m=mtx;
    }
    void operator()()
    {
        cout << "Thread"<< numero << " allocato, mi metto in coda" << endl;
        std::unique_lock<std::mutex> lk(*m);
        coda->wait(lk);
        cout << "Thread"<< numero << " risvegliato dalla coda" << endl;
    }
};

int main()
{
    std::condition_variable *coda= new condition_variable();
    mutex *m= new mutex();
    int a;

    std::thread* threads[10];

    for(a=0;a<10;a++){//creo
        threads[a]= new std::thread(Worker(a+1,coda,m));
        usleep( 100000 );
    }
    for(a=0;a<10;a++){//sveglio
        usleep( 1000 * ( rand() % 900));
        coda->notify_one();
    }
    for(a=0;a<10;a++)//aspetto termine
    {
        threads[a]->join();
        delete(threads[a]);
    }
};

Ma le cose non sono così semplici…

  • Malgrado di solito non succeda le specifiche dei pthread non garantiscono che l’ordine in cui i thread si sveglino sia uguale a quello in cui si addormentano, quindi in caso di applicazioni dove l’ordine degli accessi è importante questo va verificato esplicitamente.
  • Un altra cosa che potrebbe succedere ( e questa è più grave ) è che più di un thread venga liberato dalla coda, quindi i thread appena svegliati devono di nuovo verificare che la condizione che li ha fatti accodare sia stata soddisfatta ed eventualmente rimettersi in coda.

Il problema del barbiere addormentato

Un barbiere di Lisbona...

Un barbiere di Lisbona...

Vediamo un applicazione delle condition a un problema più complesso, il problema del barbiere addormentato di Dijkstra. il problema è definito in questo modo:

Un negozio di barbiere è costituito da una sala con una sola poltrona sulla quale si accomoda il cliente che viene servito e da una sala d’attesa con n sedie. Se non ci sono clienti da servire il bar­biere va a dormire. Se arriva un cliente e tutte le sedie della sala d’attesa sono occupate, il clien­te va via, mentre, se c’è una sedia libera e il barbiere è occupato, il cliente attenderà, seduto, il proprio turno. Se il barbiere sta dormendo (cioè è libero, senza clienti in attesa), il cliente lo sve­glia e viene servito subito.

La soluzione è più prolissa di quello che potrebbe sembrare a prima vista, tralaltro nell’implementarlo ho istintivamente usato un monitor anche se non ho accennato a questo tipo di costrutti, ne do soltanto una breve definizione OOP

Un monitor è un oggetto che incapsula la risorsa ed espone i metodi per accedervi in maniera sicura

In questo caso, come si vede nel sorgente sottostante, il contatore delle sedie occupate è nascosto dentro la classe Stanza che mette a disposizione i metodi per occupare e liberare una sedia mantenendoli sincronizzati attraverso un semaforo proprio della classe.

#include <iostream>
#include <thread>
#include <cstdlib>
#include <mutex>
#include <condition_variable>

using namespace std;

int dormi(float base, float variabile)
{
    int k=1000*1000;
    int base_i=(int)base*k;
    int variabile_i=0;
    if(variabile>0)variabile_i=rand() % (int)(variabile*k);
    usleep(base_i+variabile_i);
    return base_i+variabile_i;
};

class Stanza
{
private:
    int occupate;
    std::mutex monitor_m;

public:

    std::condition_variable attesa;

    std::mutex attesa_m;
    std::condition_variable poltrona;
    std::condition_variable letto;
    std::mutex barbiere;
    int sedie;

    int prenota()
    {
        std::unique_lock<std::mutex> lk(monitor_m);
        if(occupate>=sedie)return -1;
        occupate++;
        cout << occupate << " sedie occupate"<<endl;

        return occupate;
    }

    int guarda()
    {
        std::unique_lock<std::mutex> lk(monitor_m);
        return occupate;
    }

    int libera()
    {
        std::unique_lock<std::mutex> lk(monitor_m);
        occupate--;
        cout << occupate << " sedie occupate"<<endl;
        return occupate;
    }

    Stanza(int n_sedie)
    {
        this->sedie=n_sedie;
        occupate=0;
    }
};

class Barbiere
{
public :

    int numero;
    int azioni;
    Stanza *stanza;
    std::unique_lock<std::mutex> *barbiere;
    Barbiere(Stanza *st)
    {
        azioni=0;
        this->stanza=st;
        barbiere= new std::unique_lock<std::mutex>(stanza->barbiere,std::defer_lock_t());

    }
    void operator()()
    {
        while(true)
        {
            cout << "Barbiere controlla se ci sono clienti in attesa"<<endl;

            if(stanza->guarda()==0)
            {
                cout << "Barbiere si addormenta"<<endl;
                std::unique_lock<std::mutex> lk(stanza->barbiere);
                stanza->letto.wait(lk);
            }

            cout << ++azioni <<" Barbiere serve un cliente"<<endl;
            stanza->attesa.notify_one();
            dormi( 2 , 2 );
            stanza->poltrona.notify_one();
        }
    }
};

class Cliente
{
public :

    int numero;
    Stanza *stanza;
    std::unique_lock<std::mutex> *barbiere;
    Cliente(Stanza *st, int n)
    {
        stanza=st;
        numero=n;
        barbiere= new std::unique_lock<std::mutex>(stanza->barbiere,std::defer_lock_t());

    }
    void work()
    {
        cout << "Thread"<< numero << " allocato, entra dal barbiere" << endl;
        int s=stanza->prenota();

        if(s<0)
        {
            cout << "Thread"<< numero << " tutte le sedie sono occupate, esce" << endl;
            return;
        }
        else
        {
            if(s>0)
            {
                cout << "Thread"<< numero << " si siede e aspetta" << endl;
                if (s==1)
                {
                    cout << "Thread"<< numero << " sveglia il barbiere" << endl;
                    dormi(1,0);
                    stanza->letto.notify_one();
                }
                std::unique_lock<std::mutex> lk(stanza->attesa_m);
                stanza->attesa.wait(lk);
                cout << "Thread"<< numero << " si alza" << endl;
                stanza->libera();
            }

            barbiere->lock();
            cout << "Thread"<< numero << " si siete e si fa rasare" << endl;
            stanza->poltrona.wait(*barbiere);
            barbiere->unlock();
            cout << "Thread"<< numero << " esce" << endl;

        }

    }
    void operator()()
    {
        for(int a=0; a<2; a++)
        {
            work();
            cout << "Thread"<< numero << " va in giro a bighellonare" << endl;
            dormi(30,30);
        }
        cout << "Thread"<< numero << " muore" << endl;
    }
};

int main(int argc, char** argv)
{
    Stanza stanza(5);
    int a;

    std::thread* threads[15];

    std::thread* barbiere=new std::thread(Barbiere(&stanza));
    for(a=0; a<15; a++)   //creo
    {
        dormi(0,a);
        threads[a]= new std::thread(Cliente(&stanza,a+1));
    }
    for(a=0; a<15; a++)   //aspetto termine
    {
        threads[a]->join();
        delete(threads[a]);
    }
};

Il problema è risolto nel modo seguente:

Il barbiere:

  1. Controlla se ci sono clienti in attesa (attraverso i metodi del Monitor)
  2. se non ce ne sono si addormenta ( svegliandosi al punto 3)
  3. se ce ne sono li rade e torna al punto 1

la rasatura consiste in:

  • liberare un Cliente dalla coda della sala di attesa (attesa.notify_one) , il cliente liberato occupa la coda della poltrona
  • radere il Cliente, liberarlo dalla coda della poltrona (poltrona.notify_one())

Il Cliente

  1. Entra nella sala, cerca di sedersi
    1. Se può sedersi:
      1. Se è l’unica persona seduta sveglia il barbiere (letto.notify_one())
      2. va al punto 2
    2. Se non può sedersi esce dalla sala e va al punto 5
  2. Si mette in attesa nella coda della sala (attesa.wait(lk))
  3. una volta svegliato occupa il barbiere (barbiere->lock()) e si mette in attesa sulla sedia della poltrona (poltrona.wait(*barbiere)).
    il mutex sul barbiere evita il problema di una possibile signal non voluta ( vedi sopra il paragrafo “ma le cose non sono così semplici”)
  4. una volta svegliato libera il barbiere ed esce dalla sala
  5. va in giro a bighellonare e poi torna al punto 1

In questa implementazione sono possibili delle signal superflue su un barbiere già sveglio, ma sono irrilevanti

Con questo articolo ho concluso la serie sulla programmazione concorrente in Cpp0X Leggi tutto »

nov
09

Uso avanzato dei Mutex, i 5 Filosofi (C++0x)

Nell’articolo precedente ho parlato del mutex e del suo uso semplice con la lock_guard. in questo tratterò gli altri tipi di guard e l’uso avanzato dei mutex

unique_lock

Con unique_lock un mutex può fare molto di più di quello che permette la lock_guard :

  • Si può cercare di prenotarlo in maniera non bloccante ( se è libero lo si occupa, se non è libero non si attende)
  • Si può cercare di prenotarlo fornendo un timeout massimo di attesa ( se si tratta di un timed mutex ovviamente)
  • Si può accedere direttamente alle funzioni del mutex

e un sacco di altre cose per le quali vi rimando a una reference ( lo so, non è ufficiale… ma non ho trovato di meglio )

Timed mutex e la prenotazione con timeout massimo

Disponendo di un timed mutex potremmo per esempio eseguire questo:

std::timed_mutex m;
std::unique_lock<std::timed_mutex>
 lk(m,std::chrono::milliseconds(5));
if(lk)work()
}

Questa funzione prova ad accedere al mutex aspettando massimo 5 millisecondi, il valore della lock si usa per determinare se l’accesso è avvenuto o no

Accesso alle funzioni interne del mutex

Attraverso la unique_lock possiamo accedere alle funzioni del mutex, ma questa volta le eccezioni sono gestite.

std::mutex m;
void zona_critica()
{
 std::unique_lock<std::mutex> lk(m);
 // in questo momento il mutex è occupato
 lk.unlock();
 // in questo momento il mutex è libero
 lk.lock();
 //in questo momento il mutex è di nuovo occupato
}

Possiamo anche evitare che il mutex venga occupato all’allocazione della unique_lock utilizzando un costruttore diverso:

std::mutex m;
void zona_critica()
{
 std::unique_lock<std::mutex> lk(m,std::defer_lock_t());
 // in questo momento il mutex è libero
 lk.lock();
 // in questo momento il mutex è occupato
 lk.unlock();
 //in questo momento il mutex è di nuovo libero
}

Prenotazione multipla

Un problema molto comune nella programmazione concorrente è la necessita da parte di un processo di accedere in maniera esclusiva a più risorse, è un problema che se gestito male può causare facilmente deadlock o starvation in quanto se un processo che ha acquisito parte delle risorse necessarie attende altre risorse per continuare e queste risorse sono bloccate da altri processi anche loro in attesa tutti i processi restano bloccati in attesa l’uno dell’altro, in questo caso le stl ci vengono in aiuto dandoci un costrutto che permette di bloccare n mutex restando in attesa ma non bloccandoli

void prenota(std::mutex *a ,std::mutex *a)
 {
 std::unique_lock<std::mutex> lock_a(*a, std::defer_lock);
 std::unique_lock<std::mutex> lock_b(*b, std::defer_lock);
 std::lock(lock_a,lock_b);

 // fai qualcosa di critico
 }

Il problema dei 5 filosofi

Illustrazione originale dal testo di Dijkstra

Un problema molto interessante che illustra la potenzialità di questo costrutto è il problema dei 5 filosofi a cena, esso è stato formulato da Dijkstra intorno al 1965, per mettere alla prova le primitive di sincronizzazione dell’epoca ( i semafori fondamentalmente) il problema è questo (cito dal testo di Silberschatz) :

Si considerino cinque filosofi che passano la vita pensando e mangiando. I filosofi con­dividono un tavolo rotondo circondato da cinque sedie, una per ciascun filosofo. Al cen­tro del tavolo si trova una zuppiera colma di riso, e la tavola è apparecchiata con cinque bacchette. Quando un filosofo pensa, non interagisce con i colleghi; quan­do gli viene fame, tenta di prendere le bacchette più vicine: quelle che si trovano tra lui e i commensali alla sua destra e alla sua sinistra. Un filosofo può prendere una bacchetta alla volta e non può prendere una bacchetta che si trova già nelle mani di un suo vicino. Quando un filosofo affamato tiene in mano due bacchette contemporaneamente, man­gia senza lasciare le bacchette. Terminato il pasto, le posa e riprende a pensare.

“Il problema dei cinque filosofi è considerato un classico problema di sincronizzazione, non certo per la sua importanza pratica, e neanche per antipatia verso i filosofi da parte degli informatici, ma perché rappresenta una vasta classe di problemi di controllo della concorrenza, in particolare i problemi caratterizzati dalla necessità di assegnare varie risorse a diversi processi evitando situazioni di stallo e d’attesa indefinita.” la parola starvation deriva proprio da questo problema… se non si riesce a gestire la cosa i filosofi muoiono di fame ( to starve in inglese)

Una semplice soluzione consiste nel rappresentare ogni bacchetta con un mutex: un filosofo tenta di afferrare ciascuna bacchetta eseguendo un’operazione lock su quel mutex e la posa eseguendo una unlock.

Partendo da questo modello possiamo cominciare a cercare una soluzione, quella più immediata potrebbe esere:

un filosofo prende la bacchetta a destra, poi quella a sinistra e poi mangia

ma cosa succede se tutti prendono contemporaneamente la forchetta di destra? un deadlock, muoiono di fame aspettando che qualcuno liberi qualcosa ma nessuno libererà mai niente.

un’altra soluzione potrebbe essere:

Un filosofo di numero pari prende prima la forchetta di destra e poi quella di sinistra, un filosofo dispari il contrario

Ma questo è barare xD scartiamola

Ci rendiamo conto che un filosofo deve rilasciare la bacchetta se non può prendere la seconda, qualcosa del tipo

Un filosofo prende la bacchetta di destra, poi cerca di prendere quella di sinistra (trylock) e se non può le posa entrambe e riprova in seguito

ma che succede se i filosofi partono insieme? l’algoritmo va in loop: tutti prendono quella di destra, nessuno può prendere quella a sinistra, tutti posano tutto e si ricomincia, le operazioni necessitano di essere eseguite in maniera atomica:

Un filosofo cerca di prendere entrambe le bacchette contemporaneamente, se non può aspetta

Questo comportamento non può essere costruito direttamente usando i singoli mutex, ma possiamo farlo con il nostro std::lock(lock_a,lock_b)

La soluzione tradizionale necessita della costruzione di un Monitor, ma non me ne occuperò qui, sfruttiamo a pieno le stl e riduciamo il problema a una cosa banale, con buona pace di Dijkstra

...
class Filosofo {
public :

    int id;
    mutex *destra;
    mutex *sinistra;
    Filosofo(int n, mutex *d, mutex *s) {
        id=n;
        destra=d;
        sinistra=s;
    }

    void pensa() {
        string argomenti[5]= {
            "Alla fame nel mondo",
            "Al punto G",
            "All origine dell Universo",
            "Al senso della vita",
            "Alla domanda la cui risposta e' 42"
        };
        cout << id << ": Sto pensando... " << argomenti[id] << endl;
        usleep( 10000 * ( rand() % 90 + 10 ));
    };

    void mangia() {
        printf("%d: Cerco le bacchette...\n",id);
        std::unique_lock<std::mutex> lock_a(*destra, std::defer_lock);
        std::unique_lock<std::mutex> lock_b(*sinistra, std::defer_lock);
        std::lock(lock_a,lock_b);
        printf("%d: Sto mangiando...\n",id);
        usleep( 30000 * ( rand() % 90 + 10 ));
        printf("%d: Poso le bacchette, sazio\n",id);
    };

    void operator()() {
        printf("%d: Penso quindi sono \n",id);
        usleep( 30000 * ( rand() % 90 + 10 ));
        for(int a=0; a<5; a++) {
            pensa();
            mangia();
        }
    }
};
...

La versione completa del codice si trova qui

per questo articolo è meglio fermarmi, sta diventando troppo lungo, le condition le tratto nel prossimo

References

Se siete interessati alla versione originale del problema di Dijkstra ,risolta interamente con i semafori, potete dare un occhiata a questo testo su google docs, se poi avete anche interessi archeologici c’è la scan della versione originale qui (vi avviso, Dijkstra coda in Agol xD)

Che Dio benedica l’università del texas e google docs, visto che la versione ufficiale nel sito della Springer costa 35€

Il testo di Silberschatz che citavo è “Sistemi operativi. Concetti ed esempi” di Abraham Silberschratz, Peter Baer Galvin, Greg Gagne. potete dargli un occhiata qui , è finora il miglior libro tradotto in italiano sui sistemi operativi in cui mi sia imbattuto

L’ambiente utilizzato

Man mano che ci inoltriamo nella parte più raffinata della libreria abbiamo bisogno di versioni sempre più recenti, stavolta ci serve la 4.6 di gcc oltre a tutte le lib descritte nell’articolo iniziale sui thread

Leggi tutto »

ott
20

Sincronizzazione dei Thread e gestione delle risorse con i Mutex ( C++0X)

Come accennato nel post precedente un applicazione che sfrutta il multithreading è costituita da un insieme di processi sequenziali cooperanti, tutti in esecuzione asincrona che possono condividere dati e risorse.

L’accesso concorrente ai dati o alle risorse può provocare incoerenze nei dati o situazioni di deadlock nel caso di risorse che prevedono accesso esclusivo, in questo post cercherò di analizzare gli strumenti che C++0X offre per la sincronizzazione.

All’interno di un processo si possono separare le sezioni in cui lavora su dati interni da quelle in cui si accede a dati o risorse condivise, queste ultime vengono dette sezioni critiche e sono quelle di cui si deve curare la sincronizzazione.

Non tratterò in questo articolo le soluzioni “storiche” e full-software per la gestione delle sezioni critiche (algoritmi di Dekker, di Peterson, algoritmo del banchiere e del panettiere di Lamport, semafori in spinlock) ma tratterò l’uso di funzioni messe a disposizione dalla libreria che ci permettono di assicurare la mutua esclusione tra processi attraverso lo scheduler.

Per prima cosa cerchiamo di definire cosa deve succedere su una sezione critica: una volta individuata e minimizzata una sezione critica dobbiamo fare in modo che

  1. sia verificata la mutua esclusione ossia che soltanto un processo per volta possa trovarsi nella sezione critica
  2. si eviti l’attesa indefinita da parte di un processo che tenta di accedere a una sezione critica occupata
  3. un processo che si trova fuori dalla sezione critica non deve poter interferire con l’accesso alla sezione critica dei processi in attesa

Chi ha lavorato con i pthread in C si renderà conto che è quasi un wrapper a oggetti quello che il cpp ci offre

Il Mutex

Lo strumento fondamentale per la sincronizzazione offerto dalle stl è il Mutex, che è la crasi di mutual exlusion e serve appunto ad evitare che più processi del necessario possano accedere ad una sezione critica.
Esistono 4 tipi di mutex:

  1. semplice ( std::mutex )
  2. semplice con timeout ( std::timed_mutex )
  3. ricorsivo ( std::recursive_mutex )
  4. ricorsivo con timeout ( std::recursive_timed_mutex )
  • un mutex è semplice se il suo stato è binario ( libero/occupato ) è invece ricorsivo se permette più accessi prima di essere occupato
  • un mutex è timed se è possibile fare in modo che si liberi da solo dopo un certo tempo

fondamentalmente un mutex dispone di almeno 3 funzioni:

  • void lock();
    che permette di occuparlo o di attenderlo se è già occupato
  • void unlock();
    che permette di liberarlo
  • bool try_lock();
    che permette occuparlo se è possibile senza però rimanere in attesa

vediamo come usarli, per prima cosa costruiamo un programma che necessiti di essere sincronizzato

#include <iostream>
#include <thread>
#include <cstdlib>
#include <string>

using namespace std;

void stampa(string testo){
 for(int a=0;a<testo.length();a++){
 cout << testo[a];
 flush(cout);
 usleep( 100 * ( rand() % 900 + 100 ));
 }
 cout << endl;
}

class Worker
{
public :

 string testo;
 Worker(string t){
 testo=t;
 }
 void operator()()
 {
 stampa(testo);
 }
};

int main()
{
 thread t1(Worker("Quanto legno roderebbe un roditore se un roditore potesse rodere il legno?"));
 thread t2(Worker("How much wood could a woodchuck chuck if a woodchuck could chuck wood?"));
 t1.join();
 t2.join();
};

il codice ci farà ottenere qualcosa del tipo:

QHowu anmtuoc hl egwonodo  rcooudelrd ebbae  wuoond rocdhiutcokr cehuc ske  uinf a r odiwtooroed chpuoctkes csouled  rchoduecrek i l woloegdno??

La sincronizzazione si effettua creando un mutex globale e condividendolo tra i thread che vanno sincronizzati e racchiudendo la sezione critica dentro una lock e una unlock

...

class Worker
{
public :

 string testo;
 mutex *m;
 Worker(string t, mutex *mtx){
 m=mtx;
 testo=t;
 }
 void operator()()
 {
 m->lock();
 stampa(testo);
 m->unlock();
 }
};

int main()
{
 mutex *m= new mutex();
 thread t1(Worker("Quanto legno roderebbe un roditore se un roditore potesse rodere il legno?",m));
 thread t2(Worker("How much wood could a woodchuck chuck if a woodchuck could chuck wood?",m));
 t1.join();
 t2.join();
};

Mutex e eccezioni

Malgrado l’accesso diretto alle funzioni del mutex sia possibile è un modo pericoloso di procedere.
Infatti non gestisce le eccezioni, se un thread genera un errore all’interno della zona critica il thread viene terminato ma il mutex non viene sbloccato, quindi il modo corretto di gestire una sezione critica sarebbe usare un try{}catch in questo modo:

 void operator()()
 {
    m->lock();
    try
    {
        stampa(testo);
        m->unlock();
    }
    catch(...)
    {
        m->unlock();
        cout << "Yikes!" << endl;
    }
 }

Un modo più compatto di usare in maniera sicura i mutex si può ottenere usando gli ogetti lock_guard delle stl, che permettono anche di rilasciare il mutex in maniera automatica quando si esce dal blocco in cui sono definite ( lo sbloccano nel loro distruttore per essere precisi), il codice precedente si può riscrivere in questo modo:

 void operator()()
 {
    lock_guard<mutex> lock(*m);
    stampa(testo);
 }

Precisiamo una cosa però…

Il fatto che la guard blocchi il mutex non implica che l’intero programma non venga terminato… infatti dopo aver sbloccato il mutex propaga l’eccezione generata a livello superiore e se non c’è un catch a livello superiore questo equivale alla terminazione del processo padre

Quindi se ci limitiamo a guardare questi snippet l’esempio con la gestione manuale funziona meglio di quello con la lock ma nel caso generale l’uso della lock è comodo perché ci permette di scrivere codici per la gestione delle eccezioni che non tengono conto del mutex…spero di essermi spiegato

E con questo concludo l’articolo, nel prossimo tratterò gli altri tipi di guard e le condition usati per la gestione di situazioni più complesse

Leggi tutto »

ott
11

Il C++ e il Multithreading (C++0X)

Un’altra funzionalità molto interessante introdotta nel C++0X oltre le lambda descritte nell’articolo precedente è il supporto nativo al multithreading.

Esistevano già librerie che si occupavano di multithreading in cpp ma adesso è integrato nel linguaggio e nella sua libreria standard anche se il backend che effettivamente alloca i thread è l’onnipresente libreria pthread.
Cominciamo allora :) do per scontato che abbiate un minimo di familiarità con la programmazione concorrente e come al solito i particolari per la compilazione ( che stavolta potrebbe essere più complicata del solito) sono alla fine dell’articolo

Allochiamo il nostro primo thread

Un thread è un qualcosa che viene eseguito in parallelo, e quindi si crea attraverso una funzione che è quello che deve fare, non è necessario creare una classe intorno al thread come si farebbe in java, basta passare al costruttore di thread la funzione che deve eseguire il thread costruito o qualcosa di equivalente

#include <iostream>
#include <thread>
#include <cstdlib>

using namespace std;

void cosadafare(){
 cout<<"Sono una procedura!"<<endl;
}

class Dummy
{
public :
 void operator()()
 {
 for(int a=0;a<5;a++){
 cout<<"Sono un oggetto, e quindi più figo di una procedura! "<< a <<endl;
 usleep( 1000 * ( rand() % 900 + 100 ));
 }
 }
};

void uccidimi(){
 for(int a=0;a<50;a++){
 cout<<"Io parlo troppo "<< a <<endl;
 usleep( 1000 * ( rand() % 900 + 100 ));
 }
}

int main()
{
 Dummy w;
 std::thread t1(w);
 std::thread t2(cosadafare);
 std::thread t3(uccidimi);

 t1.join();
 t2.join();
 t3.detach();
};

Cpp è un linguaggio marcatamente OOP e infatti non è necessario passargli una procedura, basta qualunque ogetto supporti l’operatore (), possiamo anche creare una classe che definisce questo operatore e passarne un istanza.

E’ interessante notare che quando si chiama un thread su un oggetto, come nel caso di std::thread t1(w), non gli viene passato il vero oggetto ma una sua copia, ma di questo ne parliamo dopo.

Le join servono ad aspettare che i thread finiscono prima di terminare il programma, infatti i thread sono concettualmente ‘figli’ del processo padre ( il fatto che lo siano veramente dipende dall’implementazione) e se terminasse il processo padre i figli verrebbero terminati di conseguenza facendo generare a linux un allarmante messaggio di warning, per terminare un thread prima che abbia finito il suo lavoro si usa detach

Thread e condivisione dei dati

Uno dei vantaggi dei thread rispetto ai processi è che condividono lo spazio di memoria tra di loro, tutto quello che è nello spazio di indirizzamento del processo padre può essere passato ai figli.

Quindi risulta molto conveniente piazzare dentro un oggetto la procedura del thread e i dati in cui dovrà lavorare ma c’è un inconveniente: come dicevamo sopra
quando si chiama un thread su un oggetto, come nel caso di std::thread t1(w), non gli viene passato il vero oggetto ma una sua copia , questo è irrilevante nel caso di oggetti che wrappano solo una procedura ma può essere una fonte di bug subdoli nel caso essi contengano dati infatti modificando l’oggetto non modifichiamo niente all’interno del thread perché sono oggetti diversi

se vogliamo evitare questo comportamento dobbiamo forzare un passaggio via reference e non abbiamo modo di farlo col linguaggio, dobbiamo affidarci alla magia nera delle stl,la soluzione è: std::thread t1(std::ref(dw));

Vediamo un esempio

#include <iostream>
#include <thread>
#include <cstdlib>
#include <string>

using namespace std;

class Dummy
{
public :

 string testo;
 string nome;
 void operator()()
 {
 for(int a=0;a<15;a++){
 cout<<nome << " dice: " << testo<< " " << a <<endl;
 usleep( 1000 * ( rand() % 900 + 100 ));
 }
 }
};

int main()
{
 Dummy w,w2;
 w.testo="ammaccabanane";
 w.nome="thread1";
 w2=w;
 w2.nome="thread2";
 std::thread t1(w);
 std::thread t2(std::ref(w2));
 sleep(2);
 w.testo="Yikes!";
 w2.testo="Yikes!";
 t1.join();
 t2.join();
};

Eseguendolo noterete che il comportamento del primo thread resta invariato, mentre possiamo agire sul secondo modificando i suoi dati dall’esterno.

E se volessi passare parametri alla procedura e magari non usare un oggetto?

Malgrado la soluzione di un oggetto di wrapper può tornare utile il passare dei parametri alla procedura chiamante, il che ci permette anche di non usare completamente un oggetto e di passare tutti i dati su cui deve lavorare il thread alla funzione che ne costituisce il main, vediamo come funziona

#include <iostream>
#include <thread>
#include <cstdlib>
#include <string>

using namespace std;

void stampa(string cosa, string altracosa){
 for(int a=0;a<10;a++){
 cout<< cosa << altracosa << endl;
 sleep(1);
 }
}

int main()
{
 std::thread t1(stampa, "bla bla bla", " yada yada yada");
 t1.join();
};

semplicemente accodiamo i parametri da passare alla funzione e ci affidiamo alle stl per la determinazione corretta del tipo ( la sintassi per gli argomenti multipli esisteva già, ma è stata migliorata, se siete curiosi leggete qui )

e per concludere passiamo alle questioni pratiche

L’ambiente utilizzato

Per l’ambiente utilizzato valgono le considerazioni fatte nel post precedente più qualcos’altro:
le funzioni lambda del C++0X sono incluse nei compilatori GNU a partire dalla 4.5 mentre la maggior parte dei sistemi linux attualmente in circolazione usa la 4.3, questa volta però non possiamo aggirare il problema installandocci  MinGW da windows, infatti sorgono altri problemi, i phread.
La libreria dei thread appena destritta usa pthread per creare e gestire i thread, e l’implementazione della libreria per windows non include pthread, quindi questa volta ci serve una vera Linux con un vero gcc ( oppure un implementazione commerciale per windows tipo questa che però non ho verificato, se qualcuno lo fa può scrivere sui commenti come funziona e se funziona e se ne vale la pena)

  • Anche lavorando con la 4.5 di default il compilatore non accetta la nuova sintassi, bisogna specificare come flag al g++ -std=c++0x
  • Inoltre bisogna aggiungere -lpthread per usare la lib dei pthread
  • se continua a non funzionare bisogna installare verificate che sia installato anche gcc-multilib
  • se ancora continua a non funzionare e vi da errori del tipo fatal error: asm/errno.h allora l’installatore non ha fatto il suo dovere e dovete correggere a mano il nome di una cartella:

andate su /usr/include/ e controllate che esista la cartella asm-generic, se esiste fatene un link software su /usr/include/asm (NON rinominatela, non si sa mai che può succedere con un aggiornamento di pacchetti ) se non esiste e non avete ancora risolto… mi dispiace ma non so che dirvi, non sono partito da un sistema vergine e quindi non ho verificato tutte le necessità del programma… scrivetemi nei commenti e cerchiamo una soluzione.

E per questo articolo è tutto, continuerò la prossima volta parlando della gestione delle risorse condivise e degli strumenti di sincronizzazione, la parte più divertente deve ancora arrivare :)
Leggi tutto »

giu
14

Una classe Java per effettuare il backup di un database Mysql (v 2.0)

Quando si sviluppa un applicazione che permette di interagire e modificare un database può essere utile prevedere una funzione che permetta di effettuare velocemente e con un solo click il backup completo dell’intero database (dati, struttura, trigger e stored procedure).

Questa classe fa esattamente questo, e a differenza dei molti snippet che si trovano sul web è completa e autosufficente e documentata con javadoc, necessita soltanto della presenza nel sistema del comando “mysqldump” (presenza che viene verificata dalla classe stessa).

La classe avvia un istanza di mysqldump e permette di salvarne l’output in dei file di testo ,in un unico file zip o eventualmente in una stringa

A differenza della versione precedente ( descritta qui )  questa versione permette di importare/esportare anche tabelle che hanno vincoli di integrità relazionale.
Per fare questo disabilita il check delle Costraint , esegue l’intera importazione all’interno di una transazione, e poi le riattiva.
Questo comportamento può essere attivato con checkConstraint=false
Il comportamento di default è identico alla versione precedente

il file può essere scaricato da QUI

vediamo un esempio di utilizzo:

package cavia;

import darshan.util.MysqlBackup;
public class Main {

 public static void main(String[] args) {
 MysqlBackup b= new MysqlBackup("127.0.0.1", "3306", "root", "root", "darshan");
 b.checkConstraint=false;
 b.all_to_zip("/home/darshan/Scrivania/post/dump.zip");
 System.out.println("Done");
 }

la classe viene istanziata con MysqlBackup(ip, porta, user, password, database) , poi con b.checkConstraint=false; disabilito il check delle costraint e con all_to_zip eseguo il dump in un file zip, per le altre funzioni affidatevi al javadoc che trovate anche qui
Leggi tutto »

Post precedenti «