中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

建設(shè)一下網(wǎng)站要求提供源碼百度查詢網(wǎng)

建設(shè)一下網(wǎng)站要求提供源碼,百度查詢網(wǎng),wordpress大家都用什么主題,ppt公司簡介模板文章目錄1 - 字符設(shè)備介紹2 - 字符設(shè)備開發(fā)流程圖3 - 字符設(shè)備開發(fā)流程具體講解(1)設(shè)備編號的定義與申請【1】Linux主次設(shè)備號介紹【2】分配設(shè)備編號【3】釋放主次設(shè)備號(2)定義file_operations結(jié)構(gòu)體-初始化接口函數(shù)(…

文章目錄

    • 1 - 字符設(shè)備介紹
    • 2 - 字符設(shè)備開發(fā)流程圖
    • 3 - 字符設(shè)備開發(fā)流程具體講解
      • (1)設(shè)備編號的定義與申請
        • 【1】Linux主次設(shè)備號介紹
        • 【2】分配設(shè)備編號
        • 【3】釋放主次設(shè)備號
      • (2)定義file_operations結(jié)構(gòu)體-初始化接口函數(shù)
      • (3)分配cdev結(jié)構(gòu)體與注銷
      • (4)綁定主次設(shè)備號,fops到cdev中,注冊cdev給Linux內(nèi)核
      • (5)創(chuàng)建設(shè)備類型、注冊設(shè)備節(jié)點
        • 【1】創(chuàng)建
        • 【2】注銷
    • 4 - 字符設(shè)備開發(fā)與測試
      • (1)驅(qū)動源碼與測試源碼
      • (1)x86架構(gòu)虛擬機上運行
      • (2)arm架構(gòu)開發(fā)板上運行
      • (4)copy_to/from_user()函數(shù)
    • 5 - inode與file結(jié)構(gòu)體
      • (1)inode結(jié)構(gòu)體
      • (2)file結(jié)構(gòu)體


首先我們介紹一下什么是字符設(shè)備,然后講解一下字符設(shè)備開發(fā)的具體的流程,分別詳細介紹每一個流程中涉及到的結(jié)構(gòu)體以及知識點,最后我們編寫代碼實現(xiàn)字符設(shè)備的開發(fā)以及測試。


1 - 字符設(shè)備介紹

Linux內(nèi)核設(shè)計哲學(xué)是把所有的東西都抽象成文件進行訪問,這樣對設(shè)備的訪問都是通過文件I/O來進行
操作。Linux內(nèi)核將設(shè)備按照訪問特性分為三類:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備。

在這里插入圖片描述

字符設(shè)備對數(shù)據(jù)的處理按照字節(jié)流的形式進行的。典型的字符設(shè)備:串口、鍵盤、觸摸屏、攝像頭、I2C、SPI、聲卡等;應(yīng)用程序能夠使用系統(tǒng)IO函數(shù)open、write、read、lseek、close…來就行訪問。

如下圖:應(yīng)用程序運行在用戶空間,而Linux驅(qū)動屬于內(nèi)核一部分,因此驅(qū)動運行于內(nèi)核空間,當(dāng)用戶想要實現(xiàn)對內(nèi)核操作時,必須使用系統(tǒng)調(diào)用來實現(xiàn)從用戶空間到內(nèi)核空間的操作。

在這里插入圖片描述


2 - 字符設(shè)備開發(fā)流程圖

在這里插入圖片描述


3 - 字符設(shè)備開發(fā)流程具體講解

(1)設(shè)備編號的定義與申請

【1】Linux主次設(shè)備號介紹

字符設(shè)備通過文件系統(tǒng)中的設(shè)備名來存取,慣例上它們位于/dev目錄。

wangdengtao@wangdengtao-virtual-machine:~$ ls -l /dev/
總用量 0
crw-------  1 root        root     10, 124  317 18:48 cpu_dma_latency
crw-------  1 root        root     10, 203  317 18:48 cuse
...

我們可以看見上面的兩個設(shè)備,首先最前面的‘c’表示這是一個字符(character)設(shè)備;我們可以看見第二個root后面的數(shù)字,這些數(shù)字是給特殊設(shè)備的主次設(shè)備編號。10,就是主設(shè)備號,后面的124和203就是次設(shè)備號。如果我們想要對相關(guān)設(shè)備進行操作,只需要對設(shè)備文件進行讀或者寫操作就可以了。

傳統(tǒng)上,主編號標(biāo)識設(shè)備相連的驅(qū)動;次設(shè)備號被內(nèi)核來決定應(yīng)用哪個設(shè)備。

dev_t 是一個32位的數(shù)據(jù)類型,其中高 12 位為主設(shè)備號,低 20 位為次設(shè)備號。在編碼時,我們不應(yīng)該管哪些位是主設(shè)備號,哪些位是次設(shè)備號。而是應(yīng)當(dāng)利用在<linux/kdev_t.h>中的一套宏定義來獲取一個dev_t的主、次編號:

wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/include/linux$ cat kdev_t.h 
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_KDEV_T_H
#define _LINUX_KDEV_T_H#include <uapi/linux/kdev_t.h>#define MINORBITS	20
#define MINORMASK	((1U << MINORBITS) - 1)#define MAJOR(dev)	((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)	((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)	(((ma) << MINORBITS) | (mi))
...

MINORBITS 表示次設(shè)備號位數(shù),一共是 20 位。
MINORMASK 表示次設(shè)備號掩碼。
MAJOR 用于從 dev_t 中獲取主設(shè)備號,將 dev_t 右移 20 位即可。
MINOR 用于從 dev_t 中獲取次設(shè)備號,取 dev_t 的低 20 位的值即可。
MKDEV 用于將給定的主設(shè)備號和次設(shè)備號的值組合成 dev_t 類型的設(shè)備號。

【2】分配設(shè)備編號

靜態(tài)分配設(shè)備號

int register_chrdev_region(dev_t first, unsigned int count, char *name);
  • first:要分配的起始設(shè)備號。first的次編號部分通常是從0開始,但不是強制的(first = MKDEV(10, 0);)
  • count:請求分配的設(shè)備號的總數(shù)。注意,如果count太大,你要求的范圍可能溢出到下一次編號;但是只要你要求的編號范圍可用,一切都任然會正確工作。
  • name:設(shè)備名字。

動態(tài)申請設(shè)備號

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const
char *name)
  • dev:只是一個輸出參數(shù),保存申請到的設(shè)備號。
  • baseminor:次設(shè)備號,它常常是0;
  • count:要申請的設(shè)備號數(shù)量。
  • name:設(shè)備名字。

