当前位置: 首页 > news >正文

Linux第78步_使用原子整型操作来实现“互斥访问”共享资源

使用原子操作来实现“互斥访问”LED灯设备,目的是每次只允许一个应用程序使用LED灯。

1、创建MyAtomicLED目录

输入“cd /home/zgq/linux/Linux_Drivers/回车

切换到“/home/zgq/linux/Linux_Drivers/”目录

输入“mkdir MyAtomicLED回车”,创建MyAtomicLED目录

输入“ls回车”查看“/home/zgq/linux/Linux_Drivers/”目录下的文件和文件夹

2、添加gpio_led节点

若在stm32mp157d-atk.dts文件中的根节点下没有gpio_led节点,则添加gpio_led节点

3、编译设备树

1)、在VSCode终端,输入“make dtbs回车”,执行编译设备树

2)、输入“ls arch/arm/boot/uImage -l

查看是否生成了新的“uImage”文件

3)、输入“ls arch/arm/boot/dts/stm32mp157d-atk.dtb -l

查看是否生成了新的“stm32mp157d-atk.dtb”文件

拷贝输出的文件:

4)、输入“cp arch/arm/boot/uImage /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车”,执行文件拷贝,准备烧录到EMMC;

5)、输入“cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车”,执行文件拷贝,准备烧录到EMMC

6)、输入“cp arch/arm/boot/uImage /home/zgq/linux/tftpboot/ -f回车”,执行文件拷贝,准备从tftp下载;

7)、输入“cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/tftpboot/ -f回车”,执行文件拷贝,准备从tftp下载;

8)、输入“ls -l /home/zgq/linux/atk-mp1/linux/bootfs/回车”,查看“/home/zgq/linux/atk-mp1/linux/bootfs/”目录下的所有文件和文件夹

9)、输入“ls -l /home/zgq/linux/tftpboot/回车”,查看“/home/zgq/linux/tftpboot/”目录下的所有文件和文件夹

输入“chmod 777 /home/zgq/linux/tftpboot/stm32mp157d-atk.dtb回车

给“stm32mp157d-atk.dtb”文件赋予可执行权限

输入“chmod 777 /home/zgq/linux/tftpboot/uImage回车 ,给“uImage”文件赋予可执行权限

输入“ls /home/zgq/linux/tftpboot/回车”,查看“/home/zgq/linux/tftpboot/”目录下的所有文件和文件夹

4、创建LED.c

#include "LED.h"

#include <linux/gpio.h>

//使能gpio_request(),gpio_free(),gpio_direction_input(),

//使能gpio_direction_output(),gpio_get_value(),gpio_set_value()

#include <linux/of_gpio.h>

//使能of_gpio_named_count(),of_gpio_count(),of_get_named_gpio()

struct MyAtomicLED_dev  strMyAtomicLED;

int Get_gpio_num(void);

int led_GPIO_request(void);

void led_switch(u8 sta,struct MyAtomicLED_dev *dev);

int Get_gpio_num(void)

