Reading and writing the process's address space
If you have the appropriate permissions, you can access a process's address space. Since threads exist in the context of a process and have access to everything within a process, there's no need to consider threads in this discussion.
You can use the read(), write(), and lseek() functions to access the process's address space. The read() function transfers bytes from the current position within the process to the program issuing the read(), and write() transfers bytes from the program to the process.
0x00021000
from process ID number 2259, the following code snippet could be used:
int fd;
char buf [4096];
fd = open ("/proc/2259/as", O_RDONLY);
lseek (fd, 0x00021000, SEEK_SET);
read (fd, buf, 4096);
Of course, you should check the return values in your real code!
If a virtual address process has different chunks of memory mapped into its address space, performing a read or write on a given address may or may not work (or it may not affect the expected number of bytes). This is because the read() and write() functions affect only contiguous memory regions. If you try to read a page of memory that isn't mapped by the process, the read will fail; this is expected.