通过sysfs文件系统接口来改变内核模块中的变量值(一)--通过fasync实现

简介: 通过sysfs文件系统接口来改变内核模块中的变量值(一)--通过fasync实现

下面用一个具体的例子来展示设备驱动程序如何实现fasync方法,以及应用程序如何得到来自设备驱动程序的异步通知。这个例了同时也展示了sysfs文件系统在驱动程序中的用法,以及通过Linux设各模型来创建设备节点及其他一些特性(这个看起来很简单的内核模块其实体现了设备驱动程序中一些比较重要且典型的特征)。

首先是设备驱动程序的代码,在代码中,我们将Linux设备模型中的一些概念融入其中(本书第9章会详细讨论Linux的设备驱动模型,不过读者可以在这里先热热身),这样我们可以动态创建一个设备节点而无须再手动地使用mknod命令,同时代码中还创建了一个sysfs文件接口,这使得我们可以直接操控设备驱动程序中的一些数据而不必采用ioctl的方式·也许这就是设备驱动模型给我们带来的好处吧。

1.fasync_flag.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
static unsigned long flag = 0;
/*设备struct*/
struct led_struct
{
    dev_t led_dev;
        struct cdev led_cdev;
        struct class *led_class;
        struct device *led_device;
        struct fasync_struct *sigio_list;
        struct device_node *led_nd;
};
struct led_struct led_struct;
ssize_t read_flag(struct device *dev,struct device_attribute *attr,char *buf){
     size_t count = 0;
    count += sprintf(&buf[count],"%lu\n",flag);
        return count;
}
ssize_t write_flag(struct device * dev,struct device_attribute *attr,char *buf,size_t count){
    flag = buf[0] - '0';
     //给所有以FASYNC标志调用fcntl的应用程序发信号
        kill_fasync(&led_struct.sigio_list,SIGIO,POLL_IN);
        return count;
}
struct device_attribute flag_attr = __ATTR(flag,S_IRUGO | S_IWUSR,read_flag,write_flag);
int led_open(struct inode *inode, struct file *filp)//打开led
{
        printk("_____%s_____\n",__FUNCTION__);
        return 0;
}
int led_fasync (int fd, struct file *filp, int onflag){
    //将需要通知的迸程加入sigio_list链表或者从链表中移除
        return fasync_helper(fd, filp, onflag, &led_struct.sigio_list);
}
static struct file_operations led_ops = {
        .owner = THIS_MODULE,
        .open = led_open,        
        .fasync = led_fasync,
};
static int __init led_init(void)
{    
        int ret = 0;
        //    1.分配设备号
        alloc_chrdev_region(&led_struct.led_dev, 0, 1, "my_led");
        //2.创造cdev对象和file_operation,然后cdev_init初始化
        cdev_init(&led_struct.led_cdev, &led_ops);
        //3.cdev_add将cdev对象注册进入内核
        cdev_add(&led_struct.led_cdev, led_struct.led_dev, 1);
        //4.创造class与device
    led_struct.led_class =  class_create(THIS_MODULE, "led_class");
    led_struct.led_device=device_create(led_struct.led_class, NULL, led_struct.led_dev, NULL, "led");
        //在文件系统中创建一个名为"flag的文件
        ret = device_create_file(led_struct.led_device,&flag_attr);
        printk("flag = %d\n",flag);
return ret;
}
static void __exit led_exit(void)
{
    printk("flag = %d\n",flag);
        device_destroy(led_struct.led_class, led_struct.led_dev);
        class_destroy(led_struct.led_class);
        cdev_del(&led_struct.led_cdev);
        unregister_chrdev_region(led_struct.led_dev, 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

2.app.c

应用程序:app.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
int fd_tty,fd_led,fd_iic;
static unsigned long eflag;
static void sigio_handler(int sigio){
    eflag = 0;
}
static int block_sigio(void){
    sigset_t set,old;
        int ret;
        sigisemptyset(&set);
        sigaddset(&set,SIGIO);
        sigprocmask(SIG_BLOCK,&set,&old);
    ret = sigismember(&old, SIGIO);
        return ret;
}
static void unblock_sigio(int blocked){
    sigset_t set;
        if(!blocked){
        sigisemptyset(&set);
        sigaddset(&set,SIGIO);
                sigprocmask(SIG_UNBLOCK,&set,NULL);
        }
}
int main(int argc, char *argv[])
{
        int fd;
        struct sigaction sigact,oldact;
        int oflag;
        int blocked;
    blocked = block_sigio();
        sigisemptyset(&sigact.sa_mask);
        sigaddset(&sigact.sa_mask,SIGIO);
    sigact.sa_flags = 0;
    sigact.sa_handler = sigio_handler;
        if(sigaction(SIGIO,&sigact,&oldact) < 0){
                printf("sigaction failed!\n");
                unblock_sigio(blocked);
                return -1;
            }
    unblock_sigio(blocked);    
       fd = ("/dev/led",O_RDWR);
       if(fd>=0){
        fcntl(fd_led,F_SETOWN,getpid());
        oflag = fcntl(fd_led,F_GETFL);
        fcntl(fd_led,F_SETFL,oflag | FASYNC);
        printf("do everything you want until we get signal..\n");
           while(eflag);   
        close(fd);
        }
        return 0;         
}

3.Makefile

#make 编译项目
#make file 在存放.ko文件目录中创建对应项目的目录
#make install 将*.ko及其应用测试文件移动到根文件中
 # 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册
# ROOTFS_DIR 根文件系统中存放 *.ko文件所在目录
# PROJECT_NAME 在存放.ko文件目录中创建对应项目的目录
# DRIVER_NAME 项目中需要编译出.ko来的驱动
# APP_NAME 项目中的应用测试文件
#make 编译项目
#make file 在存放.ko文件目录中创建对应项目的目录
#make install 将*.ko及其应用测试文件移动到根文件中
KERN_DIR = /home/alientek/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
ROOTFS_DIR = /home/alientek/linux/nfs/rootfs/experiment
#项目名字
PROJECT_NAME = fasync
#各驱动名字,ko
DRIVER_NAME1 = fasync_flag
DRIVER_NAME2 = 
#app名字
APP_NAME = app
all:
  make -C $(KERN_DIR) M=`pwd` modules 
  $(CROSS_COMPILE)arm-linux-gnueabihf-gcc -o $(APP_NAME) $(APP_NAME).c 
clean:
  make -C $(KERN_DIR) M=`pwd` modules clean
  rm -rf modules.order
  rm -f $(APP_NAME)
file:
  mkdir $(ROOTFS_DIR)/$(PROJECT_NAME)
install:
  cp *.ko $(ROOTFS_DIR)/$(PROJECT_NAME)
  cp $(APP_NAME) $(ROOTFS_DIR)/$(PROJECT_NAME)
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
obj-m += $(DRIVER_NAME1).o

结果

1.编译

make

2.在根文件系统下创建fasync目录

sudo make file

3.复制模块文件和应用程序到根文件目录下

sudo make install

4.安装模块

insmod fasync_flag.ko

5.安装后结果

变量flag此时为0,另外在目录**/sys/devices/virtual/led_class/led** 下有一个flag文件,我们可以通过修改这个flag文件来修改变量flag的值

6.通过文件修改变量flag的值

echo 5 > flag

然后卸载模块查看变量的大小

rmmod fasync_flag.ko

变量flag的值最终被成功修改

目录
相关文章
|
3天前
|
安全 Linux
【亮剑】`chattr`是Linux中用于管理文件和目录扩展属性的命令,影响文件系统处理方式
【4月更文挑战第30天】`chattr`是Linux中用于管理文件和目录扩展属性的命令,影响文件系统处理方式。常用属性包括:`a`(追加)、`i`(不可变)、`s`(安全删除)和`S`(同步更新)。通过`chattr [选项] <模式> <文件或目录>`设置属性,如`chattr +i <文件名>`使文件不可变,`-i`移除不可变属性。`lsattr`用于查看属性。注意,只有root用户有权更改属性,不是所有文件系统都支持所有属性,且更改关键文件属性前应备份。`chattr`有助于提升系统安全性和数据保护。
|
3天前
使用udev 设置磁盘属性
使用udev 设置磁盘属性
27 0
|
10月前
通过sysfs文件系统接口来改变内核模块中的变量值(二)
通过sysfs文件系统接口来改变内核模块中的变量值(二)
38 0
|
存储 传感器 固态存储
3.2 Linux文件系统到底有什么用处?
Linux 上的文件系统一般来说就是EXT2或EXT3,但这篇文章并不准备一上来就直接讲它们,而希望结合Linux操作系统并从文件系统建立的基础——硬盘开始,一步步认识Linux的文件系统。
375 0
3.2 Linux文件系统到底有什么用处?
|
缓存 Unix Linux
Linux虚拟文件系统剖析: 文件打开、读、写逻辑
                        Linux虚拟文件系统剖析: 文件打开、读、写逻辑 perftrace@gmail.com   1     Linux文件系统剖析:文件打开操作 本文主要通过分析linux系统中的文件打开逻辑,来掌握linux虚拟文件系统相关的数据结构、函数等知识点,将之前的各个点的知识串联成一个整体。
5865 0