{

  int ret = 0;

  const char *str;

  /* 设置LED所使用的GPIO */

  /* 1、获取设备节点:strMyAtomicLED */

  strMyAtomicLED.nd = of_find_node_by_path("/gpio_led");

  //path="/gpio_led,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“gpio_led”

  //返回值:返回找到的节点,如果为NULL,表示查找失败。

  if(strMyAtomicLED.nd == NULL) {

    printk("gpio_led node not find!\r\n");

    return -EINVAL;

  }

  /* 2.读取status属性 */

  ret = of_property_read_string(strMyAtomicLED.nd, "status", &str);

  //在gpio_led节点中,status = "okay";

  //指定的设备节点strMyAtomicLED.nd

  //proname="status",给定要读取的属性名字

  //out_string=str:返回读取到的属性值

  //返回值:0,读取成功,负值,读取失败。

  if(ret < 0) return -EINVAL;

  if (strcmp(str, "okay")) return -EINVAL;

  //strcmp(s1,s2),当s1<s2时,返回值为负数

  //strcmp(s1,s2),当s1>2时,返回值为正数

  //strcmp(s1,s2),当s1=s2时,返回值为0

  /* 3、获取compatible属性值并进行匹配 */

  ret = of_property_read_string(strMyAtomicLED.nd, "compatible", &str);

  //在gpio_led节点中,compatible = "zgq,led";

  //指定的设备节点strMyAtomicLED.nd

  //proname="compatible",给定要读取的属性名字

  //out_string=str:返回读取到的属性值

  //返回值:0,读取成功,负值,读取失败。

  if(ret < 0) {

    printk("gpio_led node: Failed to get compatible property\n");

    return -EINVAL;

  }

  if (strcmp(str, "zgq,led")) {

    printk("gpio_led node: Compatible match failed\n");

    return -EINVAL;

  }

  /* 4、 根据设备树中的"led-gpio"属性,得到LED所使用的LED编号 */

  strMyAtomicLED.led_gpio = of_get_named_gpio(strMyAtomicLED.nd, "led-gpio", 0);

  //在gpio_led节点中,led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>

  //np=strMyAtomicLED.nd,指定的“设备节点”

  //propname="led-gpio",给定要读取的属性名字

  //Index=0,给定的GPIO索引为0

  //返回值:正值,获取到的GPIO编号;负值,失败。

  if(strMyAtomicLED.led_gpio < 0) {

    printk("can't get led-gpio");

    return -EINVAL;

  }

  printk("led-gpio num = %d\r\n", strMyAtomicLED.led_gpio);

  //打印结果为:“led-gpio num = 128“

  //因为GPIO编号是从0开始的,GPIOI端口的序号是8,每个端口有16个IO口,因此GPIOI0的编号为8*16=128

  return 0;

}

int led_GPIO_request(void)

{

  int ret = 0;

  /* 5.向gpio子系统申请使用“gpio编号” */

  ret = gpio_request(strMyAtomicLED.led_gpio, "LED-GPIO");

  //gpio=strMyAtomicLED.led_gpio,指定要申请的“gpio编号”

  //Iabel="LED-GPIO",给这个gpio引脚设置个名字为"LED-GPIO"

  //返回值:0,申请“gpio编号”成功;其他值,申请“gpio编号”失败;

  if (ret) {

    printk(KERN_ERR "strMyAtomicLED: Failed to request led-gpio\n");

    return ret;

  }

  /* 6、设置PI0为输出,并且输出高电平,默认关闭LED灯 */

  ret = gpio_direction_output(strMyAtomicLED.led_gpio, 1);

  //gpio=strMyAtomicLED.led_gpio,指定的“gpio编号”,这里是128,对应的是GI0引脚

  //value=1,设置引脚输出高电平

  //返回值:0,设置“引脚输出为vakued的值”成功;负值,设置“引脚输出为vakued的值”失败。

  if(ret < 0) {

    printk("can't set gpio!\r\n");

  }

  return 0;

}

void led_switch(u8 sta,struct MyAtomicLED_dev *dev)

{

if(sta == LEDON) {

    gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */

}

else if(sta == LEDOFF) {

    gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */

}

}

5、创建LED.h

#ifndef __LED_H

#define __LED_H

#include <linux/types.h>

/*

数据类型重命名

使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t

使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t

*/

#include <linux/cdev.h> //使能cdev结构

#include <linux/cdev.h> //使能class结构和device结构

#include <linux/of.h>   //使能device_node结构

#define LEDOFF 0 /* 关灯 */

#define LEDON 1 /* 开灯 */

struct MyAtomicLED_dev{

  dev_t devid; /*声明32位变量devid用来给保存设备号*/

  int major;   /*主设备号*/

  int minor;   /*次设备号*/

  struct cdev  cdev; /*字符设备结构变量cdev */

  struct class *class;     /*类*/

