]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/posix-process/signal: Add signal descriptors and primitives
authorMichalis Pappas <michalis@unikraft.io>
Sun, 7 Jan 2024 15:50:12 +0000 (16:50 +0100)
committerUnikraft Bot <monkey@unikraft.io>
Wed, 26 Mar 2025 08:05:34 +0000 (08:05 +0000)
Introduce signal descriptors into `struct posix_process` and
`struct posix_thread`. Introduce primitives for various signal
operations such as initialization, allocation, and queueing.

Initialize signal descriptors upon creating a new process or
a thread.

Checkpatch-Ignore: LONG_LINE
Checkpatch-Ignore: MACRO_ARG_REUSE
Checkpatch-Ignore: SPACING
Signed-off-by: Michalis Pappas <michalis@unikraft.io>
Reviewed-by: Ioan-Teodor Teugea <ioan_teodor.teugea@stud.acs.upb.ro>
Reviewed-by: Sergiu Moga <sergiu@unikraft.io>
Reviewed-by: Andrei Tatar <andrei@unikraft.io>
Approved-by: Andrei Tatar <andrei@unikraft.io>
GitHub-Closes: #1248

lib/posix-process/Config.uk
lib/posix-process/Makefile.uk
lib/posix-process/process.c
lib/posix-process/process.h
lib/posix-process/signal/process.h [new file with mode: 0644]
lib/posix-process/signal/signal.c [new file with mode: 0644]
lib/posix-process/signal/signal.h [new file with mode: 0644]

index a4cd42fbb5e87cc5b9a75f245e6aa0b0403bb65e..1338177f83fc7f5562af0e5f6d0121d61759617f 100644 (file)
@@ -24,6 +24,7 @@ config LIBPOSIX_PROCESS_SIGNAL
        bool "POSIX signals (EXPERIMENTAL)"
        select LIBPOSIX_PROCESS_PIDS
        select LIBSYSCALL_SHIM
+       select LIBUKLOCK
 
 config LIBPOSIX_PROCESS_CLONE
        bool "clone() system call"
index 30821a1c8e598eb8805b36c5c407e8dca8a4ba36..a36759ca2a7ac362940bf36123cadb5a00fb5aef 100644 (file)
@@ -18,6 +18,9 @@ LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/deprecated.c
 LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/process.c
 LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/wait.c
 LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signals.c
+
+LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_SIGNAL) += $(LIBPOSIX_PROCESS_BASE)/signal/signal.c
+
 LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clone.c
 LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clonetab.ld
 LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/arch/$(CONFIG_UK_ARCH)/clone.c|$(CONFIG_UK_ARCH)
index 12a19c33cba608c13dee4c8efc11ebbfbd54a215..fec17e2c5b6aa0efb532b3e0bada2c62325c6d22 100644 (file)
 #include <uk/process.h>
 #endif /* CONFIG_LIBPOSIX_PROCESS_CLONE */
 
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+#include "signal/signal.h"
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
 #include "process.h"
 
 /**
@@ -147,11 +151,35 @@ static struct posix_thread *pprocess_create_pthread(
        pthread->process = pprocess;
        pthread->tid = tid;
        pthread->thread = th;
-       uk_list_add_tail(&pthread->thread_list_entry, &pprocess->threads);
+
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       err = pprocess_signal_tdesc_alloc(pthread);
+       if (unlikely(err)) {
+               uk_pr_err("Could not allocate signal descriptor\n");
+               /* Handle rollback manually to avoid adding more
+                * signal conditionals.
+                */
+               uk_free(a, pthread);
+               goto err_free_tid;
+       }
+       err = pprocess_signal_tdesc_init(pthread);
+       if (unlikely(err)) {
+               uk_pr_err("Could not initialize signal descriptor\n");
+               /* Handle rollback manually to avoid adding more
+                * signal conditionals.
+                */
+               pprocess_signal_tdesc_free(pthread);
+               uk_free(a, pthread);
+               goto err_free_tid;
+       }
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
 
        /* Store reference to pthread with TID */
        tid_thread[tid] = pthread;
 
+       /* Add to parent's list of threads */
+       uk_list_add_tail(&pthread->thread_list_entry, &pprocess->threads);
+
        uk_pr_debug("Process PID %d: New thread TID %d\n",
                    (int) pprocess->pid, (int) pthread->tid);
        return pthread;
