Debuggen des Producer-Consumer-Kernelmoduls

1. Einzelproduzent und Einzelverbraucher

Angenommen, es gibt einen Milchhersteller, der aus Geldmangel nur einen Händler und nur ein Lager hat. Milchhersteller produzieren zunächst eine Charge Milch, lagern sie in einem Lager und benachrichtigen dann die Händler, um sie im Großhandel zu verkaufen. Nachdem der Händler die Milch verkauft hat, ruft er an, um die nächste Charge Milch zu bestellen. Erst nach Eingang der Bestellung beginnt der Milchhersteller mit der Produktion der nächsten Milchcharge.

Der pc.c-Code lautet wie folgt:

#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");

Die Funktion snprintf() dient zum Schreiben formatierter Daten in einen String. Ihr Prototyp ist:

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

Rückgabewert: Gibt bei Erfolg den Parameter str string length zurück, bei Fehlschlag -1 und der Fehlergrund wird in errno gespeichert.

Die Funktion memset() ist eine Initialisierungsfunktion, die alles in einem bestimmten Speicher auf einen angegebenen Wert setzt.

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

Der Rückgabetyp ist ein Zeiger auf den Speicherbereich s.

Die Semaphorstruktur ist wie folgt:

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

Der Prototyp der Funktion sema_inith() lautet wie folgt:

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

Das Makro kthread_create ist wie folgt definiert:

/*
 * 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)

Das Makro kthread_run ist wie folgt definiert:

/**
 * 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;								   \
})

Der Makefile-Code lautet wie folgt:

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	#清理模块

Operationsergebnis:

Fügen Sie hier eine Bildbeschreibung ein

2. Fragen und Antworten und mehrere Produzenten – mehrere Verbraucher

Fügen Sie hier eine Bildbeschreibung ein

(1) Nein, wenn das initialisierte Semaphor sem_producer ein gesperrtes, sich gegenseitig ausschließendes Semaphor und sem_consumer ein entsperrtes, sich gegenseitig ausschließendes Semaphor ist, kann der Produzenten-Thread in einem solchen Initialisierungszustand nicht produzieren und tritt in einen Semaphor-Warteprozess ein, und der Verbraucher-Thread wird dies tun Verlassen Sie die Schleife bei der if-Bedingungsanweisung aufgrund fehlender Produkte und beenden Sie den Vorgang. Dies führt dazu, dass der Produzenten-Thread nie auf das Semaphor wartet, was zu einer „Deadlock“-Situation führt.

(2) (3) (4) Idee: Da Threads gleichzeitig ausgeführt werden, können mehrere Lager als ein Lager betrachtet werden, sodass alle Situationen als „mehrere Produzenten, mehrere Verbraucher und ein Puffer“-Bezirk betrachtet werden können.

#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");

„Mehrere Produzenten, ein Konsument, ein Puffer“, führen Sie den Befehl sudo insmod pc.ko p=2 c=1 h=1 Consumer_num=1 aus, das laufende Ergebnis ist:

Fügen Sie hier eine Bildbeschreibung ein

„Mehrere Produzenten, mehrere Konsumenten, ein Puffer“, führen Sie den Befehl sudo insmod pc.ko p=2 c=2 h=1 Consumer_num=2 aus und das laufende Ergebnis ist:

Fügen Sie hier eine Bildbeschreibung ein

„Ein Produzent, ein Verbraucher, mehrere Puffer“, führen Sie den Befehl sudo insmod pc.ko p=1 c=1 h=2 Consumer_num=1 aus. Das laufende Ergebnis ist:

Fügen Sie hier eine Bildbeschreibung ein

„Mehrere Produzenten, mehrere Konsumenten, mehrere Puffer“, führen Sie den Befehl sudo insmod pc.ko p=2 c=2 h=2 Consumer_num=2 aus und das laufende Ergebnis ist:

Fügen Sie hier eine Bildbeschreibung ein

Supongo que te gusta

Origin blog.csdn.net/qq_58538265/article/details/133916726
Recomendado
Clasificación