  struct device *device;  /*设备*/

  struct device_node *nd; /*设备节点*/

  int led_gpio;   /*led所使用的GPIO编号*/

  atomic_t lock;  /*原子变量*/

};

extern struct MyAtomicLED_dev strMyAtomicLED;

extern int Get_gpio_num(void);

extern int led_GPIO_request(void);

extern void led_switch(u8 sta,struct MyAtomicLED_dev *dev);

#endif

6、创建LEDInterface.c

#include "LED.h"

#include <linux/types.h>

//数据类型重命名

//使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t

//使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t

#include <linux/ide.h>

//使能copy_from_user(),copy_to_user()

#include <linux/module.h>

//使能MyAtomicLED_init(),MyAtomicLED_exit()

#include <linux/gpio.h>

//使能gpio_request(),gpio_free(),gpio_direction_input(),

//gpio_direction_output(),gpio_get_value(),gpio_set_value()

#define MyAtomicLED_CNT    1   //定义设备数量为1

#define MyAtomicLED_NAME  "MyAtomicLEDName"  //定义设备的名字

/* 打开设备 */

static int MyAtomicLED_open(struct inode *inode, struct file *filp)

{

  /*通过判断原子变量的值来检查LED有没有被别的应用使用*/

  if (!atomic_dec_and_test(&strMyAtomicLED.lock))

  {

    //当strMyAtomicLED.lock.counter=1时,atomic_dec_and_test()返回1

    //从strMyAtomicLED.lock.counter减1,如果结果为0就返回1,否则返回0;

    atomic_inc(&strMyAtomicLED.lock);/*小于0的话就加1,使其原子变量等于0*/

    return -EBUSY; /* LED被使用,返回忙*/

  }

  filp->private_data = &strMyAtomicLED; /*设置私有数据*/

  printk("MyAtomicLED_open!\r\n");

  return 0;

}

/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t MyAtomicLED_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)

{

  return 0;

}

/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t MyAtomicLED_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

  int ret = 0;

  unsigned char databuf[1];

  unsigned char ledstat;

  ret = copy_from_user(databuf, buf, cnt);

//将buf[]中的前cnt个字节拷贝到databuf[]中

  if(ret <0){

    printk("kernel write failed!\r\n");

    ret = -EFAULT;

  }

  ledstat = databuf[0];/*获取到应用传递进来的开关灯状态*/

  led_switch(ledstat,filp->private_data);/*执行开灯或执行关灯*/

  return ret;

}

/* 关闭/释放设备 */

static int MyAtomicLED_release(struct inode *inode, struct file *filp)

{

  struct MyAtomicLED_dev *dev = filp->private_data;

  atomic_inc(&dev->lock);

  /*关闭驱动文件的时候释放原子变量,便于其它线程使用*/

  printk("MyAtomicLED_release!\r\n");

  return 0;

}

/*声明file_operations结构变量MyCharDevice_fops*/

/*它是指向设备的操作函数集合变量*/

const struct file_operations MyAtomicLED_fops = {

  .owner = THIS_MODULE,

  .open = MyAtomicLED_open,

  .read = MyAtomicLED_read,

  .write = MyAtomicLED_write,

  .release = MyAtomicLED_release,

};

/*驱动入口函数 */

static int  __init MyAtomicLED_init(void)