動態(tài)分配的缺點是你無法提前創(chuàng)建設(shè)備節(jié)點,因為分配給你的主設(shè)備號會發(fā)生變化。我們申請到了設(shè)備節(jié)點之后,可以用前面講到的宏定義 MAJOR() 來獲取主設(shè)備號。

【3】釋放主次設(shè)備號

void unregister_chrdev_region(dev_t from, unsigned count)
  • from:要釋放的設(shè)備號。
  • count:表示從 from 開始,要釋放的設(shè)備號數(shù)量。

(2)定義file_operations結(jié)構(gòu)體-初始化接口函數(shù)

file_operations 就是把系統(tǒng)調(diào)用和驅(qū)動函數(shù)關(guān)聯(lián)起來的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)。這個結(jié)構(gòu)的每一個成員都對應(yīng)著一個系統(tǒng)調(diào)用,相應(yīng)的系統(tǒng)調(diào)用將讀取 file_operations 中相應(yīng)的函數(shù)指針,接著把控制權(quán)轉(zhuǎn)交給函數(shù),從而完成了Linux設(shè)備驅(qū)動程序工作。在系統(tǒng)內(nèi)部,I/O設(shè)備的存取操作通過特定的入口點來進行,而這組特定的入口點恰恰是由設(shè)備驅(qū)動提供的。通常這組設(shè)備驅(qū)動程序接口是由結(jié)構(gòu) file_operations結(jié)構(gòu)體向系統(tǒng)說明的,它定義在include/linux/fs.h中。傳統(tǒng)上,一個 file_operations 結(jié)構(gòu)或者其一個指針稱為fops(或者它的一些變體),結(jié)構(gòu)中的每個成員必須指向驅(qū)動中的函數(shù),這些函數(shù)實現(xiàn)一個特別的操作,或者對于不支持的操作留置為NULL。當(dāng)指定為NULL指針時內(nèi)核的確切的行為是每個函數(shù)不同的。

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);int (*iopoll)(struct kiocb *kiocb, bool spin);int (*iterate) (struct file *, struct dir_context *);int (*iterate_shared) (struct file *, struct dir_context *);__poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);
...

