1. 程式人生 > >淺析結構體函式指標與核心裝置驅動

淺析結構體函式指標與核心裝置驅動

最近在公司沒什麼事做,突然有來寫寫日誌和部落格,這種突發的靈感來自於我在學習核心驅動程式碼的時候發現了結構體的一種古老的初始化方法,多虧了尚觀廣州校區的葉老師在群裡用心的給我文字講解,讓我明白了原來結構體初始化也可以用:冒號這樣的方法。


         其實在C語言中,冒號:的用法有很多,比如三目運算子   表示式?表示式1:表示式2 ;

它的意思就是如果表示式成立,那麼執行表示式1,否則執行表示式2 。還有一種就是廣泛用於結構體的位段中,例如:

Struct  node
{
         Int  id :  2 ;
         double  :  4 ;
};

大概意思就是id這個結構體成員只佔int型4個位元組中的2個位元組,這種用法在封包的時候用得最多,比如TCP,UDP,ARP,IP,HTTP等等的協議封包的時候,包有3個位元組的,有5個位元組的,可惜變數的大小隻有1 ,2 ,4, 8 。。。那解決這種方法的手段就是位段了,一個整形變數原來是佔4個位元組,現在我只要2個位元組,那我就可以用上面這種方法去取值。

         好了,今天的重點不在這,我想聊聊關於函式指標在結構體中的用法,因為它太重要了,在linux核心驅動開發中,很多框架就是對結構體中的函式指標,也就是實現那個函式,然後就對結構體中的成員賦值,也就是賦值一個函式的地址給結構體中的成員。廢話不多說,我們直接上程式碼,比較一下,看看有什麼不同。

#include <stdio.h>
#include <stdlib.h>
//定義一個字串-我名字
#define Name   "yangyuanxin"
char *p = Name;
int  add(int x , int y) ;
char ch(char a , char b) ;
struct node {
	
	int     id ;
	char   name[20];
	float   math_exam ;
	double   path ;
	//在結構體中定義一個函式指標,俗稱回撥函式
	int (*func)(int , int); 
//在結構體中定義一個函式指標,俗稱回撥函式
	char (*func1)(char , char);
};

struct node stu = {
	.id = 803 ,        //初始化id的值為803
	.name = Name ,   //初始化名字
	.math_exam = 95.2f , //初始化成績
	path:100.3f,		//注意咯,這種初始化也是可以的!
	.func = add,		//初始化函式add , 也就是讓結構體中的函式指標指向add這個函式
	.func1 = ch ,		//初始化函式ch也就是讓結構體中的函式指標指向ch這個函式
};

int main(void)
{
	//下面就不用我說了,自己分析
	printf("stu.id = %d\n",stu.id);
	printf("stu.name = %s\n",stu.name);
	printf("stu.math_exam = %.2f\n",stu.math_exam);
	printf("stu.path = %.3lf\n",stu.path);
	printf("stu.func = %d\n",stu.func(1 , 2));
	printf("stu.func1 = %c\n",stu.func1('a' , 'b'));		
	return 0 ;
} 
//函式add , 傳引數實現x和y相加
int  add(int x , int y) 
{
	return x+y ;	
}
//如果a > b , 那麼就返回a這個字元
char ch(char a , char b)
{
	if(a > b)
		return a ;
}
執行結果:

看完了註釋和程式碼,大概就明白了函式指標在結構體中的運用了吧?其實就是將函式的地址返回給函式地址,因為我們知道函式名其實就是函式的首地址,所以是不是很簡單?不過這只是簡單的舉個例子噢!來看看核心驅動是如何定義的,咱們還是直接上程式碼:

以下是我通過核心驅動去修改的一個字元裝置驅動,主要實現PWM蜂鳴器的功能,使用的平臺是基於ARM-ContexA9-tiny4412這款開發板,大家有興趣的話也可以去買一塊來玩玩。嘿嘿!!

//下面的標頭檔案是與linux核心相關的一些標頭檔案,看看就好,主要看我下面的具體分//析,這樣便於快速上手linux核心的裝置驅動。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

//定義裝置的名字為 pwm
#define DEVICE_NAME				"pwm"