{

  int ret;

  strMyAtomicLED.lock = (atomic_t)ATOMIC_INIT(0);

  /*初始化原子变量*/

  atomic_set(&strMyAtomicLED.lock, 1);

  /*原子变量初始值strMyAtomicLED.lock.counter=1*/

  ret=Get_gpio_num();//读引脚编号

  if(ret < 0) return ret;

/* 1、申请“gpio编号”*/

  ret=led_GPIO_request();//申请“gpio编号” 

  if(ret < 0) return ret;//向gpio子系统申请使用“gpio编号” 失败

  /*2、申请设备号*/

  strMyAtomicLED.major=0;

  if(strMyAtomicLED.major)/*如果指定了主设备号*/

  {

    strMyAtomicLED.devid = MKDEV(strMyAtomicLED.major, 0);

    //输入参数strMyAtomicLED.major为“主设备号”

    //输入参数0为“次设备号”,大部分驱动次设备号都选择0

    //将strMyAtomicLED.major左移20位,再与0相或,就得到“Linux设备号”

ret=register_chrdev_region( strMyAtomicLED.devid,\

                       MyAtomicLED_CNT, \

                       MyAtomicLED_NAME );

    //strMyAtomicLED.devid表示起始设备号

    //MyAtomicLED_CNT表示次设备号的数量

    //MyAtomicLED_NAME表示设备名

    if(ret < 0)

      goto free_gpio;

  }

  else

  { /* 没有定义设备号 */

ret=alloc_chrdev_region( &strMyAtomicLED.devid,\

                     0, \

                     MyAtomicLED_CNT,\

                     MyAtomicLED_NAME);

    /* 申请设备号 */

    //strMyAtomicLED.devid:保存申请到的设备号

    //0:次设备号的起始地址

    //MyAtomicLED_CNT:要申请的次设备号数量;

    //MyAtomicLED_NAME:表示“设备名字”

    if(ret < 0)

      goto free_gpio;

    strMyAtomicLED.major = MAJOR(strMyAtomicLED.devid);

    /* 获取分配号的主设备号 */

    //输入参数strMyAtomicLED.devid为“Linux设备号”

    //将strMyAtomicLED.devid右移20位得到“主设备号”

    strMyAtomicLED.minor = MINOR(strMyAtomicLED.devid);

    /* 获取分配号的次设备号 */

    //输入参数strMyAtomicLED.devid为“Linux设备号”

    //将strMyAtomicLED.devid与0xFFFFF相与后得到“次设备号”

  }

  /*3、注册字符设备*/

  strMyAtomicLED.cdev.owner = THIS_MODULE;

  //使用THIS_MODULE将owner指针指向当前这个模块

  cdev_init(&strMyAtomicLED.cdev,&MyAtomicLED_fops);

  //注册字符设备,初始化“字符设备结构变量strMyAtomicLED.cdev”

  //strMyAtomicLED.cdev是等待初始化的结构体变量

  //MyAtomicLED_fops就是字符设备文件操作函数集合

  /*4、添加字符设备*/

  ret=cdev_add(&strMyAtomicLED.cdev,strMyAtomicLED.devid,MyAtomicLED_CNT);

  //添加字符设备

  /*&strMyAtomicLED.cdev表示指向要添加的字符设备,即字符设备结构strMyAtomicLED.cdev变量*/

  //strMyAtomicLED.devid表示设备号

  //MyAtomicLED_CNT表示需要添加的设备数量

  if(ret < 0 ) //添加字符设备失败

    goto del_register;

  printk("dev id major = %d,minor = %d\r\n", strMyAtomicLED.major, strMyAtomicLED.minor);

  printk("MyAtomicLED_init is ok!!!\r\n");

  /*5、自动创建设备节点 */

  strMyAtomicLED.class =class_create(THIS_MODULE, MyAtomicLED_NAME);

  if (IS_ERR(strMyAtomicLED.class)){

    goto del_cdev;

  }

  /*6、创建设备 */

  strMyAtomicLED.device = device_create(strMyAtomicLED.class, NULL, strMyAtomicLED.devid, NULL, MyAtomicLED_NAME);

  //创建设备

  //设备要创建在strMyAtomicLED.class类下面

  //NULL表示没有父设备

  //strMyAtomicLED.devid是设备号;

  //参数drvdata=NULL,设备没有使用数据

  //MyAtomicLED_NAME是设备名字

  //如果设置fmt=MyAtomicLED_NAME 的话,就会生成/dev/MyAtomicLED_NAME设备文件。

  //返回值就是创建好的设备。

  if (IS_ERR(strMyAtomicLED.device)){

    goto destroy_class;

  }

  return 0;

destroy_class:

  class_destroy(strMyAtomicLED.class);

  //删除类

  //strMyAtomicLED.class就是要删除的类

del_cdev:

   cdev_del(&strMyAtomicLED.cdev);

   //删除字符设备

   //&strMyAtomicLED.cdev表示指向需要删除的字符设备,即字符设备结构strMyAtomicLED.cdev变量

del_register:

  unregister_chrdev_region(strMyAtomicLED.devid, MyAtomicLED_CNT);

  /* 释放设备号 */

  //strMyAtomicLED.devid:需要释放的起始设备号

  //MyAtomicLED_CNT:需要释放的次设备号数量

free_gpio://申请设备号失败

  /*释放gpio编号*/

  gpio_free(strMyAtomicLED.led_gpio);

  return -EIO;

}

