女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux驅動開發-內核共享工作隊列

DS小龍哥-嵌入式技術 ? 2022-09-17 15:03 ? 次閱讀

【摘要】 在工作隊列里,我們把推后執行的任務叫做工作(work),描述它的數據結構為work_struct,這些工作以隊列結構組織成工作隊列(workqueue),其數據結構為workqueue_struct,而工作線程就是負責執行工作隊列中的工作。系統有默認的工作者線程,自己也可以創建自己的工作者線程。

1. 內核工作隊列

工作隊列常見的使用形式是配合中斷使用,在中斷的服務函數里無法調用會導致休眠的相關函數代碼,有了工作隊列機制以后,可以將需要執行的邏輯代碼放在工作隊列里執行,只需要在中斷服務函數里觸發即可,工作隊列是允許被重新調度、睡眠。

在工作隊列里,我們把推后執行的任務叫做工作(work),描述它的數據結構為work_struct,這些工作以隊列結構組織成工作隊列(workqueue),其數據結構為workqueue_struct,而工作線程就是負責執行工作隊列中的工作。系統有默認的工作者線程,自己也可以創建自己的工作者線程。

2. 相關函數、結構介紹

2.1 工作結構

定義文件:
Workqueue.h (linux-3.5\include\Linux)

原型:
struct work_struct {
		atomic_long_t data;    
		struct list_head entry;
		work_func_t func;      /* 工作函數指針 */
	#ifdef CONFIG_LOCKDEP
		struct lockdep_map lockdep_map;
	#endif
	};

在工作結構體里,只需要關心一個成員函數:work_func_t func;
這個成員函數是一個函數指針,指向工作函數的指針;內核使用這個結構來描述一個工作,一個工作簡單理解就是對應于一個函數,可以通過內核調度函數來調用work_struct中func指針所指向的函數。

2.2 工作函數介紹

定義文件   Workqueue.h (linux-3.5\include\linux)
函數原型   typedef void (*work_func_t)(struct work_struct *work);
功能	    這是指向工作函數地址的函數指針,編寫一個工作的函數。
參數	   struct work_struct *work,這個參數,指向struct work_struct結構變量本身。

示例:

struct work_struct work;
INIT_WORK(&work, work_func);
初始化一個work結構,work_func工作函數的參數就是指向work結構。

2.3 初始化宏

1)初始化一個work結構:
INIT_WORK(_work, _func)	     
_work: struct work_struct work結構指針。
_func:用來填充work_struct work結構的fun成員,就是工作函數指針。

2)共享工作隊列調度宏:
schedule_work(_work)
它也是一個宏,作用是調度一個工作_work。
_work:要調度工作的結構指針;
示例:
schedule_work(&work)

2.4 使用共享工作隊列的步驟

1)定義一個工作結構變量
struct work_struct work; 

2)初始化工作結構(重點func成員)。
先編寫一個工作函數:
void work_func(struct work_struct * dat)
{
   printk(“%p:”,dat);
   ……
}
初始化work:
INIT_WORK(&work, work_func);

3)在適當的地方調度工作
如果工作用于中斷底部代碼,則在中斷頂部調度。
	schedule_work(&work);
不是馬上執行,而是等待CPU空閑才執行work_func。

3. 案例代碼

3.1 共享工作隊列-按鍵驅動

下面這份代碼是在一個按鍵驅動代碼,在按鍵中斷服務函數里調度共享隊列,最終在工作函數里完成按鍵值的檢測打印。工作隊列采用的是共享工作隊列。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static struct work_struct work;

static struct m_key_info *key_info_p=NULL;

/*存放按鍵的信息*/
struct m_key_info
{
	int gpio;
	char name[50];
	int val;
	int irq;
};

