This article is a continuation of the Series on Linux Device Drivers and carries the discussion on Linux device drivers and their implementation. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Sending Signal from Linux Device Driver to User Space – Linux Device Driver Tutorial Part 25.
You can also read Sysfs, Procfs, Workqueue, Completion, Softirq, and threaded IRQ in the Linux device driver.
Table of Contents
Prerequisites
In the example section, we explained signals using the interrupt program. So I would recommend you to explore interrupts using the below links before starting this.
Signals
Introduction
Generally, A signal is an action that is intended to send a particular message. It can be sound, gesture, event, etc. Below are the normal signals which we are using the day to day life.
- When we take money in ATM, we will get a message (SMS).
- Calling someone by making a sound or gesture.
- Microwave oven making a sound when it finishes its job.
- etc.
What about the signal in Linux? Signals are a way of sending simple messages which are used to notify a process or thread of a particular event. In Linux, there are many processes that will be running at a time. We can send a signal from one process to another process. Signals are one of the oldest inter-process communication methods. These signals are asynchronous. Like User space signals, can we send a signal to userspace from kernel space? Yes, why not. We will see the complete Signals in upcoming tutorials. In this tutorial, we will learn how to send a signal from Linux Device Driver to User Space.
Sending Signal from Linux Device Driver to User Space
Using the following steps easily we can send the signals.
- Decide the signal that you want to send.
- Register the user space application with the driver.
- Once something happened (in our example we used interrupts) send signals to userspace.
- Unregister the user space application when you have done with it.
Decide the signal that you want to send
First, select the signal number which you want to send. In our case, we are going to send signal 44.
Example:
#define SIGETX 44
Register the user space application with the driver
Before sending the signal, your device driver should know to whom it needs to send the signal. For that, we need to register the process to the driver. So we need to send the PID to the driver first. Then that driver will use the PID and sends the signal. You can register the application PID in anyways like IOCTL, Open/read/write call. In our example, we are going to register using IOCTL.
Example:
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if (cmd == REG_CURRENT_TASK) {
printk(KERN_INFO "REG_CURRENT_TASK\n");
task = get_current();
signum = SIGETX;
}
return 0;
}
Send signals to user space
After registering the application to the driver, the driver can able to send the signal when it wants to. In our example, we will send the signal when we get the interrupt.
Example:
//Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq,void *dev_id) {
struct siginfo info;
printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
//Sending signal to app
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIGETX;
info.si_code = SI_QUEUE;
info.si_int = 1;
if (task != NULL) {
printk(KERN_INFO "Sending signal to app\n");
if(send_sig_info(SIGETX, &info, task) < 0) {
printk(KERN_INFO "Unable to send signal\n");
}
}
return IRQ_HANDLED;
}
Unregister the user space application
When you are done with your task, you can unregister your application. Here we are unregistering when that application closes the driver.
Example:
static int etx_release(struct inode *inode, struct file *file)
{
struct task_struct *ref_task = get_current();
printk(KERN_INFO "Device File Closed...!!!\n");
//delete the task
if(ref_task == task) {
task = NULL;
}
return 0;
}
Device Driver Source Code
The complete device driver code is given below. In this source code, When we read the device file (/dev/etx_device) interrupt will hit (To understand interrupts in Linux go to this tutorial). Whenever interrupt hits, I’m sending the signal to the userspace application who registered already. Since it is a tutorial post, I’m not going to do any job in the interrupt handler except sending the signal.
driver.c
[Get the source code from GitHub]
/***************************************************************************//**
* \file driver.c
*
* \details Simple Linux device driver (Signals)
*
* \author EmbeTronicX
*
* *******************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h> //kmalloc()
#include<linux/uaccess.h> //copy_to/from_user()
#include <linux/ioctl.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/err.h>
#define SIGETX 44
#define REG_CURRENT_TASK _IOW('a','a',int32_t*)
#define IRQ_NO 11
/* Signaling to Application */
static struct task_struct *task = NULL;
static int signum = 0;
int32_t value = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.unlocked_ioctl = etx_ioctl,
.release = etx_release,
};
//Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq,void *dev_id) {
struct siginfo info;
printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
//Sending signal to app
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIGETX;
info.si_code = SI_QUEUE;
info.si_int = 1;
if (task != NULL) {
printk(KERN_INFO "Sending signal to app\n");
if(send_sig_info(SIGETX, &info, task) < 0) {
printk(KERN_INFO "Unable to send signal\n");
}
}
return IRQ_HANDLED;
}
static int etx_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Opened...!!!\n");
return 0;
}
static int etx_release(struct inode *inode, struct file *file)
{
struct task_struct *ref_task = get_current();
printk(KERN_INFO "Device File Closed...!!!\n");
//delete the task
if(ref_task == task) {
task = NULL;
}
return 0;
}
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read Function\n");
asm("int $0x3B"); //Triggering Interrupt. Corresponding to irq 11
return 0;
}
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Write function\n");
return 0;
}
static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if (cmd == REG_CURRENT_TASK) {
printk(KERN_INFO "REG_CURRENT_TASK\n");
task = get_current();
signum = SIGETX;
}
return 0;
}
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
printk(KERN_INFO "Cannot allocate major number\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
printk(KERN_INFO "Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if(IS_ERR(dev_class = class_create(THIS_MODULE,"etx_class"))){
printk(KERN_INFO "Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){
printk(KERN_INFO "Cannot create the Device 1\n");
goto r_device;
}
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler))) {
printk(KERN_INFO "my_device: cannot register IRQ ");
goto irq;
}
printk(KERN_INFO "Device Driver Insert...Done!!!\n");
return 0;
irq:
free_irq(IRQ_NO,(void *)(irq_handler));
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
static void __exit etx_driver_exit(void)
{
free_irq(IRQ_NO,(void *)(irq_handler));
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple device driver - Signals");
MODULE_VERSION("1.20");
Makefile
obj-m += driver.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean
Application Source Code
This application registers with the driver using IOCTL. Once it registered, it will be waiting for the signal from the driver. If we want to close this application we need to press CTRL+C. Because it will run infinitely. We have installed a CTRL+C signal handler.
test_app.c
/***************************************************************************//**
* \file test_app.c
*
* \details Userspace application to test the Device driver
*
* \author EmbeTronicX
*
* *******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>
#define REG_CURRENT_TASK _IOW('a','a',int32_t*)
#define SIGETX 44
static int done = 0;
int check = 0;
void ctrl_c_handler(int n, siginfo_t *info, void *unused)
{
if (n == SIGINT) {
printf("\nrecieved ctrl-c\n");
done = 1;
}
}
void sig_event_handler(int n, siginfo_t *info, void *unused)
{
if (n == SIGETX) {
check = info->si_int;
printf ("Received signal from kernel : Value = %u\n", check);
}
}
int main()
{
int fd;
int32_t value, number;
struct sigaction act;
printf("*********************************\n");
printf("*******WWW.EmbeTronicX.com*******\n");
printf("*********************************\n");
/* install ctrl-c interrupt handler to cleanup at exit */
sigemptyset (&act.sa_mask);
act.sa_flags = (SA_SIGINFO | SA_RESETHAND);
act.sa_sigaction = ctrl_c_handler;
sigaction (SIGINT, &act, NULL);
/* install custom signal handler */
sigemptyset(&act.sa_mask);
act.sa_flags = (SA_SIGINFO | SA_RESTART);
act.sa_sigaction = sig_event_handler;
sigaction(SIGETX, &act, NULL);
printf("Installed signal handler for SIGETX = %d\n", SIGETX);
printf("\nOpening Driver\n");
fd = open("/dev/etx_device", O_RDWR);
if(fd < 0) {
printf("Cannot open device file...\n");
return 0;
}
printf("Registering application ...");
/* register this task with kernel for signal */
if (ioctl(fd, REG_CURRENT_TASK,(int32_t*) &number)) {
printf("Failed\n");
close(fd);
exit(1);
}
printf("Done!!!\n");
while(!done) {
printf("Waiting for signal...\n");
//blocking check
while (!done && !check);
check = 0;
}
printf("Closing Driver\n");
close(fd);
}
Building Driver and Application
- Build the driver by using Makefile (
sudo make) - Use the below line in the terminal to compile the user space application.
gcc -o test_app test_app.c
Execution (Output)
As of now, we have driver.ko and test_app. Now we will see the output.
- Load the driver using
sudo insmod driver.ko - Run the application (
sudo ./test_app)
********************************* *******WWW.EmbeTronicX.com******* ********************************* Installed signal handler for SIGETX = 44 Opening Driver Registering application ...Done!!! Waiting for signal...
- This application will be waiting for the signal
- To send the signal from driver to app, we need to trigger the interrupt by reading the driver (
sudo cat /dev/etx_device). - Now see the Dmesg (
dmesg)
Major = 246 Minor = 0 Device Driver Insert...Done!!! Device File Opened...!!! REG_CURRENT_TASK Device File Opened...!!! Read Function Shared IRQ: Interrupt Occurred Sending signal to app Device File Closed...!!!
- As per the print, the driver has sent the signal. Now check the app.
Received signal from kernel : Value = 1
- So the application also got the signal.
- Close the application by pressing CTRL+C
- Unload the module using
sudo rmmod driver
Note: If you use newer version of kernel, then you might face some issues. Please go through the below comments and fix it.
In our next tutorial, we will discuss the kernel timer in the Linux device driver. If you want real hardware interrupt, you can implement this same signaling mechanism in the GPIO interrupt Linux driver.
Please find the other Linux device driver tutorials here.
You can also read the below tutorials.