#define PWM_IOCTL_SET_FREQ		1
#define PWM_IOCTL_STOP			0

#define NS_IN_1HZ				(1000000000UL)

	//蜂鳴器PWM_ID  0 
#define BUZZER_PWM_ID			0
//蜂鳴器GPIO配置
#define BUZZER_PMW_GPIO			EXYNOS4_GPD0(0)

//定義一個結構體指標
static struct pwm_device *pwm4buzzer;
//定義一個結構體訊號量指標,因為訊號量與鎖的機制差不多
//Mutex是一把鑰匙,一個人拿了就可進入一個房間,出來的時候把鑰匙交給佇列的第//一個。一般的用法是用於序列化對critical section程式碼的訪問,保證這段程式碼不會被//並行的執行。
//Semaphore是一件可以容納N人的房間,如果人不滿就可以進去,如果人滿了,就//要等待有人出來。對於N=1的情況,稱為binary semaphore。一般的用法是,用於限//制對於某一資源的同時訪問。
static struct semaphore lock;


static void pwm_set_freq(unsigned long freq) {
//PWM的佔空比的配置
	int period_ns = NS_IN_1HZ / freq;

	pwm_config(pwm4buzzer, period_ns / 2, period_ns); 
	pwm_enable(pwm4buzzer);
	//配置相應的GPIO
	s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_SFN(2));
}
//stop方法函式,來源於operations結構體
static  void pwm_stop(void) {
	s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);

	pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100);
	pwm_disable(pwm4buzzer);
}

//open方法函式,來源於operations結構體,主要開啟pwm的操作
static int tiny4412_pwm_open(struct inode *inode, struct file *file) {
	if (!down_trylock(&lock))
		return 0;
	else
		return -EBUSY;
}

//close方法函式,來源於operations結構體,主要是關閉pwm操作
static int tiny4412_pwm_close(struct inode *inode, struct file *file) {
	up(&lock);
	return 0;
}
//控制io口方法函式,來源於operations結構體,其實就是上層系統呼叫傳入一條命令,//驅動識別命令,然後執行相應過程。
static long tiny4412_pwm_ioctl(struct file *filep, unsigned int cmd,
		unsigned long arg)
{
	switch (cmd) {
		case PWM_IOCTL_SET_FREQ:
			if (arg == 0)
				return -EINVAL;
			pwm_set_freq(arg);
			break;

		case PWM_IOCTL_STOP:
		default:
			pwm_stop();
			break;
	}

	return 0;
}

//這就是我們要看的結構體了,其實這個結構體的定義在另一個.h當中,看看它的初始//化方式,跟我們上面那個程式的分析基本上是一樣的。對應的函式名(也就是函式的//首地址)賦值給對應的結構體成員,實現了整個結構體的初始化,這樣的方法類似於//C++和JAVA等高階語言的操作。
static  struct file_operations tiny4412_pwm_ops = {
	.owner			= THIS_MODULE,  			//表示本模組擁有
	.open			= tiny4412_pwm_open,		//表示呼叫open函式
	.release		= tiny4412_pwm_close,         //…
	.unlocked_ioctl	= tiny4412_pwm_ioctl,
};

//雜類裝置的註冊
static struct miscdevice tiny4412_misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &tiny4412_pwm_ops,
};
//pwm裝置初始化,裝置在被insmod插入模組到核心的過程中會呼叫這個函式
static int __init tiny4412_pwm_dev_init(void) {
	int ret;
	ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);
	if (ret) {
		printk("request GPIO %d for pwm failed\n", BUZZER_PMW_GPIO);
		return ret;
	}

	gpio_set_value(BUZZER_PMW_GPIO, 0);
	s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);

	pwm4buzzer = pwm_request(BUZZER_PWM_ID, DEVICE_NAME);
	if (IS_ERR(pwm4buzzer)) {
		printk("request pwm %d for %s failed\n", BUZZER_PWM_ID, DEVICE_NAME);
		return -ENODEV;
	}

	pwm_stop();

	sema_init(&lock, 1);
	ret = misc_register(&tiny4412_misc_dev);

	printk(DEVICE_NAME "\tinitialized\n");

	return ret;
}
//裝置在被解除安裝rmmod的過程中會呼叫這個函式
static void __exit tiny4412_pwm_dev_exit(void) {
	pwm_stop();

	misc_deregister(&tiny4412_misc_dev);
	gpio_free(BUZZER_PMW_GPIO);
}

