Why does a SELECT statement dirty cache buffers in Postgres?
This has a simple reason. 

In PostgreSQL a row has to go through a visibility check. On the first read, the system checks if a row can be seen by everybody. If it is, it will be "frozen". This is where the writes come from. Similarly, VACUUM also sets bits. 

There is a detailed explanation: http://www.cybertec.at/speeding-up-things-with-hint-bits/.