プロデューサー・コンシューマー・カーネル・モジュールのデバッグ

1. 単一のプロデューサーと単一のコンシューマー

資金不足のため、販売店と倉庫が 1 つしかない牛乳メーカーがあるとします。牛乳メーカーはまず牛乳をバッチ生産して倉庫に保管し、それから販売業者に卸売りするように通知します。ディーラーは牛乳を販売した後、次のバッチの牛乳を注文するよう電話をかけます。牛乳メーカーは注文を受けてから次の牛乳の生産を開始します。

pc.c コードは次のとおりです。

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

snprintf() 関数は、フォーマットされたデータを文字列に書き込むために使用されます。そのプロトタイプは次のとおりです。

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

戻り値: 成功した場合はパラメーター str 文字列の長さを返し、失敗した場合は -1 を返し、エラーの理由は errno に格納されます。

memset() 関数は、特定のメモリ内のすべてを指定された値に設定する初期化関数です。

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

戻り値の型は、記憶領域へのポインタです。

セマフォの構造は次のとおりです。

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

sema_inith() 関数のプロトタイプは次のとおりです。

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

kthread_create マクロは次のように定義されます。

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

kthread_run マクロは次のように定義されます。

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

Makefile コードは次のとおりです。

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

操作結果:

ここに画像の説明を挿入します

2. 質疑応答と複数のプロデューサー - 複数のコンシューマー

ここに画像の説明を挿入します

(1) いいえ、初期化されたセマフォ sem_Producer がロックされた相互排他セマフォで、 sem_consumer がロックされていない相互排他セマフォである場合、そのような初期化状態では、プロデューサー スレッドは生成できず、セマフォ待機プロセスに入り、コンシューマー スレッドはセマフォを生成できません。製品が不足しているため、if 条件文でループを終了し、操作を終了します。これにより、プロデューサ スレッドがセマフォを待機することがなくなり、「デッドロック」状況が発生します。

(2) (3) (4) 考え方: スレッドが並行して実行されるため、複数のウェアハウスを 1 つのウェアハウスとみなすことができ、すべての状況を「複数のプロデューサー、複数のコンシューマー、および 1 つのバッファー」地区とみなすことができます。

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

「複数のプロデューサー、1 つのコンシューマー、1 つのバッファー」の場合は、コマンド sudo insmod pc.ko p=2 c=1 h=1 Consumer_num=1 を実行します。実行結果は次のようになります。

ここに画像の説明を挿入します

「複数のプロデューサー、複数のコンシューマー、1 つのバッファー」の場合、コマンド sudo insmod pc.ko p=2 c=2 h=1 Consumer_num=2 を実行すると、実行結果は次のようになります。

ここに画像の説明を挿入します

「1 つのプロデューサー、1 つのコンシューマー、複数のバッファー」の場合は、コマンド sudo insmod pc.ko p=1 c=1 h=2 Consumer_num=1 を実行します。実行結果は次のようになります。

ここに画像の説明を挿入します

「複数のプロデューサー、複数のコンシューマー、複数のバッファー」の場合は、コマンド sudo insmod pc.ko p=2 c=2 h=2 Consumer_num=2 を実行すると、実行結果は次のようになります。

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/qq_58538265/article/details/133916726