Producer consumer kernel module debugging

1. Single producer and single consumer

Suppose there is a milk manufacturer, which has one distributor and only one warehouse due to lack of funds. Milk manufacturers first produce a batch of milk and put it in a warehouse, and then notify distributors to wholesale it. After the dealer sells the milk, he calls to order the next batch of milk. The milk manufacturer starts producing the next batch of milk only after receiving the order.

The pc.c code is as follows:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <asm/atomic.h>
#include <linux/delay.h>
#include <linux/kthread.h>

#define PRODUCT_NUMS 10

//信号量定义
static struct semaphore sem_producer;
static struct semaphore sem_consumer;

static char product[12];
static atomic_t num;
static int id=1;
static int consume_num=1;

//生产者线程
static int producer(void *p)
{
	char *product=(char *)p;
	int i;
	
	atomic_inc(&num);		//给num加1
	printk("producer [%d] strat ...\n",current->pid);
	for(i=0;i<PRODUCT_NUMS;i++)			//仓库最多存10批牛奶
	{
		down(&sem_producer);			//获取信号量sem_producer,当该信号量无法获取时,它将进入睡眠状态,直到信号量可用,它才继续执行
		snprintf(product,12,"2010-01-%d",id++);		
		printk("producer [%d] produce %s\n",current->pid,product);
		up(&sem_consumer);				//释放信号量sem_consumer
	}
	
	printk("producer [%d] exit ...\n",current->pid);
	return 0;
}

//消费者线程
static int consumer(void *p)
{
	char *product=(char *)p;
	
	printk("consumer [%d] start ... \n",current->pid);
	for(;;)
	{
		msleep(100);
		down_interruptible(&sem_consumer);		//获取信号量sem_consumer,进程在等待获取信号量的时候是可以被信号打断的
		if(consume_num >= PRODUCT_NUMS*atomic_read(&num))	//PRODUCT_NUMS*atomic_read(&num) 表示生产了多少批牛奶
			break;
		printk("consumer [%d] consume %s\n",current->pid,product);
		consume_num++;
		memset(product,'\0',12);
		up(&sem_producer);					//释放信号量sem_producer
	}
	
	printk("consume [%d] exit ...\n",current->pid);
	return 0;
}

static int procon_init(void)
{
	printk(KERN_INFO"show producer and consumer\n");		//KERN_INFO 表示内核提示信息
	sema_init(&sem_producer,1);	
	sema_init(&sem_consumer,0);	
	atomic_set(&num,0);										//原子操作sum=0
	//创建相应的内核线程
	kthread_run(producer,product,"producer");
	kthread_run(consumer,product,"consumer");
	
	return 0;
}

static void procon_exit(void)
{
	printk(KERN_INFO"exit producer and consumer\n");
}

module_init(procon_init);
module_exit(procon_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("producer and consumer Module");
MODULE_ALIAS("a simplest module");

The snprintf() function is used to write formatted data into a string. Its prototype is:

/* 参数:
   str为要写入的字符串;
   n为要写入的字符的最大数目,超过n会被截断;
   format为格式化字符串,argument为其变量。
*/
int snprintf(char *str, int n, char * format [, argument, …]);

Return value: Returns the parameter str string length if successful, -1 if failed, and the error reason is stored in errno.

The memset() function is an initialization function, which sets everything in a certain memory to a specified value.

/* 参数:
   s指向要填充的内存块;
   c是要被设置的值;
   n是要被设置该值的字符数。
*/
void *memset(void *s, int c, size_t n); 

The return type is a pointer to storage area s.

The semaphore structure is as follows:

struct semaphore {
	raw_spinlock_t		lock;			//自旋锁
	unsigned int		count;			//资源数量
	struct list_head	wait_list;		//存放等待队列链表的地址
};

The prototype of the sema_inith() function is as follows:

/* 参数:
   sem为信号量;
   val也就是semaphore结构体中的count;
*/
static inline void sema_init(struct semaphore *sem, int val);

The kthread_create macro is defined as follows:

/*
 * kthread_create - 在当前节点上创建一个内核线程
 * @threadfn: 在线程中运行的函数
 * @data: threadfn()的数据指针
 * @namefmt: 线程名称的printf格式字符串
 * @arg...: namefmt的参数
 *
 * 注意:kthread_create()只是创建一个内核线程,但并没有启动
 */
#define kthread_create(threadfn, data, namefmt, arg...) \
	kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)

The kthread_run macro is defined as follows:

/**
 * kthread_run - 创建并唤醒一个线程
 * @threadfn: 运行到signal_pending(current)为止的函数
 * @data: threadfn()的数据指针
 * @namefmt: 线程名称的printf格式字符串
 *
 * 调用wake_up_process()来启动线程
 */
#define kthread_run(threadfn, data, namefmt, ...)			   \
({									   \
	struct task_struct *__k						   \
		= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
	if (!IS_ERR(__k))						   \
		wake_up_process(__k);					   \
	__k;								   \
})

