1. Overview

Semaphores are synchronization primitives between various processes or between the various threads of a process.

Sometimes, we may need to find the processes currently using semaphores.

In this tutorial, we’ll first learn about semaphores. Then, we’ll discuss how to find the currently used semaphores or, in other words, the active semaphores. Finally, we’ll look at a couple of examples.

2. Brief Information About Semaphores

There are mainly two types of semaphores in Linux. These are the traditional System V semaphores and the newer POSIX semaphores.

The POSIX semaphores have also two types: named semaphores and memory-based (unnamed) semaphores.

A process can perform three main operations on a semaphore. The first one is the creation of a semaphore. We can specify an initial value for the semaphore during creation.

The second operation is waiting for a semaphore. The wait operation checks the semaphore’s value, waits (blocks) if the value is less than or equal to 0, and then decrements the value once it’s greater than 0.

The third operation is posting to a semaphore. The post operation increments the value of the semaphore. In this case, if there are any other threads or processes waiting the semaphore’s value to be greater than 0, the operating system awakes one of them.

3. Finding the Active Semaphores

We’ll briefly discuss how to find the active System V and POSIX named semaphores.

3.1. The ipcs and lsipc Commands

We’ll use the ipcs command to get information about the active System V semaphore sets. In fact, ipcs shows the status of all System V IPC (inter-process communication) objects if we use it without any options. These objects are shared memory, message queues, and semaphores.

We must use the -s option of ipcs to get only the active semaphore sets:

$ ipcs -s

------ Semaphore Arrays --------
keys       semid      owner      perms      nsems

There are no active semaphore sets currently, according to the output of ipcs -s.

We use the -m and -q options to get information about the active shared memory segments and the active message queues, respectively.

The newer lsipc command also gives information on currently employed IPC objects. Its usage is like ipcs. Additionally, it supports a richer set of output formats, such as JSON.

ipcs and lsipc show information only on System V IPC facilities.

3.2. The /dev/shm Directory

The /dev/shm directory exists in Linux distros as a temporary file storage system. It uses RAM as the backing store. Therefore, it offers a shared memory implementation that lets IPC.

POSIX named semaphores are identified by names that correspond to pathnames in the file system. These files are created in the /dev/shm directory. The file names are in the form of sem.name where name is the semaphore’s name.

4. Code Examples

In this section, we’ll look at some sample C code to create System V and POSIX named semaphores.

4.1. Code Example Using System V Semaphores

We’ll use the C program, sem_system_v.c, that uses System V semaphores:

#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>

#define SEM_KEY 0x12345

union semun {
    int val;
    struct semid_ds *buf;
    ushort *array;
};

int main()
{
    union semun arg;

    // Create the semaphore
    int semid = semget(SEM_KEY, 1, IPC_CREAT | 0666);

    // Initialize the semaphore
    arg.val = 1;
    semctl(semid, 0, SETVAL, arg);
  
    printf("Will sleep\n");
    sleep(60);
    printf("Woke up\n");

    // Remove the semaphore set
    semctl(semid, 0, IPC_RMID);

    return 0;
}

Let’s discuss the source code briefly.

Firstly, we create the semaphore using the int semid = semget(SEM_KEY, 1, IPC_CREAT | 0666) statement. The first argument of semget() is the identifier of the semaphore set, which is 0x12345. We pass 1 as the second argument to create only one semaphore. The IPC_CREAT flag in the third argument tells the system to create the semaphore if it doesn’t exist. On the other hand, the other flag, 0666, in the third argument specifies the permissions of the semaphore.

Then, we initialize the semaphore using the semctl(semid, 0, SETVAL, arg) statement. The first argument of semctl() is the semaphore set identifier that semget() returns. The second argument specifies the semaphore in the semaphore set. The semaphores in a set are numbered starting from 0. As we have only one semaphore in the set, we pass 0. The third argument, SETVAL, sets the semaphore’s value to the given value in the fourth argument. We specify the initial value of the semaphore in the val field of the arg variable as the fourth argument. It’s 1 in our case.

Then, the process sleeps using the statement sleep(60).

Finally, we remove the semaphore set using the semctl(semid, 0, IPC_RMID) statement. The first argument of semctl() is the semaphore set identifier. We pass 0 as the second argument to specify the semaphore in the semaphore set. The third argument, IPC_RMID, is the flag to remove the semaphore set.

Normally, we use the semop() function to decrement and increment the semaphore’s value. Therefore, we synchronize threads or processes depending on the value of the semaphore. However, it’s enough for us to create a System V semaphore as we’ll just try to identify the process using the semaphore.

4.2. Code Example Using POSIX Named Semaphores

We’ll use the C program, sem_posix_named.c, that uses POSIX named semaphores:

#include <stdio.h>
#include <unistd.h>

#include <semaphore.h>
#include <fcntl.h>

int main()
{
    char sem_name[] = "my_named_semaphore";

    // Create and open the semaphore
    sem_t *s = sem_open(sem_name, O_CREAT, S_IRUSR | S_IWUSR, 1);
  
    printf("Will sleep\n");
    sleep(60);
    printf("Woke up\n");

    // Close the semaphore
    sem_close(s);

    // Delete the semaphore
    sem_unlink(sem_name);

    return 0;
}