我就拿后面我們要寫的代碼為例講解一下,我們在file_operations中將open系統(tǒng)調(diào)用函數(shù)指向了chrtest_drv_open這個函數(shù),open系統(tǒng)調(diào)用就會把控制權(quán)轉(zhuǎn)交給這個函數(shù),完成驅(qū)動函數(shù)與系統(tǒng)調(diào)用函數(shù)的轉(zhuǎn)換。

static int chrtest_drv_open (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
/* 定義自己的file_operations結(jié)構(gòu)體*/
static struct file_operations chrtest_fops = {.owner = THIS_MODULE,.open = chrtest_drv_open,
};

(3)分配cdev結(jié)構(gòu)體與注銷

內(nèi)核在內(nèi)部使用類型struct cdev的結(jié)構(gòu)體來代表字符設(shè)備。在內(nèi)核調(diào)用你的設(shè)備操作之前,你必須分配一個這樣的結(jié)構(gòu)體并注冊給linux內(nèi)核,在這個結(jié)構(gòu)體里有對于這個設(shè)備進行操作的函數(shù),具體定義在file_operations結(jié)構(gòu)體中。該結(jié)構(gòu)體定義在include/linux/cdev.h文件中。

struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;
};

獲取cdev:struct cdev *cdev_alloc(void)

注銷cdev:

void cdev_del(struct cdev *p)

(4)綁定主次設(shè)備號,fops到cdev中,注冊cdev給Linux內(nèi)核

在分配到cdev結(jié)構(gòu)體后,接下來我們將它初始化,并將對該設(shè)備驅(qū)動所支持的系統(tǒng)調(diào)用函數(shù)存放在
file_operations結(jié)構(gòu)體添加進來,然后我們通過cdev_add函數(shù)將他們注冊給Linux內(nèi)核,這樣完成整個
Linux設(shè)備的注冊過程。
初始化設(shè)備:cdev_init(struct cdev *dev, struct file_operations *fops);
cdev_add的函數(shù)原型如下:

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
  • dev是cdev結(jié)構(gòu)。
  • num是這個設(shè)備相應(yīng)的第一個設(shè)備號。
  • count是應(yīng)當(dāng)關(guān)聯(lián)到設(shè)備的設(shè)備號的數(shù)目。

例子:

static struct file_operations chrtest_fods ={.owner = THIS_MODULE,.open = chrtest_open,
};chrtest_cdev = cdev_alloc()/*獲取cdev*/chrtest_cdev->owner = THIS_MODULE; /*.owner這表示誰擁有你這個驅(qū)動程序*/cdev_init(chrtest_cdev, &chrtest_fops); /*將fops到cdev中*/result = cdev_add(chrtest_cdev, devno, 1); /*將字符設(shè)備注冊進內(nèi)核*/if(0 != result){printk(KERN_INFO " %s driver can't register cdev:result=%d\n", DEV_NAME,result);}

(5)創(chuàng)建設(shè)備類型、注冊設(shè)備節(jié)點

【1】創(chuàng)建

手動創(chuàng)建設(shè)備節(jié)點

輸入如下命令創(chuàng)建/dev/chardev 這個設(shè)備節(jié)點文件:

mknod /dev/chardev c 10 0

在/dev路徑下創(chuàng)建一個名字為chardev的字符設(shè)備節(jié)點,主設(shè)備號為10,次設(shè)備號為0。

自動創(chuàng)建設(shè)備節(jié)點

class_create()//創(chuàng)建設(shè)備類型,類這個概念在Linux中被抽象成一種設(shè)備的集合(/sys/class/目錄下)
device_create()//注冊設(shè)備節(jié)點(/dev/目錄下)

class_create()這個函數(shù)使用非常簡單,在內(nèi)核中是一個宏定義。/include/linux/device.h中:

#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
  • owner:struct module結(jié)構(gòu)體類型的指針,一般賦值為THIS_MODULE。
  • name:char類型的指針,類名。

