1. Overview
Sometimes, we may want a particular process to run on a specific processor or a range of processors. Binding a process to a specific set of processors is called processor affinity or CPU pinning.
The main benefit of processor affinity is optimizing cache performance. Forcing a process to run on a specific processor helps in reducing the latency caused by cache misses.
In this tutorial, we’ll discuss how to run a process on a specific processor.
2. The taskset Command
The simplest way to bind a process to a set of specific processors is using the taskset command. We can use taskset either while spawning the process or after we spawn the process.
First, before exploring taskset, let’s check the available processors in our machine using the lscpu command:
$ lscpu | grep “On-line CPU(s) list”
On-line CPU(s) list: 0-4
So, we have five processors in our machine. We’ll refer to them as processor 0, processor 1, and so on.
2.1. Using taskset While Spawning a Process
Let’s begin exploring taskset using an example:
$ taskset –c 0 cat /dev/random >& /dev/null &
[1] 914745
We spawned a process generating random numbers using the command cat /dev/random >& /dev/null. The job id of the spawned process is 1, and its process id (PID) is 914745, as it’s apparent from the output.
We can use the –c option of taskset for specifying the processors on which the process can run. In our example, we specified –c 0 while running the process. So, we wanted the process to run on the first processor, processor 0.
We can check the current processor affinity list of the process again using taskset:
$ taskset –cp 914745
pid 914745’s current affinity list: 0
This time, in addition to the –c option, we used the –p option for specifying the PID of the process. We can see that the current affinity list of the process is 0, as we specified while spawning the process using taskset earlier.
Now, let’s check whether the process is actually running on processor 0. We can check the processor the process is bound to using the ps command:
$ ps –o pid,psr,comm –p 914745
PID PSR COMMAND
914745 0 cat
We used the –o option of ps to display several properties of the process. The –o option’s psr format specifier displays the processor that the process is currently bound to. psr stands for processor. The pid and comm format specifiers display the PID and the command name of the process, respectively. The value in the PSR column of ps is 0, so the process with PID 914745 was bound to processor 0 as expected. The process, on the other hand, is the cat command we used for generating random numbers.
2.2. Changing the Processor Affinity of a Running Process
We can also change the processor affinity of a running process using taskset.
Now, let’s bind the running process with PID 914745 to processor 2:
$ taskset –cp 2 914745
pid 914745’s current affinity list: 0
pid 914745’s new affinity list: 2
As we see, we bound the process to processor 2. The previously bound processor was processor 0.
Let’s check the new affinity list using taskset:
$ taskset –cp 914745
pid 914745’s current affinity list: 2
Now, let’s check whether the process is actually running on processor 2 using ps:
$ ps –o pid,psr,comm –p 914745
PID PSR COMMAND
914745 2 cat
So, we were successful in binding the already running process to processor 2.
The actual binding of a running process to a new processor may take a few seconds after calling taskset.
We specified only one processor up to this point. It’s also possible to specify a set of processors:
$ taskset –cp 0-1,4 914745
pid 914745’s current affinity list: 2
pid 914745’s new affinity list: 0,1,4
In this case, the arguments of the –c option, 0-1,4, specified that taskset could assign the process to one of the processors in the list, processor 0, processor 1, or processor 4.
Let’s check the new affinity list once more using taskset:
$ taskset –cp 914745
pid 914745’s current affinity list: 0,1,4
Now, let’s check to which processor taskset bound the process using ps:
$ ps –o pid,psr,comm –p 914745
PID PSR COMMAND
914745 4 cat
As we can see, taskset has bound the process to processor 4.
We used the –c option to explicitly specify the processor affinity list. We can also specify the processor affinity list using a bitmask instead of the –c option. The lowest order bit in the bitmask corresponds to the first processor, the second lowest order bit corresponds to the second processor, and so on. We must specify the bitmask as a hexadecimal number.
Let’s change the processor affinity list to include processor 0, processor 1, and processor 2. As the last three bits correspond to these processors, we must set the bitmask to 0x7:
$ taskset –p 0x7 914745
pid 914745’s current affinity list: 13
pid 914745’s new affinity list: 7
The processor affinity list was 0, 1, 4, which was 0x13 in hexadecimal before the change. Using the leading “0x” in the bitmask is optional.
3. Using the Python Programming Language
There are several scripting languages available in Linux distributions. Python is one of them. It’s possible to execute Python statements from the command line.
We can set the processor affinity of a process using Python. Let’s change the processor affinity of the already running process with PID 914745:
$ pid=914745
$ python –c “import os; os.sched_setaffinity($pid, {0,1,3})”
First, we assigned the PID of the process to a shell variable using the pid=914745 statement. Later, we changed the processor affinity of the process using the second statement starting with python -c. We can execute Python statements from the terminal using the –c option.
The first statement in the double quotes, import os, imports the os module. There are several libraries that come bundled with the core Python distribution. The os module is one of those libraries. This module lets us interact with the operating system in a portable way.
We used the sched_setaffinity() method of the os module as the second statement in the double-quotes. The sched_setaffinity() takes two arguments. The first argument is the PID of the process whose processor affinity we want to set. The second argument is a tuple of integers representing the set of processors. We used {0,1,3} as the second argument. So, the operating system can bind the process to one of the processors in the tuple.
Now, let’s check if we were able to set the processor affinity using ps:
$ ps –o pid,psr,comm –p 914745
PID PSR COMMAND
914745 3 cat
The output shows that the operating system bound the process to processor 3.
We can also check the processor affinity using the sched_getaffinity() method of the os module. It only takes a single parameter, the PID of the process:
$ pid=914745
$ python3 –c “import os; print(os.sched_getaffinity($pid))”
{0, 1, 3}
So, we were also able to check the current processor affinity of a process using Python.
4. Conclusion
In this article, we discussed two different methods to set the processor affinity of a process.
First, we used taskset and discussed how to set the processor affinity while spawning the process using taskset. Additionally, we learned how to bind an already running process to a particular set of processors using taskset. We showed that we could use ps to check which processor a process is bound to.
Then, we used the Python programming language to set and get the processor affinity of a process. We can set it using the os module’s sched_setaffinity() method and get it using the module’s sched_getaffinity() method.