The Makefile code is as follows:

obj-m += pc.o

CURRENT_PATH:=$(shell pwd)	#模块所在的当前所在路径
LINUX_KERNEL:=$(shell uname -r)	#linux内核代码的当前版本
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)	#linux内核的当前版本源码路径

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules	#编译模块
#				内核的路径		  当前目录编译完放哪   表明编译的是内核模块

clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean	#清理模块

operation result:

Insert image description here

2. Questions and Answers and Multiple Producers-Multiple Consumers

Insert image description here

(1) No, if the initialized semaphore sem_producer is a locked mutually exclusive semaphore and sem_consumer is an unlocked mutually exclusive semaphore, then in such an initialization state, the producer thread cannot produce and will enter a semaphore wait process, and the consumer thread will exit the loop at the if conditional statement due to lack of products and end the operation. This will cause the producer thread to never wait for the semaphore, leading to a "deadlock" situation.

(2) (3) (4) Idea: Since threads execute concurrently, multiple warehouses can be regarded as one warehouse, so all situations can be regarded as "multiple producers, multiple consumers, and one buffer" district"

#include <linux/init.h>
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <asm/atomic.h>
#include <linux/delay.h>
#include <linux/kthread.h>

int p=0,c=0,h=0,consume_num=0;
module_param(p,int,0);
module_param(c,int,0);
module_param(h,int,0);
module_param(consume_num,int,0);

#define PRODUCT_NUMS 10

//信号量定义
static struct semaphore sem_producer;
static struct semaphore sem_consumer;

static char product[12];
static atomic_t num;
static int id=1;

//生产者线程
static int producer(void *p)
{
	char *product=(char *)p;
	int i;
	
	if(atomic_read(&num)<p)
	{
        atomic_inc(&num);		//给num加1
        printk("producer [%d] strat ...\n",current->pid);
        for(i=0;i<PRODUCT_NUMS;i++)	
        {
            down(&sem_producer);			//获取信号量sem_producer,当该信号量无法获取时,它将进入睡眠状态,直到信号量可用,它才继续执行
            snprintf(product,12,"2010-01-%d",id++);		
            printk("producer [%d] produce %s\n",current->pid,product);
            up(&sem_consumer);				//释放信号量sem_consumer
        }

        printk("producer [%d] exit ...\n",current->pid);
        return 0;
	}
	
	return 0;
}

//消费者线程
static int consumer(void *p)
{
	char *product=(char *)p;
	
	printk("consumer [%d] start ... \n",current->pid);
	for(;;)
	{
		msleep(100);
		down_interruptible(&sem_consumer);		//获取信号量sem_consumer,进程在等待获取信号量的时候是可以被信号打断的
		if(consume_num >= PRODUCT_NUMS*atomic_read(&num))	//PRODUCT_NUMS*atomic_read(&num) 表示生产了多少批牛奶
			break;
		printk("consumer [%d] consume %s\n",current->pid,product);
		consume_num++;
		memset(product,'\0',12);
		up(&sem_producer);					//释放信号量sem_producer
	}
	
	printk("consume [%d] exit ...\n",current->pid);
	return 0;
}

static int procon_init(void)
{
	int a=1;
	int b=1;
	printk(KERN_INFO"show producer and consumer\n");		//KERN_INFO 表示内核提示信息
	sema_init(&sem_producer,p);
	if(p==c)
	{
		sema_init(&sem_consumer,0);
	}else if(p>c){
		sema_init(&sem_consumer,c);
	}else{	
		sema_init(&sem_consumer,c-p);
	}
	atomic_set(&num,0);										//原子操作sum=0
	//创建相应的内核线程
	for(a;a<=p;a++)
	{
		kthread_run(producer,product,"producer %d",a);
	}
	for(b;b<=c;b++)
	{
		kthread_run(consumer,product,"consumer %d",b);
	}
	
	return 0;
}

static void procon_exit(void)
{
	printk(KERN_INFO"exit producer and consumer\n");
}

module_init(procon_init);
module_exit(procon_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("producer and consumer Module");
MODULE_ALIAS("a simplest module");

"Multiple producers, one consumer, one buffer", execute the command sudo insmod pc.ko p=2 c=1 h=1 consume_num=1, the running result is:

Insert image description here

"Multiple producers, multiple consumers, one buffer", execute the command sudo insmod pc.ko p=2 c=2 h=1 consume_num=2, and the running result is:

Insert image description here

"One producer, one consumer, multiple buffers", execute the command sudo insmod pc.ko p=1 c=1 h=2 consume_num=1, the running result is:

Insert image description here

"Multiple producers, multiple consumers, multiple buffers", execute the command sudo insmod pc.ko p=2 c=2 h=2 consume_num=2, and the running result is:

Insert image description here

Guess you like

Origin blog.csdn.net/qq_58538265/article/details/133916726