Embedded Software | Firmware | Linux Devic Driver | RTOS
Hi, I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!
Discover more from EmbeTronicX
Subscribe to get the latest posts sent to your email.

Hi,
I wanted to have some more clarity regarding the structure “siginfo” and functions such as “send_sig_info”
Have these structures and functions been discussed before? Asking this question as I don’t see any introduction to these parameters.
I am facing same issue as i told in interrupt handler chapter.
/tmp/ccXOqlDC.s:103: Error: bad instruction `int $0x3B’
please advise how i resove it ?
hi,SLR,
when i run test_app,the terminal is show “wait signal..” forever,so i need to open another terminal to run “cat ./test_app /dev/xxx?
i usually used MobaXterm connect my linux host by ssh.
thank you very much.
i find my error,forgiving “!check”,but when i use “cat /dev/xxx” ,it can not execte command.
# ./drv_test /dev/sculldev
—DRIVER TEST—
file has opend
register done
wait for signal
cat /dev/sculldev
After loading the driver, I am running the userSpace program. The signal received once and after that, I am getting a segmentation fault.
Saying that:::segfault at 24 ipb777d918 sp bfe11190 error 6 in ld-2.15.so. I am using an older kernel version and my kernel version is 3.13. And the interrupt I am using is keypress interrupt which has IRQ_NO 1.
Are you getting this error when you receive the 2nd signal? Just to isolate the issue, try without the application and see are you facing this issue in the kernel (Is ISR triggered properly?).
Without the application, I am not facing any issues. The driver is loaded and no error messages. After running the user space application the signal is sent once and I am getting a segfault. The below message is the dmesg. And I highlighted the segfault message.
[ 428.883406] Interrupt is triggered,count:82
[ 429.036766] Interrupt is triggered,count:83
[ 429.104442] Opening the file
[ 429.107849] called by userSpace
[ 432.122993] Interrupt is triggered,count:84
[ 432.123119] sending the signal to userspace
[ 432.143175] show_signal_msg: 126 callbacks suppressed
[ 432.143210] a.out[2542]: segfault at 24 ip b77d3918 sp bfec5f30 error 6 in ld-2.15.so[b77d3000+1000]
[ 432.149043] closing the file
[ 432.373662] Interrupt is triggered,count:85
[ 432.406732] Interrupt is triggered,count:86
[ 432.440123] Interrupt is triggered,count:87
hey please send us the both app and driver code to embetronicx@gmail.com. We will try to find the problem.
Hello,
We ran your code that you have mailed us. We tried in Ubuntu 14.04 and Linux kernel 3.13. But we didn’t face that issue that you are pointing.
I have just added the files that you have sent to us for other people’s reference.
SignalDriver.c
SignalUserSpaceProg.c
getting error
/home/bucky/Desktop/new_driver/driver.c: In function ‘irq_handler’:
/home/bucky/Desktop/new_driver/driver.c:65:27: error: storage size of ‘info’ isn’t known
struct kernel_siginfo info;//defined in signal.h
Hi Shubham,
You might be using the newer kernel than we have used it. You should port our old kernel changes to new kernel. We have provided our changes below.
Include this header file (
#include "linux/sched/signal.h")Then typecast the
infoargument to (struct kernel_siginfo *)Please apply this patch.
After applying this you, might face problem with interrupt too. Please refer this link to fix that issue.
Getting following error in dmesg logs
do_IRQ: 7.59 No irq handler for vector
while triggering interrupt
sudo cat /dev/etx_device
Tried following steps to get rid of this error
sudo gedit /etc/default/grub
change GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash pci=nomsi,noaer”
3.sudo update-grub
but its not working
kernel version :- 4.14.0-041400-generic
Hi,
You might be using newer kernel than we have used it. Please refer this link to fix that.
Hello,
Interesting post. Could you please share the application as well?
I think the driver code has been copied over.
Thanks,
Mihaita
Hi Mihaita,
Thanks for pointing that. Now we have copied the application code.
Thanks.