@@ -171,6 +199,10 @@ static void pprocess_release_pthread(struct posix_thread *pthread)
        UK_ASSERT(pthread);
        UK_ASSERT(pthread->_a);
 
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       pprocess_signal_tdesc_free(pthread);
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
        /* remove from process' thread list */
        uk_list_del_init(&pthread->thread_list_entry);
 
@@ -223,6 +255,23 @@ int uk_posix_process_create(struct uk_alloc *a,
        UK_INIT_LIST_HEAD(&pprocess->threads);
        UK_INIT_LIST_HEAD(&pprocess->children);
 
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       ret = pprocess_signal_pdesc_alloc(pprocess);
+       if (unlikely(ret)) {
+               uk_pr_err("Could not allocate signal descriptor\n");
+               /* Free manually as we can jump to err_free_pprocess
+                * after this allocation is successful.
+                */
+               uk_free(a, pprocess);
+               goto err_out;
+       }
+       ret = pprocess_signal_pdesc_init(pprocess);
+       if (unlikely(ret)) {
+               uk_pr_err("Could not initialize signal descriptor\n");
+               goto err_free_pprocess;
+       }
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
        /* Check if we have a pthread structure already for this thread
         * or if we need to allocate one
         */
@@ -249,12 +298,21 @@ int uk_posix_process_create(struct uk_alloc *a,
                uk_list_add_tail(&(*pthread)->thread_list_entry,
                                 &pprocess->threads);
 
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+               /* Reset signal state of this thread */
+               ret = pprocess_signal_tdesc_init(*pthread);
+               if (unlikely(ret)) {
+                       uk_pr_err("Could not initialize signal descriptor\n");
+                       goto err_free_pprocess;
+               }
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
                /* Release original process if it became empty of threads */
                if (uk_list_empty(&orig_pprocess->threads))
                        pprocess_release(orig_pprocess);
        }
 
-       /* Add to process table */
+       /* Add to process table. No failure past this point. */
        if (unlikely((unsigned long)pprocess->pid >= ARRAY_SIZE(pid_process))) {
                uk_pr_err("Process limit reached, could not create new process\n");
                ret = -EAGAIN;
@@ -276,6 +334,9 @@ int uk_posix_process_create(struct uk_alloc *a,
        return 0;
 
 err_free_pprocess:
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       pprocess_signal_pdesc_free(pprocess);
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
        uk_free(a, pprocess);
 err_out:
        return ret;
@@ -311,6 +372,10 @@ static void pprocess_release(struct posix_process *pprocess)
                }
        }
 
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       pprocess_signal_pdesc_free(pprocess);
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
+
        pid_process[pprocess->pid] = NULL;
 
        uk_pr_debug("Process PID %d released\n",
index 44c2c07f8447c28e333250ba9887c33bc41f10a2..843dd779e9c8e974577cb252bf91761a9fdff5c1 100644 (file)
@@ -71,6 +71,9 @@ struct posix_process {
        struct uk_list_head child_list_entry;
        struct uk_list_head threads;
        struct uk_alloc *_a;
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       struct uk_signal_pdesc *signal;
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
 
        /* TODO: Mutex */
 };
@@ -83,6 +86,9 @@ struct posix_thread {
        struct uk_thread *thread;
        struct uk_alloc *_a;
        enum posix_thread_state state;
+#if CONFIG_LIBPOSIX_PROCESS_SIGNAL
+       struct uk_signal_tdesc *signal;
+#endif /* CONFIG_LIBPOSIX_PROCESS_SIGNAL */
 
        /* TODO: Mutex */
 };
diff --git a/lib/posix-process/signal/process.h b/lib/posix-process/signal/process.h
new file mode 100644 (file)
index 0000000..d8d38b0
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include "../process.h"
diff --git a/lib/posix-process/signal/signal.c b/lib/posix-process/signal/signal.c
new file mode 100644 (file)
index 0000000..20b6cca
--- /dev/null
@@ -0,0 +1,276 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include <uk/process.h>
+#include <uk/refcount.h>
+#include <uk/semaphore.h>
+
+#include "process.h"
+#include "sigset.h"
+#include "signal.h"
+
+/* Enqueue signal for a thread or a process
+ *
+ * Setting pthread to NULL signifies that the signal
+ * targets the process.
+ */
+static int pprocess_signal_enqueue(struct posix_process *pproc,
+                                  struct posix_thread *pthread,
+                                  struct uk_signal *sig)
+{
+       int signum;
+
+       UK_ASSERT(sig);
+       UK_ASSERT(pproc);
+
+       if (pthread)
+               UK_ASSERT(pthread->process == pproc);
+
+       if (unlikely(pproc->signal->queued_count >= pproc->signal->queued_max))
+               return -EAGAIN;
+
+       signum = sig->siginfo.si_signo;
+
+       if (pthread) {
+               /* Standard signals can be queued once */
+               if (signum < SIGRTMIN && IS_PENDING(pthread->signal->sigqueue, signum))
+                       return 0;
+
+               uk_pr_debug("Queueing signal %d for tid %d (pid %d)\n",
+                           signum, pthread->tid, pproc->pid);
+
+               uk_list_add_tail(&sig->list_head,
+                                &pthread->signal->sigqueue.list_head[signum]);
+               SET_PENDING(pproc->signal->sigqueue, signum);
+       } else {
+               /* Standard signals can be queued once */
+               if (signum < SIGRTMIN && IS_PENDING(pproc->signal->sigqueue, signum))
+                       return 0;
+
+               uk_pr_debug("Queueing signal %d for pid %d\n",
+                           signum, pproc->pid);
+
+               uk_list_add_tail(&sig->list_head,
+                                &pproc->signal->sigqueue.list_head[signum]);
+               SET_PENDING(pproc->signal->sigqueue, signum);
+       }
+
+       pproc->signal->queued_count++;
+
+       return 0;
+}
+
+struct uk_signal *pprocess_signal_dequeue(struct posix_process *pproc,
+                                         struct posix_thread *pthread,
+                                         int signum)
+{
+       struct uk_signal_queue *sigqueue;
+       struct uk_signal *sig;
+
+       UK_ASSERT(pproc);
+
+       if (pthread) {
+               UK_ASSERT(pthread->process == pproc);
+               sigqueue = &pthread->signal->sigqueue;
+       } else {
+               sigqueue = &pproc->signal->sigqueue;
+       }
+
+       sig = uk_list_first_entry_or_null(&sigqueue->list_head[signum],
+                                         struct uk_signal,
+                                         list_head);
+       if (!sig)
+               return sig;
+
+       uk_list_del(&sig->list_head);
+
+       /* Reset pending bit if the signal queue is empty */
+       if (uk_list_empty(&sigqueue->list_head[signum])) {
+               if (pthread)
+                       RESET_PENDING(pthread->signal->sigqueue, signum);
+               else
+                       RESET_PENDING(pproc->signal->sigqueue, signum);
+       }
+
+       pproc->signal->queued_count--;
+
+       return sig;
+}
+
+struct uk_signal *pprocess_signal_next_pending_t(struct posix_thread *pthread)
+{
+       struct posix_process *pproc;
+       struct uk_signal *sig;
+       int signum;
+
+       pproc = tid2pprocess(pthread->tid);
+       UK_ASSERT(pproc);
+
+       pprocess_signal_foreach(signum) {
+               if (!pprocess_signal_is_deliverable(pthread, signum))
+                       continue;
+
+               sig = pprocess_signal_dequeue(pproc, pthread, signum);
+               if (sig)
+                       return sig;
+       }
+
+       return NULL;
+}
+
+void pprocess_signal_clear_pending(struct posix_process *proc, int signum)
+{
+       struct posix_thread *thread, *threadn;
+       struct uk_signal *sig;
+
+       if (IS_PENDING(proc->signal->sigqueue, signum)) {
+               while ((sig = pprocess_signal_dequeue(proc, NULL, signum)))
+                       uk_signal_free(proc->_a, sig);
+       }
+
+       uk_pprocess_foreach_pthread(proc, thread, threadn) {
+               if (!IS_PENDING(thread->signal->sigqueue, signum))
+                       continue;
+               while ((sig = pprocess_signal_dequeue(proc, thread, signum)))
+                       uk_signal_free(thread->_a, sig);
+       }
+}
+
+void pprocess_signal_sigaction_clear(struct kern_sigaction *ks)
+{
+       ks->ks_handler = SIG_DFL;
+       ks->ks_flags = 0;
+       ks->ks_restorer = NULL;
+       uk_sigemptyset(&ks->ks_mask);
+}
+
+int pprocess_signal_sigaction_new(struct uk_alloc *alloc, struct uk_signal_pdesc *pd)
+{
+       pd->sigaction = uk_malloc(alloc, sizeof(struct uk_sigaction));
+       if (unlikely(!pd->sigaction)) {
+               uk_pr_err("Could not allocate memory\n");
+               return -ENOMEM;
+       }
+
+       pd->sigaction->alloc = alloc;
+       uk_refcount_init(&pd->sigaction->refcnt, 1);
+
+       return 0;
+}
+
+int pprocess_signal_pdesc_alloc(struct posix_process *process)
+{
+       UK_ASSERT(process);
+
+       process->signal = uk_malloc(process->_a,
+                                   sizeof(struct uk_signal_pdesc));
+       if (unlikely(!process->signal)) {
+               uk_pr_err("Could not allocate memory\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+int pprocess_signal_tdesc_alloc(struct posix_thread *thread)
+{
+       UK_ASSERT(thread);
+
+       thread->signal = uk_malloc(thread->_a, sizeof(struct uk_signal_tdesc));
+       if (unlikely(!thread->signal)) {
+               uk_pr_err("Could not allocate memory\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+int pprocess_signal_pdesc_init(struct posix_process *process)
+{
+       struct uk_signal_pdesc *pd;
+       int signum;
+       int rc;
+
+       UK_ASSERT(process);
+       UK_ASSERT(process->signal);
+
+       pd = process->signal;
+
+       pd->queued_count = 0;
+       pd->queued_max = _POSIX_SIGQUEUE_MAX;
+
+       uk_sigemptyset(&pd->sigqueue.pending);
+
+       pprocess_signal_foreach(signum)
+               UK_INIT_LIST_HEAD(&pd->sigqueue.list_head[signum]);
+
+       rc = pprocess_signal_sigaction_new(process->_a, pd);
+       if (unlikely(rc))
+               return rc;
+
+       pprocess_signal_foreach(signum)
+               pprocess_signal_sigaction_clear(KERN_SIGACTION(process, signum));
+
+       return 0;
+}
+
+int pprocess_signal_tdesc_init(struct posix_thread *thread)
+{
+       struct uk_signal_tdesc *td;
+       int signum;
+
+       UK_ASSERT(thread);
+       UK_ASSERT(thread->signal);
+
+       td = thread->signal;
+
+       uk_sigemptyset(&td->mask);
+       uk_sigemptyset(&td->sigqueue.pending);
+
+       pprocess_signal_foreach(signum)
+               UK_INIT_LIST_HEAD(&td->sigqueue.list_head[signum]);
+
+       uk_semaphore_init(&thread->signal->pending_semaphore, 0);
+       uk_semaphore_init(&thread->signal->deliver_semaphore, 0);
+
+       return 0;
+}
+
+void pprocess_signal_pdesc_free(struct posix_process *process)
+{
+       struct uk_signal *sig;
+       int signum;
+
+       UK_ASSERT(process);
+       UK_ASSERT(process->signal);
+
+       pprocess_signal_foreach(signum)
+               while ((sig = pprocess_signal_dequeue(process, NULL, signum)))
+                       uk_signal_free(process->_a, sig);
+
+       pprocess_signal_sigaction_release(process->signal->sigaction);
+
+       uk_free(process->_a, process->signal);
+}
+
+void pprocess_signal_tdesc_free(struct posix_thread *thread)
+{
+       struct posix_process *pproc;
+       struct uk_signal *sig;
+       int signum;
+
+       UK_ASSERT(thread);
+       UK_ASSERT(thread->signal);
+
+       pproc = tid2pprocess(thread->tid);
+       UK_ASSERT(pproc);
+
+       pprocess_signal_foreach(signum)
+               while ((sig = pprocess_signal_dequeue(pproc, thread, signum)))
+                       uk_signal_free(thread->_a, sig);
+
+       uk_signal_free(thread->process->_a, thread->signal);
+}
diff --git a/lib/posix-process/signal/signal.h b/lib/posix-process/signal/signal.h
new file mode 100644 (file)
index 0000000..d91ceba
--- /dev/null
@@ -0,0 +1,255 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#ifndef __UK_PROCESS_SIGNAL_H__
+#define __UK_PROCESS_SIGNAL_H__
+
+#include <limits.h>
+#include <signal.h>
+
+#include <uk/alloc.h>
+#include <uk/list.h>
+#include <uk/process.h>
+#include <uk/refcount.h>
+#include <uk/semaphore.h>
+
+#include "process.h"
+#include "sigset.h"
+
+#define SIG_ARRAY_COUNT                _NSIG /* SIGRTMAX + 1 */
+
+/* Check if signal number is valid */
+#define IS_VALID(_signum)                                              \
+       ((_signum) > 0 && (_signum) <= SIGRTMAX)
+
+/* Check whether signal number is pending in sigqueue */
+#define IS_PENDING(_sigqueue, _signum)                                 \
+       uk_sigismember(&(_sigqueue).pending, (_signum))
+
+/* Set signal number in sigqueue */
+#define SET_PENDING(_sigqueue, _signum)                                        \
+       uk_sigaddset(&(_sigqueue).pending, (_signum))
+
+/* Clear a signal from a sigqueue */
+#define RESET_PENDING(_sigqueue, _signum)                              \
+       uk_sigdelset(&(_sigqueue).pending, (_signum))
+
+/* Check if posix thread masks signal number */
+#define IS_MASKED(_t, _signum)                                         \
+       uk_sigismember(&(_t)->signal->mask, (_signum))
+
+/* Sets a posix thread's mask of the given signal number */
+#define SET_MASKED(_t, _signum)                                                \
+       uk_sigaddset(&(_t)->signal->mask, (_signum))
+
+/* Check if signal is ignored by a process. A signal is ignored if it
+ * is explicitly set to IGNORE by sigaction, or if it is set to its
+ * default action and that action is IGNORE.
+ */
+#define IS_IGNORED(_pproc, _signum)                                    \
+       ((KERN_SIGACTION(_pproc, _signum)->ks_handler == SIG_IGN) ||    \
+        (KERN_SIGACTION(_pproc, _signum)->ks_handler == SIG_DFL &&     \
+         UK_BIT(_signum) & SIGACT_IGN_MASK))
+
+/* Allocate and return `struct uk_signal` or NULL */
+#define uk_signal_alloc(_alloc)                                                \
+       uk_zalloc(_alloc, sizeof(struct uk_signal))
+
+/* Free a previously allocated `struct uk_signal` */
+#define uk_signal_free(_alloc, _sig)                                   \
+       uk_free(_alloc, _sig)
+
+/* Helper to get pointer to kern_sigaction from process,
+ * for a given signal
+ */
+#define KERN_SIGACTION(_proc, _signum)                                 \
+       ({                                                              \
+               UK_ASSERT((_proc)->signal);                             \
+               UK_ASSERT((_proc)->signal->sigaction);                  \
+               &(_proc)->signal->sigaction->ks[_signum];               \
+       })
+
+/* Helper to iterate over all signals (std + rt) */
+#define pprocess_signal_foreach(_sn)                                   \
+       for ((_sn) = 1; (__sz)(_sn) < SIG_ARRAY_COUNT; (_sn)++)
+
+/* Helper to iterate over standard signals */
+#define pprocess_stdsig_foreach(_sn)                                   \
+       for ((_sn) = 1; (__sz)(_sn) < SIGRTMIN; (_sn)++)
+
+/* Kernel version of `struct sigaction` passed to rt_sigaction(). The order
+ * of the elements of this struct is different of the `struct sigaction`
+ * passed to the sigaction() glibc wrapper.
+ */
+struct kern_sigaction {
+       void (*ks_handler)(int signo);
+       unsigned long ks_flags;
+       void (*ks_restorer)(void);
+       uk_sigset_t ks_mask;
+};
+
+struct uk_sigaction {
+       struct kern_sigaction ks[SIG_ARRAY_COUNT];
+       struct uk_alloc *alloc;
+       __atomic refcnt;
+};
+
+#if CONFIG_LIBPOSIX_PROCESS_PIDS
+
+/* Descriptor of a pending signal.
+ *
+ * Each pending signal is described as a set of
+ * information `siginfo`. This includes the signal
+ * number, sending pid, address of faulting
+ * instruction, and more. The individual fields of
+ * `siginfo_t` are described in `man siginfo_t`.
+ */
+struct uk_signal {
+       siginfo_t siginfo;
+       struct uk_list_head list_head;
+};
+
+/* Descriptor of a signal queue.
+ *
+ * Pending signals are expressed in a signal set, that
+ * provides information on which signal numbers are pending.
+ *
+ * By POSIX, standard signals are queued once regardless of
+ * the number of times the signal was issued by the sender.
+ * Real-time signals allow in-order delivery of multiple
+ * instances of a given signal.
+ *
+ * This structure maintains an list of `struct uk_signal` for
+ * each signal number. Lists of standard signals can only
+ * contain a single item.
+ */
+struct uk_signal_queue {
+       uk_sigset_t pending;
+       struct uk_list_head list_head[SIG_ARRAY_COUNT];
+};
+
+/* Signal descriptor of a posix_process.
+ *
+ * A process may queue up to `queued_max` signals,
+ * the value of which is set at init time.
+ * POSIX requires at least _POSIX_SIGQUEUE_MAX
+ * signals, while Linux allows the user to configure
+ * that value via rlimit.
+ *
+ * Signals can be targeting the process, or individual
+ * threads. Signals pending for the process are queued
+ * in `sigqueue`. Signals pending for individual
+ * threads are queued in thread-specific queues (see
+ * `struct uk_signal_tdesc`).
+ *
+ * Each signal defines a signal action, which may
+ * be the default one or a custom one specified by
+ * calling `rt_sigaction`. Signal actions are defined
+ * at the process level, i.e. are common for all
+ * threads of a given process.
+ */
+struct uk_signal_pdesc {
+       __sz queued_count;
+       __sz queued_max;
+       struct uk_signal_queue sigqueue;
+       /* We use dynamically allocated memory for sigaction as passing
+        * CLONE_SIGHAND to clone() requires that the parent and the child
+        * share the same set of handlers.
+        */
+       struct uk_sigaction *sigaction;
+};
+
+/* Signal descriptor of a posix_thread.
+ *
+ * Each thread maintains its own signal mask.
+ * That mask applies to signals targeting
+ * that thread, and also impacts the delivery
+ * of process-wide signals, as these are
+ * delivered to the first thread that does
+ * not mask that signal.
+ *
+ * Each thread also maintains its own signal
+ * queue, as besides signals targeting the
+ * process, signals can target individual
+ * threads. For details on signal queues see
+ * the description of `struct uk_signal_queue`.
+ */
+struct uk_signal_tdesc {
+       uk_sigset_t mask;
+       uk_sigset_t sigwait_set;
+       struct uk_signal_queue sigqueue;
+       struct uk_semaphore deliver_semaphore;
+       struct uk_semaphore pending_semaphore;
+};
+
+/* Allocate signal descriptor of a posix process */
+int pprocess_signal_pdesc_alloc(struct posix_process *proc);
+
+/* Allocate signal descriptor of a posix thread */
+int pprocess_signal_tdesc_alloc(struct posix_thread *thread);
+
+/* Initialize signal descriptors of a posix process */
+int pprocess_signal_pdesc_init(struct posix_process *proc);
+
+/* Initialize signal descriptors of a posix thread */
+int pprocess_signal_tdesc_init(struct posix_thread *thread);
+
+/* Free signal descritors of a posix process
+ *
+ * This function is safe to call at thread termination as
+ * we reject signals once the process exits.
+ */
+void pprocess_signal_pdesc_free(struct posix_process *process);
+
+/* Free signal descritors of a posix thread
+ *
+ * This function is safe to call at thread termination as
+ * we reject signals once the process exits.
+ */
+void pprocess_signal_tdesc_free(struct posix_thread *thread);
+
+/* Dequeue a signal from a thread or process
+ *
+ * Setting pthread to NULL signifies that the signal
+ * is queued at the process
+ */
+struct uk_signal *pprocess_signal_dequeue(struct posix_process *pproc,
+                                         struct posix_thread *pthread,
+                                         int signum);
+
+/* Allocates and, upon success, acquires a new instance of uk_sigaction.
+ * Does not clear or otherwise initialize signal dispositions.
+ */
+int pprocess_signal_sigaction_new(struct uk_alloc *alloc,
+                                 struct uk_signal_pdesc *pd);
+
+static inline void pprocess_signal_sigaction_acquire(struct uk_sigaction *sa)
+{
+       uk_refcount_acquire(&sa->refcnt);
+}
+
+static inline void pprocess_signal_sigaction_release(struct uk_sigaction *sa)
+{
+       if (uk_refcount_release(&sa->refcnt))
+               uk_free(sa->alloc, sa);
+}
+
+/* Clears the fields of an instance of struct uk_sigaction */
+void pprocess_signal_sigaction_clear(struct kern_sigaction *ks);
+
+/* Clear pending signal(s) from process. If the signum corresponds to
+ * an std-signal, it clears that an signal from pending. Otherwise,
+ * if signum corresponding an rt-signal it clears all instances from
+ * the queue.
+ */
+void pprocess_signal_clear_pending(struct posix_process *proc, int signum);
+
+/* Get next pending signal of a given thread */
+struct uk_signal *pprocess_signal_next_pending_t(struct posix_thread *pthread);
+
+#endif /* CONFIG_LIBPOSIX_PROCESS_PIDS */
+
+#endif /* __UK_PROCESS_SIGNAL_H__ */