/*驱动出口函数 */

static void __exit MyAtomicLED_exit(void)

{

  /*1、删除字符设备*/

  cdev_del(&strMyAtomicLED.cdev);

  /*删除字符设备*/

  /*&strMyAtomicLED.cdev表示指向需要删除的字符设备,即字符设备结构&strMyAtomicLED.cdev变量*/

  /*2、 释放设备号 */

  unregister_chrdev_region(strMyAtomicLED.devid, MyAtomicLED_CNT);

  /*释放设备号 */

  //strMyAtomicLED.devid:需要释放的起始设备号

  //MyAtomicLED_CNT:需要释放的次设备号数;

  /*3、 删除设备 */

  device_destroy(strMyAtomicLED.class, strMyAtomicLED.devid);

  //删除创建的设备

  //strMyAtomicLED.class是要删除的设备所处的类

  //strMyAtomicLED.devid是要删除的设备号

  

  /*4、删除类*/

  class_destroy(strMyAtomicLED.class);

  //删除类

  //strMyAtomicLED.class就是要删除的类

  /*5、释放gpio编号*/

  gpio_free(strMyAtomicLED.led_gpio);

}

module_init(MyAtomicLED_init);

//指定MyAtomicLED_init()为驱动入口函数

module_exit(MyAtomicLED_exit);

//指定MyAtomicLED_exit()为驱动出口函数

MODULE_AUTHOR("Zhanggong");//添加作者名字

MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”

MODULE_INFO(intree,"Y");

//去除显示“loading out-of-tree module taints kernel.”

7、LED_APP.c如下:

//添加延时程序,导致关闭文件推迟,为了是演示互斥访问共享资源的效果

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

//APP运行命令:./LED_APP filename <1>|<0>如果是1表示打开LED,如果是0表示关闭LED

#define LEDOFF 0 /* 关灯 */

#define LEDON 1 /* 开灯 */

/*

参数argc: argv[]数组元素个数

参数argv[]:是一个指针数组

返回值: 0 成功;其他 失败

*/

int main(int argc, char *argv[])

