I played with kernel preemption.
Idea was to see how higher priority RT process preempts lower priority process running in kernel.
In order to get into kernel, simple module was created with read()
syscall callback:
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
static dev_t my_dev;
static struct cdev *my_cdev;
// sysfs class structure
static struct class *mychardev_class = NULL;
// callback for read() system call
static ssize_t my_read(struct file *file, char __user *buf,size_t count,loff_t *ppos)
{
int len = 20;
if(*ppos > 0)
{
return 0;
}
msleep(10000);
if (copy_to_user(buf, "hello from demo_dev ", len)) {
return -EFAULT;
} else {
*ppos +=len;
return len;
}
}
static struct file_operations my_fops =
{
.owner = THIS_MODULE,
.read = my_read,
};
static int __init hello_init (void)
{
my_dev = MKDEV(400,0);
// create sysfs class
mychardev_class = class_create(THIS_MODULE, "mychardev");
register_chrdev_region(my_dev,1,"demo");
my_cdev=cdev_alloc();
if(!my_cdev)
{
printk (KERN_ERR "cdev alloc error.\n");
return -1;
}
my_cdev->ops = &my_fops;
my_cdev->owner = THIS_MODULE;
cdev_init(my_cdev, &my_fops);
if(cdev_add(my_cdev,my_dev,1))
{
printk (KERN_ERR "cdev add error.\n");
return -1;
}
// create device node /dev/demo_dev
device_create(mychardev_class, NULL, my_dev, NULL, "demo_dev");
printk (KERN_INFO "demo_dev added.\n");
return 0;
}
static void __exit hello_exit (void)
{
device_destroy(mychardev_class, my_dev);
cdev_del(my_cdev);
class_unregister(mychardev_class);
class_destroy(mychardev_class);
unregister_chrdev_region(my_dev, 1);
printk (KERN_INFO "demo_dev removed.\n");
}
module_init (hello_init);
module_exit (hello_exit);
MODULE_LICENSE("GPL");
To test it, 2 threads were created with SCHED_FIFO
policy, one with higher priority 50 and one with lower priority 30, created 2 seconds later:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int off;
void *hi_prio(void *p)
{
printf("thread1 start time=%ld\n",time(NULL)-off);
sleep(3);
printf("thread1 stop time=%ld\n",time(NULL)-off);
return NULL;
}
void *low_prio(void *p)
{
int ret;
char buf[20];
int fd=open("/dev/demo_dev",O_RDONLY); // #mknod /dev/demo c 400 0
printf("thread2 start time=%ld\n",time(NULL)-off);
ret = read(fd,buf,20);
printf("thread2 read:%s\n");
printf("thread2 stop time=%ld\n",time(NULL)-off);
return NULL;
}
int main()
{
pthread_t t1,t2,t3;
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
param.sched_priority = 50;
pthread_attr_setschedparam(&attr, ¶m);
off = time(NULL);
pthread_create(&t1,&attr,hi_prio,NULL);
param.sched_priority = 30;
pthread_attr_setschedparam(&attr, ¶m);
sleep(2);
pthread_create(&t2,&attr,low_prio,NULL);
sleep(20);
printf("main thread stop time=%ld\n",time(NULL)-off);
puts("end test");
return 0;
}
High priority thread slept for 3 seconds and result is as expected:
thread1 start time=0
thread2 start time=2
thread1 stop time=3
thread2 read:hello from demo_dev
thread2 stop time=13
main thread stop time=22
end test
But when i changed sleepy wait msleep(10000);
with busy wait:
mdelay(5000); // busy-wait for 5 seconds
schedule();
mdelay(5000); // busy-wait for 5 seconds
High priority thread woke up after 7 seconds.
thread1 start time=0
thread2 start time=2
thread1 stop time=7
thread2 read:hello from demo_dev
thread2 stop time=12
main thread stop time=22
end test
The reason I suppose is because just voluntary preemption was configured:
CONFIG_PREEMPT_VOLUNTARY=y
#CONFIG_PREEMPT is not set
And process can't be preempted in kernel code unless it explicitly calls schedule()
or blocks(sleeps)?
I assume same results would be with other scheduling policy?
You have only voluntary preemption enabled,
CONFIG_PREEMPT is not set
, that means when kernel executedsyscall
Another higher priority process cant interrupt it on this same CPU when it becomes runable.