//模組初始化
module_init(tiny4412_pwm_dev_init);
//銷燬模組
module_exit(tiny4412_pwm_dev_exit);
//宣告GPL協議
MODULE_LICENSE("GPL");
//作者:yangyuanxin
MODULE_AUTHOR("Yangyuanxin");
//描述:三星PWM裝置
MODULE_DESCRIPTION("Exynos4 PWM Driver");

	好了,程式碼我們分析完了,這就只是實現一個簡簡單單的字元裝置PWM蜂鳴器,然而,核心中還有很多更為複雜的結構體中的函式指標,要多看多練,慢慢就熟了。我們再來回憶一下核心的結構體初始化是怎樣的:
static  struct file_operations tiny4412_pwm_ops = {
	.owner			= THIS_MODULE,  			//表示本模組擁有
	.open			= tiny4412_pwm_open,		//表示呼叫open函式
	.release		= tiny4412_pwm_close,         //…
	.unlocked_ioctl	= tiny4412_pwm_ioctl,
};
上面這個程式碼初始化了PWM相關的操作函式,而這些函式以函式指標的形式定義在一個結構體裡,類似的方法就和我舉的第一個例子一樣。
struct node stu = {
	.id = 803 ,        //初始化id的值為803
	.name = Name ,   //初始化名字
	.math_exam = 95.2f , //初始化成績
	path:100.3f,		//注意咯,這種初始化也是可以的!
	.func = add,		//初始化函式add , 也就是讓結構體中的函式指標指向add這個函式
	.func1 = ch ,		//初始化函式ch也就是讓結構體中的函式指標指向ch這個函式
};
其實結構體中函式指標就是這麼回事,用途很大,在核心裝置驅動的開發中,如果能夠用好函式指標,那麼將對開發大大的有利!今天就到這裡,往後遇到問題,我還會給大家一起分享。
我的CSDN部落格: http://blog.csdn.net/morixinguan/article/month/2015/12



相關推薦

淺析結構函式指標核心裝置驅動

最近在公司沒什麼事做,突然有來寫寫日誌和部落格,這種突發的靈感來自於我在學習核心驅動程式碼的時候發現了結構體的一種古老的初始化方法,多虧了尚觀廣州校區的葉老師在群裡用心的給我文字講解,讓我明白了原來結構體初始化也可以用:冒號這樣的方法。          其實在C語言中

函式指標指標函式以及結構呼叫函式的方法

        以前忘記在哪見過見過C語言中:結構體通過指標的方式呼叫函式的方法,就一直找,找了半天,同時發現自己對函式指標和指標函式這兩個概念沒搞清楚。下面先說一下兩者的區別。再者就是說一下結構體是

C語言結構中的函式指標函式