struct m_key_info key_info[]=
{
	{EXYNOS4_GPX3(2),"key_irq_1",0x01},
	{EXYNOS4_GPX3(3),"key_irq_2",0x02},
	{EXYNOS4_GPX3(4),"key_irq_3",0x03},
	{EXYNOS4_GPX3(5),"key_irq_4",0x04},
};
/*
工作函數
*/
static void key_work_func(struct work_struct *work)
{
	msleep(50);
	//udelay(n);
	//mdelay(n);
	//msleep(unsigned int msecs);
	
	if(gpio_get_value(key_info_p->gpio)==0) //判斷按鍵是否按下
	{
		printk("按鍵值:%#x\n",key_info_p->val);
	}
	else
	{
		printk("按鍵值:%#x\n",key_info_p->val|0x80);
	}
}
/*
中斷服務函數
*/
static irqreturn_t key_irq_handler(int irq, void *dev)
{
    key_info_p=(struct m_key_info*)dev;

	/*調度工作----工作結構體添加到系統共享工作隊列里*/
	schedule_work(&work);
	
	return IRQ_HANDLED;
}

static int __init tiny4412_interrupt_drv_init(void)
{
    /*初始化工作*/
	INIT_WORK(&work,key_work_func);
	
	int i;
	for(i=0;i
	3.2 自定義工作隊列-按鍵驅動

工作隊列除了可以使用內核共享隊列以外,也可以自己創建隊列,下面這份代碼就演示如何自己創建隊列,并完成初始化、調用。代碼原型還是一份按鍵驅動代碼,與上面代碼相比,加了字符設備節點注冊,替換系統共享工作隊列為自定義的工作隊列。

#include 
#include 
#include  /*雜項設備相關結構體*/
#include          /*文件操作集合頭文件*/
#include     /*使用copy_to_user和copy_from_user*/
#include          /*使用IO端口映射*/
#include        
#include      /*設備*/
#include        /*標準字符設備--分配設備號*/
#include       /*ioctl操作*/
#include   /*注冊中斷相關*/
#include  		  /*中斷邊沿類型定義*/
#include  	  /*中斷IO口定義*/
#include       /*內核定時器相關*/
#include        /*等待隊列相關*/
#include       /*等待隊列相關*/
#include        /*POLL機制相關*/
#include  /*自旋鎖相關*/
#include        /*自旋鎖相關*/
#include           /*原子操作相關*/
#include          /*原子操作相關*/
#include           /*延時函數*/
#include 
#include          /*信號相關頭文件*/
#include       /*工作隊列相關*/

/*----------------------------------------------------
	創建自己的工作隊列creator_workqueue測試
-----------------------------------------------------*/

/*定義ioctl的命令*/
#define Cmd_LEDON  _IO('L',1)   //無方向    --開燈
#define Cmd_LEDOFF  _IO('L',0)   //無方向  ---關燈

/*定義設備號注冊相關*/
static dev_t keydev;            //存放設備號
static struct cdev *keyCdev;    //定義cdev結構體指針
static struct class *cls;       //定義類結構體指針

/*定義按鍵中斷相關*/
static unsigned int irq_buff[4]; /*存放中斷編號*/
static int key_value=0;          /*存放按鍵按下的鍵值*/

/*定時器相關*/
struct timer_list my_timer;

/*全局標志*/
static int poll_flag=0;

struct mutex ; /*  互斥鎖  */

/*等待隊列相關*/
static DECLARE_WAIT_QUEUE_HEAD(wait);/*初始化等待隊列頭*/
static int condition=0;            /*喚醒隊列的條件-為假休眠-為真喚醒*/

/*異步通知助手相關*/
static struct fasync_struct *myfasync; 

/*信號量*/
static DEFINE_SEMAPHORE(name_sem);

/*內核工作隊列相關結構體*/
static struct work_struct my_work;

/*延時工作隊列相關結構體*/
static struct delayed_work my_delay_work;

/*創建自己的工作隊列相關*/
struct workqueue_struct *my_work_queue;

struct Buttons_data
{
   char key_name[10]; /*按鍵的名字*/
   char key;           /*按鍵值*/
   int  GPIO;          /*GPIO口編號*/
};
/*工作隊列的處理函數*/
static void my_work_func(struct work_struct *work)
{
	static int count=0;
	printk("\n\n用戶創建的系統共享工作隊列調度成功%d 次\n\n",count++);
}

/*結構體整體賦值*/
static struct Buttons_data Key_interrupt[4]=
{
	{"key1",0x01,EXYNOS4_GPX3(2)},
	{"key2",0x02,EXYNOS4_GPX3(3)},
	{"key3",0x03,EXYNOS4_GPX3(4)},
	{"key4",0x04,EXYNOS4_GPX3(5)},
};
/*按鍵中斷服務函數*/
static irqreturn_t irq_handler_function(int irq,void * dat)
{
   struct Buttons_data *p =(struct Buttons_data *)dat; /*強制轉換*/
   
   if(!gpio_get_value(p->GPIO))
   {
		key_value=p->key;            /*獲取按下按鍵值*/
   }
   else
   {
		key_value=p->key|0x80;      /*獲取松開按鍵值*/
   }
   mod_timer(&my_timer,jiffies+1); /*修改超時時間*/
   return IRQ_HANDLED;
}
/*定時器中斷服務函數*/
static void timer_function(unsigned long data)
{
	printk("按鍵值讀取成功!!0x%x--->!\n",key_value);
    /*添加延時工作到系統工作隊列中等待執行*/
   // schedule_delayed_work(&my_delay_work,HZ*5);
	//queue_work(my_work_queue,&my_work); /*調度共享工作隊列*/
	queue_delayed_work_on(-1,my_work_queue,&my_delay_work,HZ*5);
}

static int key_open(struct inode *my_inode, struct file *my_file)
{
    unsigned char i;
	for(i=0;i<4;i++)
	{
		//獲取中斷編號
	   irq_buff[i]=gpio_to_irq(EXYNOS4_GPX3(2+i));
	   request_irq(irq_buff[i],irq_handler_function,IRQ_TYPE_EDGE_BOTH,Key_interrupt[i].key_name,&Key_interrupt[i]);
	}

	/*定時器相關*/
	my_timer.expires=0;/*1秒鐘*/
    my_timer.function=timer_function;/*定時器中斷處理函數*/
    my_timer.data=888; /*傳遞給定時器中斷服務函數的參數-用于共享定時器*/
    init_timer(&my_timer);        /*初始化定時器*/
	add_timer(&my_timer);	       /*啟動定時器*/
	
	printk("open ok !\n");
	return 0;
}

static ssize_t key_read(struct file *my_file, char __user *buf, size_t my_conut, loff_t * my_loff)
{
    int error=0;
	error=copy_to_user(buf,&key_value,my_conut); /*向應用層拷貝按鍵值*/
	key_value=0;

	if(!error)
	{
		return 0;         /*沒有讀取成功*/
	}
	else
	{
		return my_conut; /*返回成功讀取的字節數*/
	}
}

static  ssize_t key_write(struct file *my_file, const char __user *buf, size_t my_conut, loff_t *my_loff)
{
    int error;
	printk("write ok !\n");
	return 1;
}
 
static long key_unlocked_ioctl(struct file *my_file, unsigned int cmd, unsigned long argv)
{
   int dat;
  /*只有傳遞地址的時候才需要轉換-----*/
	void __user *argv1=(void __user*)argv;          //強制轉換地址
     printk("argv1=%ld\n",*(unsigned long*)argv1);	//取出數據

	 argv=(unsigned long*)argv;  /*轉為指針形式*/
    
    switch(cmd)
	{
		case Cmd_LEDON:
			dat=100;
			copy_to_user(argv,&dat,4);
			printk("LEDON_----->OK\n");
			break;
	
		case Cmd_LEDOFF:
			dat=200;
			copy_to_user(argv,&dat,4);
			printk("LEDOFF_----->OK\n");
			break;
	}
	return 0;
}

/*poll--*/
unsigned int my_poll(struct file *my_file, struct poll_table_struct * p)
{ 
    /*喚醒休眠的進程*/  
	poll_wait(my_file,&wait,p);/*添加等待隊列--不是立即休眠*/
	printk("<1>""8888\n");
	 if(condition==1)
	 {
	    printk("drive----poll ----ok!\n");
	    condition=0;   /*清除標志*/
		return POLLIN;  /*返回事件*/  
	 }
	return 0;  /*返回事件*/  
}

/*異步通知助手*/
int key_fasync(int fd, struct file *my_file,int on)  //異步通知
{
	int error;
	printk("驅動層收到的文件描述符:%d\n",fd);
	error=fasync_helper(fd,my_file,on,&myfasync);
	printk("驅動層異步通知結構體文件描述符:%d\n",myfasync->fa_fd);
	return error;
}

static int  key_release(struct inode *my_inode, struct file *my_file)
{
   int i;
	//釋放中斷
    for(i=0;i<4;i++)
   	{
   		free_irq(irq_buff[i],&Key_interrupt[i]); 
	}
	return 0;
}

/*定義一個文件操作集合結構體*/
static struct file_operations ops_key={
   .owner = THIS_MODULE,
   .read=key_read,       /*讀函數-被應用層read函數調用*/
   .write=key_write,     /*寫函數-被應用層write函數調用*/
   .open=key_open,       /*打開函數-被應用層open函數調用*/
   .release=key_release, /*釋放函數*/
   .unlocked_ioctl=key_unlocked_ioctl,  /*ioctl操作*/
   .poll=my_poll,         /*poll機制*/
   .fasync=key_fasync,    /*異步通知助手*/
};

static int __init key_init1(void)
{
  /*動態分配一個設備號*/
   alloc_chrdev_region(&keydev,0,1,"mykey"); //我們可以讀取/proc/devices文件以獲得Linux內核分配給設備的主設備號和設備名字
  /*動態分配cdev結構體,返個cdev結構;如果執行失敗,將返回NULL。*/
   keyCdev = cdev_alloc();
  /*初始化Cdev結構體*/
   cdev_init(keyCdev,&ops_key);
  /*注冊Cdev結構體*/
   cdev_add(keyCdev,keydev,1);
  /*創建類*/
   cls=class_create(THIS_MODULE,"my_key");
   /*在類下面創建設備*/
    device_create(cls,NULL,keydev,NULL,"my_delaywork");//  /dev/

   /*創建自己的工作隊列*/
	my_work_queue =create_workqueue("my_workqueue");
	/*初始化延時工作隊列*/
	 INIT_DELAYED_WORK(&my_delay_work,my_work_func);

    /*初始化無延時的工作隊列*/
	// INIT_WORK(&my_work,my_work_func);
	
	 printk("<1>""key drive init OK!!-->__FILE__=%s  __LINE__=%d\n",__FILE__,__LINE__);
	return 0;
}

//KERN_EMERG
static void __exit key_exit(void)
{
	device_destroy(cls,keydev); 		   //注銷設備節點	
	class_destroy(cls);				   //注銷分配的類	
	cdev_del(keyCdev);					   //注銷CDEV結構體	
	unregister_chrdev_region(keydev,1); //注銷設備
	kfree(keyCdev);                      //釋放結構體
    printk("<1>""key drive exit OK!! -->__FILE__=%s  __LINE__=%d\n",__FILE__,__LINE__);
}
//EXPORT_SYMBOL(key_init);
module_init(key_init1);  /*驅動入口*/
module_exit(key_exit);  /*驅動出口*/
MODULE_LICENSE("GPL");
(key_info)>(key_info)/sizeof(key_info[0]);i++)>
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 嵌入式
    +關注

    關注

    5140

    文章

    19524

    瀏覽量

    314748
  • 內核
    +關注

    關注

    3

    文章

    1408

    瀏覽量

    41083
  • 函數
    +關注

    關注

    3

    文章

    4367

    瀏覽量

    64155
收藏 人收藏

    評論

    相關推薦
    熱點推薦

    北京迅為RK3568開發板OpenHarmony系統南向驅動開發內核HDF驅動框架架構

    北京迅為RK3568開發板OpenHarmony系統南向驅動開發內核HDF驅動框架架構
    的頭像 發表于 03-11 14:13 ?977次閱讀
    北京迅為RK3568<b class='flag-5'>開發</b>板OpenHarmony系統南向<b class='flag-5'>驅動</b><b class='flag-5'>開發</b><b class='flag-5'>內核</b>HDF<b class='flag-5'>驅動</b>框架架構

    迅為RK3568開發驅動指南Linux中通用SPI設備驅動

    迅為RK3568開發驅動指南Linux中通用SPI設備驅動
    的頭像 發表于 01-23 11:02 ?2483次閱讀
    迅為RK3568<b class='flag-5'>開發</b>板<b class='flag-5'>驅動</b>指南<b class='flag-5'>Linux</b>中通用SPI設備<b class='flag-5'>驅動</b>

    嵌入式學習-飛凌嵌入式ElfBoard ELF 1板卡-Linux內核移植之內核簡介

    所以每個模塊都有對應的維護人員。維護人員的工作就是審核人們提交的代碼是否正確,如果沒有問題,就會合并到主分支上。這樣就會使linux內核不斷完善和更新。接下來就是芯片原廠例如恩智浦,開發
    發表于 12-16 13:08

    飛凌嵌入式ElfBoard ELF 1板卡-Linux內核移植之內核簡介

    所以每個模塊都有對應的維護人員。維護人員的工作就是審核人們提交的代碼是否正確,如果沒有問題,就會合并到主分支上。這樣就會使linux內核不斷完善和更新。接下來就是芯片原廠例如恩智浦,開發
    發表于 12-13 09:03

    deepin社區亮相第19屆中國Linux內核開發者大會

    中國 Linux 內核開發者大會,作為中國 Linux 內核領域最具影響力的峰會之一,一直以來都備受矚目。
    的頭像 發表于 10-29 16:35 ?828次閱讀

    linux內核中通用HID觸摸驅動

    linux內核中,為HID觸摸面板實現了一個通用的驅動程序,位于/drivers/hid/hid-multitouch.c文件中。hid觸摸驅動是以struct hid_driver
    的頭像 發表于 10-29 10:55 ?1975次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內核</b>中通用HID觸摸<b class='flag-5'>驅動</b>

    迅為iTOP-RK3568開發驅動開發指南-第十八篇 PWM

    實驗 第43章 特殊的軟中斷tasklet分析實驗 第44章 共享工作隊列實驗 第45章 自定義工作隊列實驗 第46章 延遲工作實驗 第47章 工作
    發表于 10-29 10:13

    詳解linux內核的uevent機制

    linux內核中,uevent機制是一種內核和用戶空間通信的機制,用于通知用戶空間應用程序各種硬件更改或其他事件,比如插入或移除硬件設備(如USB驅動器或網絡接口)。uevent表示
    的頭像 發表于 09-29 17:01 ?1660次閱讀

    linux驅動程序如何加載進內核

    Linux系統中,驅動程序是內核與硬件設備之間的橋梁。它們允許內核與硬件設備進行通信,從而實現對硬件設備的控制和管理。 驅動程序的編寫
    的頭像 發表于 08-30 15:02 ?949次閱讀

    linux驅動程序的編譯方法是什么

    Linux驅動程序的編譯方法主要包括兩種: 與內核一起編譯 和 編譯成獨立的內核模塊 。以下是對這兩種方法的介紹: 一、與內核一起編譯 與
    的頭像 發表于 08-30 14:46 ?1098次閱讀

    linux驅動程序的編譯方法有哪兩種

    Collection)或其他C/C++編譯器來編譯源代碼文件。這種方法較為原始,需要開發者手動指定編譯器選項、包含路徑、庫文件等。然而,在Linux驅動開發中,由于
    的頭像 發表于 08-30 14:39 ?1353次閱讀

    linux驅動程序運行在什么空間

    Linux 驅動程序是操作系統的一部分,負責管理硬件設備與操作系統之間的交互。驅動程序運行在內核空間(Kernel Space),這是操作系統的核心部分,與用戶空間(User Spac
    的頭像 發表于 08-30 14:37 ?788次閱讀

    Linux 驅動開發與應用開發,你知道多少?

    一、Linux驅動開發與應用開發的區別開發層次不同:Linux
    的頭像 發表于 08-30 12:16 ?1393次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>驅動</b><b class='flag-5'>開發</b>與應用<b class='flag-5'>開發</b>,你知道多少?

    Linux內核測試技術

    Linux 內核Linux操作系統的核心部分,負責管理硬件資源和提供系統調用接口。隨著 Linux 內核的不斷發展和更新,其復雜性和代碼規
    的頭像 發表于 08-13 13:42 ?901次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內核</b>測試技術

    歡創播報 華為宣布鴻蒙內核已超越Linux內核

    1 華為宣布鴻蒙內核已超越Linux內核 ? 6月21日,在華為開發者大會上, HarmonyOS NEXT(鴻蒙NEXT)——真正獨立于安卓和iOS的鴻蒙操作系統,正式登場。這是Ha
    的頭像 發表于 06-27 11:30 ?1150次閱讀