{

  int fd, retvalue;

  char *filename;

  unsigned char databuf[1];

  unsigned char cnt = 0;

  if(argc != 3)

  {

    printf("Error Usage!\r\n");

    return -1;

  }

  //argv[]是指向输入参数“./LED_App” “/dev/LED” “1”

  filename = argv[1];

  //argv[1]指向字符串“/dev/LED”

  fd = open(filename, O_RDWR);

  //如果打开“/dev/LED”文件成功,则fd为“文件描述符”

  //fd=0表示标准输入流; fd=1表示标准输出流;fd=2表示错误输出流;

  if(fd < 0)

  {

    printf("Can't open file %s\r\n", filename);

    return -1;

  }

  databuf[0]= atoi(argv[2]); /* 写入的数据,是数字的,表示打开或关闭 */

  retvalue = write(fd, databuf, 1);

  //将databuf[]中前1个字节发送给用户

  //返回值大于0表示写入的字节数;

  //返回值等于0表示没有写入任何数据;

  //返回值小于0表示写入失败

  if(retvalue < 0)

  {

    printf("write file %s failed!\r\n", filename);

    close(fd);

    //fd表示要关闭的“文件描述符”

    //返回值等于0表示关闭成功

//返回值小于0表示关闭失败

printf("close file %s\r\n", filename);

    return -1;

  }

  /* 模拟占用10S LED */

//添加延时程序,导致关闭文件推迟,为了是演示互斥访问共享资源的效果

  while(1) {

    sleep(5);

    cnt++;

    printf("App running times:%d\r\n", cnt);

    if(cnt >= 2) break;

  }

  /* 关闭设备 */

  retvalue = close(fd);

  //fd表示要关闭的“文件描述符”

  //返回值等于0表示关闭成功

  //返回值小于0表示关闭失败

  if(retvalue < 0)

  {

    printf("Can't close file %s\r\n", filename);

    return -1;

  }

else printf("close file %s\r\n", filename);

  return 0;

}

8、创建Makefile

KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31

#使用“:=”将其后面的字符串赋值给KERNELDIR

CURRENT_PATH := $(shell pwd)

#采用“shell pwd”获取当前打开的路径

#使用“$(变量名)”引用“变量的值

MyAPP := LED_APP

MyAtomicLED_Module-objs = LEDInterface.o LED.o

obj-m := MyAtomicLED_Module.o

CC := arm-none-linux-gnueabihf-gcc

drv:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

app:

$(CC)  $(MyAPP).c  -o $(MyAPP)

clean:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

rm $(MyAPP)

install:

sudo cp *.ko $(MyAPP) /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f

9、添加“c_cpp_properties.json

按下“Ctrl+Shift+P”,打开VSCode控制台,然后输入“C/C++:Edit Configurations(JSON)”,打开以后会自动在“.vscode ”目录下生成一个名为“c_cpp_properties.json” 的文件。

修改c_cpp_properties.json内容如下所示:

{

    "configurations": [

        {

            "name": "Linux",

            "includePath": [

                "${workspaceFolder}/**",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31",

                "/home/zgq/linux/Linux_Drivers/MyAtomicLED",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/include",

                "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include/generated"

            ],

            "defines": [],

            "compilerPath": "/usr/bin/gcc",

            "cStandard": "gnu11",

            "cppStandard": "gnu++14",

            "intelliSenseMode": "gcc-x64"

        }

    ],

    "version": 4

}

10、编译

输入“make clean回车

输入“make drv回车

输入“make app回车

输入“make install回车

输入“ls /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -l回车”产看是存在“LED_APP和MyAtomicLED_Module.ko

11、测试

启动开发板,从网络下载程序

输入“root

输入“cd /lib/modules/5.4.31/回车

切换到“/lib/modules/5.4.31/”目录

注意:“lib/modules/5.4.31/在虚拟机中是位于“/home/zgq/linux/nfs/rootfs/”目录下,但在开发板中,却是位于根目录中

输入“ls -l”查看“MyAtomicLED_Module.ko和LED_APP”是否存在

输入“depmod”,驱动在第一次执行时,需要运行“depmod”

输入“modprobe MyAtomicLED_Module.ko”,加载“MyAtomicLED_Module.ko”模块

输入“lsmod”查看有哪些驱动在工作

输入“ls /dev/MyAtomicLEDName -l回车”,发现节点文件“/dev/MyAtomicLEDName

输入“./LED_APP /dev/MyAtomicLEDName 1&回车”执行开灯

注意:“ &”表示在后台运行LED_APP这个软件

等待出现“close file /dev/MyAtomicLEDName”按下“回车键”。

注意:如果没有出现“close file /dev/MyAtomicLEDName”,就急着输入“./LED_APP /dev/MyAtomicLEDName 0回车”,就会提示“Can't open file /dev/MyAtomicLEDName”,这就避免线程竞争访问贡献资源