device_create()用于創(chuàng)建設(shè)備:

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
  • class:該設(shè)備依附的類。
  • parent:父設(shè)備。
  • devt:設(shè)備號(此處的設(shè)備號為主次設(shè)備號)。
  • drvdata:私有數(shù)據(jù)。
  • fmt:設(shè)備名。

創(chuàng)建例子:

	/*自動創(chuàng)建設(shè)備類型、/dev設(shè)備節(jié)點*/chrdev_class = class_create(THIS_MODULE, DEV_NAME); /*創(chuàng)建設(shè)備類型sys/class/chrdev*/if (IS_ERR(chrdev_class)) {result = PTR_ERR(chrdev_class);goto ERROR;}device_create(chrdev_class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME); /*/dev/chrdev 注冊這個設(shè)備節(jié)點*/

【2】注銷

注銷設(shè)備類型:

void class_destroy(struct class *cls)

注銷設(shè)備節(jié)點:

device_destroy()

注銷例子:

device_destroy(chrdev_class, MKDEV(dev_major, 0)); /*注銷這個設(shè)備節(jié)點*/
class_destroy(chrdev_class); /*刪除這個設(shè)備類型*/

4 - 字符設(shè)備開發(fā)與測試

(1)驅(qū)動源碼與測試源碼

字符設(shè)備驅(qū)動開發(fā)源碼:

/*************************************************************************> File Name: char_dev.c> Author: WangDengtao> Mail: 1799055460@qq.com > Created Time: 2023年03月16日 星期四 16時40分29秒************************************************************************/#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/fs.h>/*如果沒有定義DEV_MAJOR就設(shè)置設(shè)備號為0,采用動態(tài)申請,如果有則使用宏定義的設(shè)備號*/
//#define DEV_MAJOR 88
#ifndef DEV_MAJOR
#define DEV_MAJOR 0
#endif#define DEV_NAME  "chardev"       /*宏定義設(shè)備的名字*/
#define MIN(a,b)  (a < b ? a : b)int dev_major = DEV_MAJOR;        /*主設(shè)備號*/
static struct cdev *chrtest_cdev; /*創(chuàng)建cdev結(jié)構(gòu)體*/
static char kernel_buf[1024];     
static struct class *chrdev_class; /*定義一個class用于自動創(chuàng)建類*//*實現(xiàn)對應(yīng)的open/read/write等函數(shù),填入file_operations結(jié)構(gòu)體 */
static ssize_t char_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*將內(nèi)核空間的數(shù)據(jù)復(fù)制到用戶空間*/err = copy_to_user(buf, kernel_buf, MIN(1024, size));return MIN(1024, size);
}static ssize_t char_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*將buf中的數(shù)據(jù)復(fù)制到寫緩沖區(qū)kernel_buf中,因為用戶空間內(nèi)存不能直接訪問內(nèi)核空間的內(nèi)存*/err = copy_from_user(kernel_buf, buf, MIN(1024, size)); return MIN(1024, size);
}static int char_open (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static int char_close (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/*定義自己的file_operations結(jié)構(gòu)體*/
static struct file_operations chrtest_fops = {.owner = THIS_MODULE,.open  = char_open,.read  = char_read,.write = char_write,.release = char_close,
};/*注冊驅(qū)動函數(shù):寫入口函數(shù),安裝驅(qū)動程序時就會調(diào)用這個入口函數(shù) */
static int __init chardev_init(void)
{int result;/*dev_t 定義在文件 include/linux/types.htypedef __u32 __kernel_dev_t;......typedef __kernel_dev_t dev_t;可以看出 dev_t 是__u32 類型的,而__u32 定義在文件 include/uapi/asm-generic/int-ll64.h里面,定義如下:typedef unsigned int __u32;綜上所述,dev_t 其實就是 unsigned int 類型,是一個 32 位的數(shù)據(jù)類型。主設(shè)備號和次設(shè)備號兩部分,其中高 12 位為主設(shè)備號,低 20 位為次設(shè)備號。因此 Linux系統(tǒng)中主設(shè)備號范圍為 0~4095。*/dev_t devno;/*定義一個dev_t的變量表示設(shè)備號*/printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*字符設(shè)備驅(qū)動注冊的流程二:分配主次設(shè)備號,這里不僅支持靜態(tài)指定,也支持動態(tài)申請*//*靜態(tài)申請主次設(shè)備號*/if(0 != dev_major){devno = MKDEV(dev_major, 0);//將主設(shè)備號dev_major和從設(shè)備號0分配給devno變量result = register_chrdev_region(devno, 1, DEV_NAME);//請求分配一個設(shè)備號,名字為DEV_NAME(chardev),設(shè)備號是:88 0}else{result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);//求分配一個名字為chardev的設(shè)備號,從設(shè)備號為0,保存到devno變量中dev_major = MAJOR(devno);//獲取設(shè)備號}/*失敗后的處理結(jié)果,總規(guī)上面只執(zhí)行一次,所以直接在外面判斷就可*/if(result < 0){printk(KERN_ERR " %s chardev can't use major %d\n", DEV_NAME, dev_major);return -ENODEV;}printk(KERN_DEBUG " %s driver use major %d\n", DEV_NAME, dev_major);/*字符串設(shè)備驅(qū)動流程三:分配cdev結(jié)構(gòu)體,使用動態(tài)申請的方式*//*內(nèi)核在內(nèi)部使用類型struct cdev的結(jié)構(gòu)體來代表字符設(shè)備。在內(nèi)核調(diào)用你的設(shè)備操作之前,你必須分配一個這樣的結(jié)構(gòu)體并注冊給linux內(nèi)核,在這個結(jié)構(gòu)體里有對于這個設(shè)備進行操作的函數(shù),具體定義在file_operation結(jié)構(gòu)體中。*/if(NULL == (chrtest_cdev = cdev_alloc())){printk(KERN_ERR "%s driver can't alloc for the cdev\n", DEV_NAME);unregister_chrdev_region(devno, 1);//釋放掉設(shè)備號return -ENOMEM;}/*字符設(shè)備驅(qū)動流程四:分配cdev結(jié)構(gòu)體,綁定主次設(shè)備號,fops到cdev結(jié)構(gòu)體中,并且注冊到linux內(nèi)核*/chrtest_cdev -> owner = THIS_MODULE; /*.owner這表示誰擁有這個驅(qū)動程序*/cdev_init(chrtest_cdev, &chrtest_fops);/*初始化設(shè)備*/result = cdev_add(chrtest_cdev, devno, 1); /*將字符設(shè)備注冊進內(nèi)核*/if(0 != result){printk(KERN_INFO "%s driver can't register cdev:result = %d\n", DEV_NAME, result);goto ERROR;}printk(KERN_INFO "%s driver can register cdev:result = %d\n", DEV_NAME, result);/*自動創(chuàng)建設(shè)備類型、/dev設(shè)備節(jié)點*/chrdev_class = class_create(THIS_MODULE, DEV_NAME); /*創(chuàng)建設(shè)備類型sys/class/chrdev*/if (IS_ERR(chrdev_class)) {result = PTR_ERR(chrdev_class);goto ERROR;}device_create(chrdev_class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME); /*/dev/chrdev 注冊這個設(shè)備節(jié)點*/return 0;ERROR:printk(KERN_ERR" %s driver installed failure.\n", DEV_NAME);cdev_del(chrtest_cdev);unregister_chrdev_region(devno, 1);return result;}/* 有入口函數(shù)就應(yīng)該有出口函數(shù):卸載驅(qū)動程序時,就會去調(diào)用這個出口函數(shù)*/
static void __exit chardev_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 注銷設(shè)備類型、/dev設(shè)備節(jié)點*/device_destroy(chrdev_class, MKDEV(dev_major, 0)); /*注銷這個設(shè)備節(jié)點*/class_destroy(chrdev_class); /*刪除這個設(shè)備類型*/cdev_del(chrtest_cdev); /*注銷字符設(shè)備*/unregister_chrdev_region(MKDEV(dev_major,0), 1); /*釋放設(shè)備號*/printk(KERN_ERR" %s driver version 1.0.0 removed!\n", DEV_NAME);return;
}/*調(diào)用函數(shù) module_init 來聲明 xxx_init 為驅(qū)動入口函數(shù),當(dāng)加載驅(qū)動的時候 xxx_init函數(shù)就會被調(diào)用.*/
module_init(chardev_init);
/*調(diào)用函數(shù)module_exit來聲明xxx_exit為驅(qū)動出口函數(shù),當(dāng)卸載驅(qū)動的時候xxx_exit函數(shù)就會被調(diào)用.*/
module_exit(chardev_exit);/*添加LICENSE和作者信息,是來告訴內(nèi)核,該模塊帶有一個自由許可證;沒有這樣的說明,在加載模塊的時內(nèi)核會“抱怨”.*/
MODULE_LICENSE("Dual BSD/GPL");//許可 GPL、GPL v2、Dual MPL/GPL、Proprietary(專有)等,沒有內(nèi)核會提示.
MODULE_AUTHOR("WangDengtao");//作者
MODULE_VERSION("V1.0");//版本

測試字符設(shè)備驅(qū)動源碼:

/*************************************************************************> File Name: char_dev_test.c> Author: WangDengtao> Mail: 1799055460@qq.com > Created Time: 2023年03月16日 星期四 16時50分29秒************************************************************************/#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>int main(int argc, char **argv)
{int fd;char buf[1024];int len;/* 1. 判斷參數(shù) */if (argc < 2){printf("Usage: %s -w <string> /dev/??\n", argv[0]);printf(" %s -r\n", argv[0]);return -1;}/* 2. 打開文件 */if(argc == 4){fd = open(argv[3], O_RDWR);}if(argc == 3){fd = open(argv[2], O_RDWR);}if (fd == -1){printf("can not open file %s\n", argv[3]);return -1;}/* 3. 寫文件或讀文件 */if ((0 == strcmp(argv[1], "-w")) && (argc == 4)){len = strlen(argv[2]) + 1;len = len < 1024 ? len : 1024;printf("Write to %s success!\n", argv[3]);printf("write len: %d\n", len);write(fd, argv[2], len);}else if((0 == strcmp(argv[1], "-r")) && (argc == 3)){memset(buf, 0, sizeof(buf));len = read(fd, buf, 1024);printf("Read from %s success!\n", argv[2]);printf("read len: %ld\n", strlen(buf)+1);buf[1023] = '\0';printf("char_dev_test read : %s\n", buf);}else{printf("Usage: %s -w <string> /dev/??\n", argv[0]);printf(" %s -r\n", argv[0]);return -1;}close(fd);return 0;
}

(1)x86架構(gòu)虛擬機上運行

Makefile:

KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
obj-m := char_dev.oCC=gcc
APP_NAME=char_dev_testall:$(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules@${CC} ${APP_NAME}.c -o ${APP_NAME}@make clearclear:@rm -f *.o *.cmd *.mod *.mod.c@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f@rm -f .*ko.cmd .*.o.cmd .*.o.d@rm -f *.unsignedclean:@rm -f *.ko@rm -f ${APP_NAME}

運行結(jié)果:

在這里插入圖片描述
在這里插入圖片描述

(2)arm架構(gòu)開發(fā)板上運行

Makefile:

KERNAL_DIR ?= /home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx
PWD :=$(shell pwd)
obj-m := char_dev.oCC=arm-linux-gnueabihf-gcc
APP_NAME=char_dev_testall:$(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules@${CC} ${APP_NAME}.c -o ${APP_NAME}@make clearclear:@rm -f *.o *.cmd *.mod *.mod.c@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f@rm -f .*ko.cmd .*.o.cmd .*.o.d@rm -f *.unsignedclean:@rm -f *.ko@rm -f ${APP_NAME}

運行結(jié)果:
在這里插入圖片描述
這里提醒一下,我們需要將我們的測試程序和驅(qū)動程序復(fù)制到我們的tftpboot目錄下開發(fā)板才可以進行獲取。
開發(fā)板獲取以及測試:

root@igkboard:~# tftp -gr char_dev_test 192.168.10.168
root@igkboard:~# tftp -gr char_dev.ko 192.168.10.168
root@igkboard:~# ls
char_dev.ko  char_dev_test  hello.ko
root@igkboard:~# chmod a+x char_dev_test root@igkboard:~# ./char_dev_test                                       
Usage: ./char_dev_test -w <string> /dev/??
./char_dev_test -rroot@igkboard:~# insmod char_dev.ko
root@igkboard:~# ls -l /dev/chardev 
crw------- 1 root root 243, 0 Mar 18 04:34 /dev/chardevroot@igkboard:~# ./char_dev_test -w hello /dev/chardev 
Write to /dev/chardev success!
write len: 6
root@igkboard:~# ./char_dev_test -r /dev/chardev 
Read from /dev/chardev success!
read len: 6
char_dev_test read : hello
root@igkboard:~# rmmod char_dev
root@igkboard:~# ls -l /dev/chardev                    
ls: cannot access '/dev/chardev': No such file or directory

測試成功。

(4)copy_to/from_user()函數(shù)

代碼中出現(xiàn)的兩個沒有提到的函數(shù):

static inline long copy_to_user(void __user *to, const void *from, unsigned long n);
/*
to: 目標(biāo)地址,這個地址是用戶空間的地址; 
from: 源地址,這個地址是內(nèi)核空間的地址; 
n: 將要拷貝的數(shù)據(jù)的字節(jié)數(shù)。
*/
unsigned long copy_from_user (void * to, const void __user * from, unsigned long n);
/*
to: 目標(biāo)地址,這個地址是內(nèi)核空間的地址; 
from: 源地址,這個地址是用戶空間的地址; 
n: 將要拷貝的數(shù)據(jù)的字節(jié)數(shù)。
*/

copy_to_user 和 copy_from_user 是在進行驅(qū)動相關(guān)程序設(shè)計的時候,要經(jīng)常遇到的函數(shù)。由于內(nèi)
核空間與用戶空間的內(nèi)存不能直接互訪,因此借助函數(shù) copy_to_user() 完成內(nèi)核空間到用戶空間的復(fù)制,函數(shù) copy_from_user() 完成用戶空間到內(nèi)核空間的復(fù)制。

我們代碼中用到的全局變量kernel_buf是保存寫進去的內(nèi)容的,我們write的時候調(diào)用了copy_from_user(kernel_buf, buf, MIN(1024, size)函數(shù),將要寫進去的數(shù)據(jù)(buf)復(fù)制到讀緩沖區(qū)(kernel_buf)中,然后再read的時候,調(diào)用copy_to_user(buf, kernel_buf, MIN(1024, size)函數(shù)將kernel_buf中的值讀取出來復(fù)制到buf中,就可以直接讀到buf中了,也就獲取到了。


5 - inode與file結(jié)構(gòu)體

(1)inode結(jié)構(gòu)體

Linux中一切皆文件,當(dāng)我們在Linux中創(chuàng)建一個文件時,就會在相應(yīng)的文件系統(tǒng)中創(chuàng)建一個inode與之對應(yīng),文件實體和文件inode是一一對應(yīng)的,創(chuàng)建好一個inode會存在存儲器中。第一次open就會將inode在內(nèi)存中有一個備份,同一個文件被多次打開并不會產(chǎn)生多個inode,當(dāng)所有被打開的文件都被close之后,inode在內(nèi)存中的實例才會被釋放。既然如此,當(dāng)我們使用mknod(或其他方法)創(chuàng)建一個設(shè)備文件時,也會在文件系統(tǒng)中創(chuàng)建一個inode,這個inode和其他的inode一樣,用來存儲關(guān)于這個文件的靜態(tài)信息(不變的信息),包括這個設(shè)備文件對應(yīng)的設(shè)備號,文件的路徑以及對應(yīng)的驅(qū)動對象等。

struct inode {······struct hlist_node	i_hash;struct list_head	i_list;		/* backing dev IO list */struct list_head	i_sb_list;//主次設(shè)備號dev_t				i_rdev;struct list_head	i_devices;//用聯(lián)合體是因為該文件可能是塊設(shè)備文件或者字符設(shè)備文件union {struct pipe_inode_info	*i_pipe;	//管道文件struct block_device	    *i_bdev;	//塊設(shè)備文件struct cdev		*i_cdev;	//字符設(shè)備文件};//私有數(shù)據(jù)void			*i_private; /* fs or device private pointer */
};

我們一般比較關(guān)心的只有兩個變量:

  • dev_t i_rdev:
    代表設(shè)備文件的節(jié)點,這個成員包含實際的設(shè)備編號
  • struct cdev *i_cdev:
    這個結(jié)構(gòu)體代表字符設(shè)備,這個成員包含一個指針,指向這個結(jié)構(gòu)體,當(dāng)節(jié)點指的是一個字符設(shè)備文件時。

(2)file結(jié)構(gòu)體

file結(jié)構(gòu)體代表一個打開的文件。它由內(nèi)核在open時創(chuàng)建,并傳遞給在文件上操作的任何函數(shù),直到最后的關(guān)閉。在文件的所有實例都關(guān)閉后,內(nèi)核釋放這個數(shù)據(jù)結(jié)構(gòu)。

struct file結(jié)構(gòu)體 用來表示一個動態(tài)的設(shè)備,每當(dāng)open打開一個文件時就會產(chǎn)生一個struct file結(jié)構(gòu)體 與之對應(yīng)。

struct file {union {struct list_head	fu_list;struct rcu_head 	fu_rcuhead;}f_u;······const struct file_operations	*f_op;	//該文件對應(yīng)的操作方法unsigned int 	f_flags;	fmode_t			f_mode;	//打開文件的權(quán)限,比如:只讀打開、只寫打開、讀寫打開loff_t			f_pos;	//文件指針的偏移量/* needed for tty driver, and maybe others */void			*private_data;	//私有數(shù)據(jù)
};

在這里插入圖片描述
結(jié)合上面的圖片可以進一步了解兩個結(jié)構(gòu)體之間是如何聯(lián)系的。


http://www.risenshineclean.com/news/12120.html

相關(guān)文章:

  • 網(wǎng)站建設(shè)談客戶說什么網(wǎng)絡(luò)營銷有哪些形式
  • 建站網(wǎng)站怎么上傳代碼奉節(jié)縣關(guān)鍵詞seo排名優(yōu)化
  • 學(xué)生可以做的網(wǎng)站兼職百度論壇發(fā)帖
  • 廣州網(wǎng)站設(shè)計價格手機優(yōu)化大師官方版
  • wordpress注冊郵箱發(fā)送網(wǎng)站 seo
  • 網(wǎng)站做外鏈的技巧天津seo網(wǎng)絡(luò)
  • 建一個團購網(wǎng)站要多少錢網(wǎng)站建設(shè) 網(wǎng)站制作
  • 做什么網(wǎng)站能吸引流量免費正規(guī)大數(shù)據(jù)查詢平臺
  • 鶴壁網(wǎng)站建設(shè)公司佛山網(wǎng)站建設(shè)模板
  • 國內(nèi)知名網(wǎng)站制作公司文明seo技術(shù)教程網(wǎng)
  • 深圳建設(shè)廳官方網(wǎng)站上海百度分公司電話
  • 簡述網(wǎng)站一般建設(shè)的流程圖瀏覽器谷歌手機版下載
  • 網(wǎng)站建設(shè)免費的靠得住嗎seo3
  • 云匠網(wǎng)的美工靠譜嗎石家莊seo全網(wǎng)營銷
  • 在電腦上做苗木網(wǎng)站單頁網(wǎng)站制作
  • 網(wǎng)站怎么增加流量如何優(yōu)化網(wǎng)站快速排名
  • 龍泉驛區(qū)建設(shè)局網(wǎng)站免費的發(fā)帖收錄網(wǎng)站
  • gis網(wǎng)站開發(fā)教程網(wǎng)絡(luò)營銷的四大要素
  • jsp怎樣做網(wǎng)站網(wǎng)絡(luò)推廣是什么職位
  • 廈門網(wǎng)站做優(yōu)化今日關(guān)鍵詞
  • 做英語教具的網(wǎng)站谷歌競價排名推廣公司
  • 做彈幕視頻效果的網(wǎng)站百度渠道開戶哪里找
  • 搜索網(wǎng)站怎么做產(chǎn)品營銷推廣的方案
  • 建站網(wǎng)站教程成品人和精品人的區(qū)別在哪
  • 正規(guī)網(wǎng)站建設(shè)建設(shè)公司做網(wǎng)站seo優(yōu)化
  • vscode 網(wǎng)站開發(fā)專業(yè)網(wǎng)絡(luò)推廣公司
  • 做藥物研發(fā)的人上什么網(wǎng)站seo發(fā)外鏈的網(wǎng)站
  • 做百度移動端網(wǎng)站軟件下載江門關(guān)鍵詞優(yōu)化公司
  • 網(wǎng)站服務(wù)器異常是什么意思云南seo網(wǎng)站關(guān)鍵詞優(yōu)化軟件
  • 組建網(wǎng)站 多少錢南京seo域名