Let’s discuss the source code briefly.

The sem_open(sem_name, O_CREAT, S_IRUSR | S_IWUSR, 1) statement creates a new named semaphore. The first argument of sem_open(), sem_name, contains the semaphore’s name, my_named_semaphore. The second argument, O_CREAT, specifies that the named semaphore must be created if it doesn’t exist. The third argument, S_IRUSR | S_IWUSR, is for specifying the permissions of the file corresponding to the semaphore. Finally, the fourth argument is 1. It specifies the initial value of the semaphore.

The return value of sem_open() is a pointer to the created semaphore.

Then, after creating the POSIX named semaphore using sem_open(), the process sleeps using the statement sleep(60).

Finally, we call the sem_close(s) and sem_unlink(sem_name) statements for closing and deleting the named semaphore.

Normally, we use the sem_wait() and sem_post() functions to decrement and increment the semaphore’s value. Hence, we achieve the synchronization of threads or processes. However, creation of a named semaphore is enough for us to be able to find it.

5. Running the Examples

In this section, we’ll run the examples and try to identify the processes currently using semaphores.

5.1. Checking System V Semaphores

First, we’ll compile the program:

$ gcc sem_system_v.c -o sem_system_v 

We used the gcc compiler to compile sem_system_v.c. The -o flag specifies the name of the output executable file. It’s sem_system_v in our case.

Let’s first check the active semaphore sets before running the program:

$ ipcs -s

------ Semaphore Arrays --------
keys       semid      owner      perms      nsems

There are no active semaphore sets. Let’s run the program:

$ ./sem_system_v &
[1] 3918
Will sleep

We started the program in the background. The PID of the process spawned by running the program is 3918. The program is now sleeping because of the sleep(60) statement in the source code.

Let’s check the active semaphore sets again:

$ ipcs -s

------ Semaphore Arrays --------
keys       semid      owner      perms      nsems
0x00012345 14         alice      0          1

We see that there’s an active semaphore set now. The key of the semaphore set in the keys column is 0x00012345, which is the value of the first argument we passed to semget(). Additionally, the nsems column shows that there’s only one semaphore in the semaphore set because the number of semaphores we passed to semget() as the second argument was 1.

However, there’s no information about the process using the semaphore set. We can use the -i option ipcs for extra information:

$ ipcs –s –i 14

Semaphore Array semid=14
uid=1000         gid=1000        cuid=1000       cgid=1000
mode=0666, access_perms=0666
nsems = 1
otime = Not set
ctime = Thu Jan 12 14:17:59 2023
semnum     value      ncount     zcount     pid
0          1          0          0          3918

We pass the ID of the resource using the -i option. It was the semaphore ID, 14, in our example. The -i option shows the details of the specified resource.

The pid column in the output identifies the most recent process that completed a semaphore operation. The PID in the pid column, 3918, is the PID of the process we spawned by running sem_system_v program. Therefore, we succeeded in finding the process currently using the semaphore.

Using the -i option of ipcs may need root privileges.

5.2. Checking POSIX Semaphores

First, let’s compile the program:

$ gcc sem_posix_named.c -o sem_posix_named -lpthread

The name of the executable program is sem_posix_named. We also linked it with libpthread.so.

Now, it’s time to run the program:

$ sem_posix_named &
[1] 4923
Will sleep

We started the program in the background. The PID of the process spawned by running the program is 4923. The process is now sleeping because of the sleep(60) statement.

We’ll try to find the process using the semaphore while the program is running. First, let’s check the semaphore using ipcs:

$ ipcs -s

------ Semaphore Arrays --------
keys       semid      owner      perms      nsems

There are no active semaphore sets. That’s the expected behavior as ipcs doesn’t show POSIX semaphores.

Now, let’s check whether there’s any named POSIX semaphore in /dev/shm:

$ ls -l /dev/shm
total 4
-rw-------. 1 alice alice 32 Jan 13 14:17 sem.my_named_semaphore

There’s a named semaphore in /dev/shm, namely sem.my_named_semaphore. We may use the lsof command to find the process using this named semaphore:

$ lsof /dev/shm/sem.my_named_semaphore
COMMAND    PID   USER  FD   TYPE DEVICE SIZE/OFF NODE NAME
sem_posix 4923  alice DEL    REG   0,22            10 /dev/shm/sem.my_named_semaphore

There are several columns in the output of lsof. However, the column that’s of interest to us is the PID column. The PID in this column is the PID of the process we spawned by running sem_posix_named. It’s 4923, as expected.

Therefore, we successfully found the process that uses the POSIX named semaphore.

6. Conclusion

In this article, we discussed finding the processes currently using semaphores. We learned about semaphores and discussed how to find the active ones. We saw that we could use the ipcs and lsipc commands to find the active System V semaphores. On the other hand, the files corresponding to POSIX named semaphores are in the /dev/shm directory.

Then, we looked at two examples for System V and POSIX named semaphores. After finding the active semaphores in the system, we learned how to find the processes using them.