输入“./LED_APP /dev/MyAtomicLEDName 0回车”执行关灯

输入“rmmod MyAtomicLED_Module.ko”,卸载“MyAtomicLED_Module.ko”模块

注意:输入“rmmod MyAtomicLED_Module”也可以卸载“MyAtomicLED_Module.ko”模块

输入“lsmod”查看有哪些驱动在工作。

输入“ls /dev/MyAtomicLEDName -l回车”,查询节点文件“/dev/MyAtomicLEDName”是否存在

相关文章:

Linux第78步_使用原子整型操作来实现“互斥访问”共享资源

使用原子操作来实现“互斥访问”LED灯设备&#xff0c;目的是每次只允许一个应用程序使用LED灯。 1、创建MyAtomicLED目录 输入“cd /home/zgq/linux/Linux_Drivers/回车” 切换到“/home/zgq/linux/Linux_Drivers/”目录 输入“mkdir MyAtomicLED回车”&#xff0c;创建MyA…...

C++——C++11(3)

C——C11&#xff08;3&#xff09; lambda表达式&#xff08;匿名的仿函数对象&#xff09;一些注意点lambda捕捉列表[][&][this] lambda的赋值 function包装器function成员函数的包装 bind绑定参数 我们今天接着来了解一下C11一些新的特性&#xff0c;如果还没有看过上两…...

更改el-tabs默认样式,实现tab标签居中显示,标签对应内容使用另一个div显示

首先看效果图 如图所示&#xff0c;标签在浏览器窗口居中&#xff0c;但是下面的内容依然是默认从左到右&#xff0c;不会受到tab样式的影响 <template><div><div style"display: flex; justify-content: center; align-items: center;"><el-…...

微信小程序原生<map>地图实现标记多个位置以及map 组件 callout 自定义气泡

一、老规矩先上效果图: 二、在pages文件夹下新建image文件夹用来存放标记的图片。 三、代码片段 也可以参考小程序文档:https://developers.weixin.qq.com/miniprogram/dev/component/map.html index.wxml代码 <mapid="map"style="width: 100%; height:1…...

外包干了3天,技术明显进步。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入南京某软件公司&#xff0c;干了接近2年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…...

Transformer学习笔记(二)

一、文本嵌入层Embedding 1、作用&#xff1a; 无论是源文本嵌入还是目标文本嵌入&#xff0c;都是为了将文本中词汇的数字表示转变为向量表示&#xff0c;希望在这样的高维空间捕捉词汇间的关系。 二、位置编码器Positional Encoding 1、作用&#xff1a; 因为在Transformer…...

C#求水仙花数

目录 1.何谓水仙花数 2.求三位数的水仙花数 3.在遍历中使用Math.DivRem方法再求水仙花数 1.何谓水仙花数 水仙花数&#xff08;Narcissistic number&#xff09;是指一个 n 位正整数&#xff0c;它的每个位上的数字的 n 次幂之和等于它本身。例如&#xff0c;153 是一个 3 …...

FFmpeg转码参数说明及视频转码示例

-b : 设置音频或者视频的转码码率 -b:v 只设置视频码率 -b:a 只设置音频码率 -ab: 只设置音频码率, 默认码率大小为: 128k bit/s -g: 设置视频GOP大小,表示I帧之间的间隔,默认为12 -ar: 设置音频采样率,默认0 -ac: 设置音频通道数量 默认0 -bf: 设置连…...

qiankun:vite/webpack项目配置

相关博文&#xff1a; https://juejin.cn/post/7216536069285429285?searchId202403091501088BACFF113F980BA3B5F3 https://www.bilibili.com/video/BV12T411q7dq/?spm_id_from333.337.search-card.all.click qiankun结构&#xff1a; 主应用base&#xff1a;vue3historyv…...