1、函式指標 一般的函式指標可以這麼定義: int(*func)(int,int); 表示一個指向含有兩個int引數並且返回值是int形式的任何一個函式指標. 假如存在這樣的一個函式: int add2(int x,int y) { return x+y;

C結構中的函式指標函式

1. 函式指標 一般的函式指標可以這麼定義:int(*func)(int,int); 表示一個指向含有兩個int引數並且返回值是int形式的任何一個函式指標. 假如存在這樣的一個函式:int add2(int x,int y){return x+y;}那麼在實際使用指標func時可以這樣實現:func=&

指向結構指標p++p = p->next的區別

在編寫 遍歷連結串列的過程中需要讓指向結構體的指標移動到下一節點這一操作的過程中,發現了使用p++,程式不按預期執行,結果發現p++與p = p->next 的區別。 #include <stdio.h> #include <stdlib.h> typed

關於sizeof函式、memcpy函式以及結構關於指標的問題彙總

1、sizeof()函式用於獲取變數、型別等位元組數。但是不能通過結構體指標獲取結構體的位元組數。使用會出現錯誤; 比如: typede struct { unsigned char yuliu[5]; unsigned char port; }_stru

結構指標和C語言函式傳參

資料結構的基礎,是函式的傳遞和結構體的應用。 首先從函式傳參做起筆記,c語言函式傳參的本質都是傳值。我的大學教材上舉例為傳值和傳地址; 我借用百度上的一些回答作為引用: (1)傳值,就是把你的變

指標結構函式-其實可以這樣詳細理解

今天一大早登了下QQ空間,看到本科的一個學弟發表一篇日誌,寫關於西電微軟俱樂部面試題的解答,寫的很不 錯。我也一下子起興了,因為我曾經也是被指標困惑很久,搞不清頭緒,本科到研究生,我也筆試面試不下二十

結構初始化及結構指標.結構陣列.結構函式的呼叫賦值等

#include "stdio.h" #include "stdlib.h" #include "string.h" int fun(void); /************************************* int ARRSCORE[3]={133,123

C++2-------結構,輸入輸出函式

一、C語言與c++中的不同之:輸入與輸出C++中既可以使用C語言中的輸入輸出函式也可以用Cin和Cout進行輸入與輸出,不需要格式控制。# include<iostream> using namespace std; int main() { printf("h

結構巢狀結構指標

struct和typedef struct 首先是結構體的定義格式分三塊來講述:  1 首先://注意在C和C++裡不同    在C中定義一個結構體型別要用typedef:    typedef struct Student    {    int a;   

結構指標變數結構成員指標變數

C程式碼 #include <stdio.h> #include <stdlib.h> #include <string.h> struct student{    char *name;    int score;  

【C/C++開發】函式指標回撥函式

C++很多類庫都喜歡用回撥函式,MFC中的定時器,訊息機制,hook機制等待,包括現在在研究的cocos2d-x中也有很多的回撥函式。 1.回撥函式 什麼是回撥函式呢?回撥函式其實就是一個通過函式指標呼叫的函式!假如你把A函式的指標當作引數傳給B函式,然後在B函式中通過A函式傳進來的這個指標

rust學習筆記基礎篇4--結構變數宣告繫結,所有權,借用,引用(霜之小刀)

rust學習筆記基礎篇4–結構體變數宣告與繫結,所有權,借用,引用(霜之小刀) 歡迎轉載和引用,若有問題請聯絡 若有疑問,請聯絡 Email : [email protected] QQ:2279557541 關於結構體的變數宣告 看過前面

陣列指標指標陣列 函式指標指標函式

/* 陣列指標實質是一個指標,它指向陣列,如對於一個二維陣列指標,指標指向每行元素的首地址*//*#include<stdio.h> int main(){ int a[3][3]={1,2,3,4,5,6,7,8,9}; int (*Pint)[3]=&a[0]; printf("

C++函式指標 C++11 function 函式物件對比

轉自:https://blog.csdn.net/skillart/article/details/52336303 1.函式指標 函式指標:是指向函式的指標變數,在C編譯時,每一個函式都有一個入口地址,那麼這個指向這個函式的函式指標便指向這個地址。函式指標主要由以下兩方面的用途:呼叫函式和

結構函式作用及示例

一、作用     1. 提高程式碼閱讀性     2. 分類管理函式及部分屬性     3. 偏向於c++的面向物件思維 二、使用方法:     1. 宣告結構體函式    

Delphi 2010 新增功能之: IOUtils 單元(6): TPath(結構) 的方法屬性

以後路徑相關的處理, 用 IOUtils.TPath 就很方便了.   //較常用的方法: TPath.GetTempPath;                  {獲取臨時資料夾路徑} TPath.

函式指標函式物件

    今天看c++中vector資料結構的底層實現,發現遍歷操作的實現之一用到了函式物件,花時間又複習了一下函式指標和函式物件。 函式指標:是指向函式的指標變數,在C編譯時,每一個函式都有一個入口地址,那麼這個指向這個函式的函式指標便指向這個地址。 函式指標的用途是很大的

【C語言】用結構陣列指標完成:有三個學生資訊,存放在結構陣列中,要求輸出全部資訊

//用結構體陣列指標完成:有三個學生資訊,存放在結構體陣列中,要求輸出全部資訊 #include <stdio.h> struct Stu { int num; char name[2