日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

(七)linux函数接口的使用

發(fā)布時間:2025/3/8 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (七)linux函数接口的使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前面我們講解了字符設備的驅動模型,有了前面的基礎后,今天學習函數(shù)接口就比較容易了

目錄

        • (一)open函數(shù)接口
        • (二)read函數(shù)接口
        • (三)lseek函數(shù)接口
        • (四)用戶空間和用戶空間交換數(shù)據(jù)
        • (五)通過設備節(jié)點提取設備號
        • (六)映射ioremap
        • (七)實例:LED驅動編程

思考一個問題:當我們應用層調用open、read、write、close的時候,內核層是如何實現(xiàn)的呢?
前面學習字符設備驅動模型中有一個file_operation結構體,當我們調用open函數(shù)的時候,內核會調用file_operation結構體的open函數(shù)指針指向的函數(shù)。

我們來看一下file_operation結構體的樣子:

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int); //llseek對應了系統(tǒng)提供的lseek接口,實現(xiàn)函數(shù)指針位置的定位ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//當用戶層調用系統(tǒng)層提供的read接口的時候需要通過此函數(shù)指針所指向的接口來實現(xiàn)對應的操作ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//當用戶層調用系統(tǒng)層提供的write接口的時候需要通過此函數(shù)指針所指向的接口來實現(xiàn)對應的操作unsigned int (*poll) (struct file *, struct poll_table_struct *);// 當需要進行輪詢操作的時候調用的底層接口,對應了系統(tǒng)層的select和pollint (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);//struct inode *:內核內部用來標識文件的數(shù)據(jù)結構 int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*fasync) (int, struct file *, int); };

(一)open函數(shù)接口

系統(tǒng)層接口:

int open(const char *pathname, int flags, mode_t mode);//O_CREAT//O_NONBLOCK or O_NDELAY

內核層接口:

int (*open) (struct inode *, struct file *);

struct inode :內核中用來標識文件的數(shù)據(jù)結構,此數(shù)據(jù)結構的成員無需程序員手動賦值,而是內核中已經(jīng)賦予了與文件相對應的操作值
struct file *:該結構體標識了一個打開的文件,系統(tǒng)會為每一個打開的文件關聯(lián)一個struct file 數(shù)據(jù)結構,是在內核打開文件的同時,將該參數(shù)傳遞到和文件操作相關的所有需要該參數(shù)的接口中

(二)read函數(shù)接口

系統(tǒng)層:

#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);

內核層:

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

read接口可以直接將內核空間的數(shù)據(jù)傳遞到用戶空間,但是一般在開發(fā)驅動的過程中不會直接采用這種方式,原因是本操作需要兩個空間地址,即用戶空間和內核空間,用戶空間直接操作內核地址是非常危險的,常用copy_to_user和copy_from_user進行用戶空間和內核空間交換數(shù)據(jù)。

(三)lseek函數(shù)接口

系統(tǒng)層:

#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence);

內核層:

loff_t (*llseek) (struct file *, loff_t, int);

struct file *:文件結構體
loff_t:上層傳遞的偏移量
int :文件光標定位狀態(tài)

SEEK_SET:將光標定位在文件的開頭,此時loff_t的值為正數(shù) SEEK_CUR:將光標定位在當前位置,此時loff_t的值為可正可負 SEEK_EDN:將光標定位在文件的結尾,此時loff_t的值為負數(shù)

在這里面可以實現(xiàn)文件偏移操作,例如:

loff_t cdev_lseek(struct file *fp, loff_t offset, int whence) {//獲取偏移量需要offset和whence結合loff_t newoff=0;switch(whence){case SEEK_SET: newoff=offset;break;case SEEK_CUR: newoff=fp->f_pos+offset;break;case SEEK_END: newoff=offset+4;break;}if(newoff >4)newoff=4;if(newoff<0)newoff=0;fp->f_pos = newoff;return newoff;}

(四)用戶空間和用戶空間交換數(shù)據(jù)

copy_to_user:將內核空間的數(shù)據(jù)拷貝到用戶空間

static inline long copy_to_user(void __user *to,const void *from, unsigned long n) {to:用戶空間的地址from:內核空間的地址n:傳遞數(shù)據(jù)的大小 might_sleep();#define VERIFY_WRITE 1if (access_ok(VERIFY_WRITE, to, n))return __copy_to_user(to, from, n);elsereturn n; }

copy_from_user:將用戶空間的數(shù)據(jù)拷貝到內核空間

static inline long copy_from_user(void *to,const void __user * from, unsigned long n) {might_sleep();if (access_ok(VERIFY_READ, from, n))return __copy_from_user(to, from, n);elsereturn n; }

(五)通過設備節(jié)點提取設備號

//通過設備節(jié)點提取次設備號 static inline unsigned iminor(const struct inode *inode) {return MINOR(inode->i_rdev); } //通過設備節(jié)點提取次主設備號 static inline unsigned imajor(const struct inode *inode) {return MAJOR(inode->i_rdev); }

(六)映射ioremap

程序中在操作物理硬件地址的時候不要直接操作對應的地址,需要先進行映射操作

static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size) {return (void __iomem*) (unsigned long)offset; } typedef u32 phys_addr_t; phys_addr_t offset:指的是映射的物理地址 unsigned long size:映射空間的大小 void __iomem *:接收映射后的起始地址

解除映射:

void iounmap (volatile void __iomem *addr)

(七)實例:LED驅動編程

思路:
首先把需要操作的寄存器物理地址進行映射,然后在open函數(shù)中做初始化工作,最后在read/write函數(shù)中調用copy_to/from_user函數(shù)將用戶空間(內核空間)的數(shù)據(jù)拷貝到內核空間(用戶空間),對數(shù)據(jù)進行操作
led.c

#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h>int i=0; dev_t dev=0; #define CDEVCOUNT 5 #define CDEVNAME "cdevdevice" #define CDEVCLASS "myclass" #define INODENAME "mycdev"#define ADDRSZIE 8unsigned int phy_addr = 0x110002E0;//映射的起始地址為GPM4CON unsigned int * virt_addr = NULL;//用來接收映射后的起始地址struct cdev * cdev=NULL; struct class * cdevclass=NULL;#define GPM4CON (*(volatile unsigned int * )virt_addr) #define GPM4DAT (*(volatile unsigned int * )(virt_addr +1)) int cdev_open (struct inode *node, struct file *file) {//清空配置寄存器GPM4CON &= ~(0XFFFF<<0);//設置引腳為輸出狀態(tài)GPM4CON |= (0x1111<<0);//給指定寄存器初始化GPM4DAT |= (0x0F<<0);printk("cdev_open is install\n");return 0; } ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset) {printk("cdev_read is install\n");return 0; } ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset) {int i=0;char str[4]={-1,-1,-1,-1};int ret =copy_from_user(str,buf,4);for(i=0;i<4;i++){if(str[i]=='0')GPM4DAT |= (1<<i);else if(str[i]=='1')GPM4DAT &=~(1<<i);}printk("cdev_write is install\n");return 0; } int cdev_release (struct inode *node, struct file *fp) {printk("cdev_release is install\n");return 0; } struct file_operations fop={.open=cdev_open,.read=cdev_read,.write=cdev_write,.release=cdev_release, }; static int __init cdev_module_init(void) {int ret=alloc_chrdev_region(&dev, 0, CDEVCOUNT, CDEVNAME);if(ret){return -1;}cdev=cdev_alloc();if (!cdev)goto out;cdev_init(cdev, &fop);if(cdev_add(cdev, dev, CDEVCOUNT)){goto out1;}printk("cdev_add success\n");cdevclass=class_create(THIS_MODULE,CDEVCLASS);if (IS_ERR(cdevclass)){goto out2;}printk("class_create success\n");for(i=0;i<5;i++)device_create(cdevclass,NULL, dev+i, NULL, "mycdev%d",i );printk("device_create success\n");virt_addr = ioremap(phy_addr, ADDRSZIE);return 0;out:unregister_chrdev_region(dev,CDEVCOUNT);return -2;out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return PTR_ERR(cdevclass); } static void __exit cdev_module_cleanup(void) {for(--i;i>=0;i--)device_destroy(cdevclass,dev+i);printk("device_destroy success\n");class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);iounmap(virt_addr);printk("kfree success\n"); } module_init(cdev_module_init); module_exit(cdev_module_cleanup); MODULE_LICENSE("GPL");

led_app.c

#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {char str[]={'1','1','1','0'};int fd= open(argv[1],O_RDWR);if(fd== -1){perror("open");return -1;}write(fd,str,4);close(fd);return 0; }

Makefile

CFLAG =-C TARGET = led TARGET1 = led_app KERNEL = /mydriver/linux-3.5 obj-m += $(TARGET).oall:make $(CFLAG) $(KERNEL) M=$(PWD)arm-linux-gcc -o $(TARGET1) $(TARGET1).c clean:make $(CFLAG) $(KERNEL) M=$(PWD) clean

本文章僅供學習交流用禁止用作商業(yè)用途,文中內容來水枂編輯,如需轉載請告知,謝謝合作

微信公眾號:zhjj0729

微博:文藝to青年

總結

以上是生活随笔為你收集整理的(七)linux函数接口的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。