【Linux】深入了解Linux磁盘配额:限制用户磁盘空间的利器

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 前言 在多用户环境下管理磁盘空间是服务器管理中的一项重要任务。Linux提供了强大的磁盘配额功能&#xff0c;可以帮助管理员限制用户或组对文件系统…...

Kamailio Debian安装

新方法是&#xff1a; apt install -y gnupg2 wget -O- https://deb.kamailio.org/kamailiodebkey.gpg | gpg --dearmor | tee /usr/share/keyrings/kamailio.gpg 老方法是&#xff1a; apt install -y gnupg2 wget -O- http://deb.kamailio.org/kamailiodebkey.gpg | apt-key…...

web学习笔记(三十四)

目录 1.面向对象的特征 2.面向对象的继承方式 3.正则表达式 3.1如何创建正则表达式 3.2边界符 3.2[ ]方括号 3.3正则表达式中相关的方法汇总 1.面向对象的特征 封装性&#xff1a;就像是把东西放在一个密封的盒子里一样&#xff0c;只让外部使用者通过指定的接口来访…...

2024/03/16----面试中遇到的一些面试题

1.请简单的说一下IOC&#xff0c;AOP 1.1 IOC 控制反转&#xff08;IOC&#xff09;是一种设计思想&#xff0c;就是将原本在程序中需要手动创建对象&#xff0c;现在交由Spring管理创建&#xff0c;从而降低代码之间的耦合度。 IoC 最常见以及最合理的实现方式叫做依赖注入…...

【SysBench】Linux 安装 sysbench-1.20

安装目的是为了对 MySQL 8.0.x 、PostgreSQL 进行基准测试。 0、sysbench 简介 sysbench 是一个可编写脚本的多线程基准测试工具&#xff0c;基于 LuaJIT 。 它最常用于数据库基准测试&#xff0c;但也可以 用于创建任意不涉及数据库服务器的复杂工作负载。 sysbench 附带以…...

设计模式 — — 代理模式

一、是什么 代理模式&#xff08;Proxy Pattern&#xff09;是为一个对象提供一个代用品或占位符&#xff0c;以便控制对它的 生活场景&#xff1a; 租房、买房&#xff0c;比如链家等房屋中介机构&#xff0c;起到的作用就是代理 二、使用 const proxy new Proxy(target, …...

【高通camera hal bug分析】高通自带相机镜像问题

首先打了两个log&#xff0c;一个是开启镜像的log&#xff0c;还有一个是没有开启镜像的log&#xff0c;如果我们开启镜像以后&#xff0c;观察开启镜像log发现 , 这段代码走的没有任何问题&#xff0c;因为Flip的值等于1了。 关闭镜像log如下&#xff1a; 如果我们不开启镜像…...

EPICS和Arduino Uno之间基于串行文本协议的控制开发

Arduino Uno的串口服务程序设置如文本的串口通信协议设计以及在Arduino上的应用-CSDN博客中所示。通过在串口上发送约定的文本协议&#xff0c;它实现的功能如下&#xff1a; 实现功能&#xff1a; 读取三路0.0V~5.0V模拟量输入&#xff0c;读取端口A0~A2设置三路0.0V~5.0V的模…...

数据结构的概念大合集02(线性表)

概念大合集02 1、线性表及其逻辑结构1.1 线性表的定义1.2 线性表的基本操作 2、线性表的顺序存储结构2.1 顺序表 3、线性表的链式存储3.1 链表3.1.1 头结点&#xff08;头指针&#xff09;&#xff0c;首指针&#xff0c;尾指针&#xff0c;尾结点3.1.2 单链表3.1.3 双链表3.1.…...

CSS3DRenderer, CSS3DSprite API 使用案例demo

CSS3DRenderer, CSS3DSprite API 使用案例demo <!DOCTYPE html> <html><head><title>three.js css3d - sprites</title><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, user-scalabl…...

河马优化算法(HO)-2024年Nature子刊新算法 公式原理详解与性能测评 Matlab代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 一、种群初始化 二、河马在河流或…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...