Controlling processes via the /proc file system

Implemented by the Process Manager component of procnto, the /proc virtual file system lets you access and control every process and thread running within the system.

The /proc file system manifests each process currently running on the system as a directory whose name is the numerical process ID (decimal) of the process. Inside this directory, you'll find a file called as (address space) that contains the process's entire memory space. Threads are accessible through the as file created for the process; you can select a thread via devctl() calls. You can use the following standard functions to access the /proc file system:

Function Purpose
open() Establish a file descriptor to a process
read() Read data from the process's address space
write() Write data to the process's address space
stat() Return struct stat information
lseek() Establish a position within the process's address space for further operations
devctl() Manipulate a process or thread
close() Release a file descriptor

Ancillary functions (such as readdir(), opendir(), and so on) are supported on the directory /proc itself — this aids in implementing commands such as ls .

Establishing a connection

To be able to access a process or thread, you must first use the open() call to get a valid file descriptor. You can then use this file descriptor with the function calls listed below to access the process or thread.

Open the file (/proc/ pid /as), not the /proc/ pid directory.

To read or write data from or to the process, you must have opened the file descriptor in the appropriate mode. You must also have appropriate privileges to open the particular process. By default:

  • Any process can read any other process's address space (but see the note below).
  • To write to a process's address space, your user ID and group ID must match that of the process or you must be root. Only one process can have a /proc/ pid /as file open for writing at a time.

When you start procnto , you can use the -u option to specify the umask to use for entries in /proc/ pid. The default is 0066; loosening this up can be a security problem.

Opening /proc/ pid /as for read-only access succeeds, even if the file permissions would normally say that it should fail with an EACESS. However, the kernel marks the OCB as allowing only devctl() commands. This prevents unprivileged processes from examining a process's memory, but still allows a non-root pidin to display some useful information.

When you're done accessing the process or thread, you should close() the file descriptor. Depending on what you were doing, certain actions can occur on the process or thread when you perform the close(). These actions are documented below.

Reading and writing the process's address space

The easiest operation to perform is to access the 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.

Determining the offset

The position at which transfer occurs depends on the current offset as set on the file descriptor. In virtual-address systems such as BlackBerry 10 OS, the current offset is taken to be the virtual address from the process's perspective.

For example, to read 4096 bytes at offset 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!

Determining accessibility

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.

Last modified: 2014-12-11

Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus