日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux并发与竞争实验(一次只允许一个应用程序操作LED灯)

發(fā)布時(shí)間:2023/12/10 linux 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux并发与竞争实验(一次只允许一个应用程序操作LED灯) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 原子操作實(shí)驗(yàn)
    • 實(shí)驗(yàn)程序編寫
    • 運(yùn)行測(cè)試(運(yùn)行多個(gè)APP搶占資源)
  • 自旋鎖實(shí)驗(yàn)
    • 實(shí)驗(yàn)程序編寫
    • 運(yùn)行測(cè)試
  • 信號(hào)量實(shí)驗(yàn)
    • 實(shí)驗(yàn)程序編寫
    • 運(yùn)行測(cè)試(第二條命令因?yàn)楂@取信號(hào)量失敗而進(jìn)入休眠狀態(tài))
  • 互斥體實(shí)驗(yàn)(類似二值信號(hào)量,會(huì)休眠)
    • 實(shí)驗(yàn)程序編寫
    • 運(yùn)行測(cè)試

在上一章中我們學(xué)習(xí)了Linux 下的并發(fā)與競(jìng)爭(zhēng),并且學(xué)習(xí)了四種常用的處理并發(fā)和競(jìng)爭(zhēng)的機(jī)制:原子操作、自旋鎖、信號(hào)量和互斥體。本章我們就通過四個(gè)實(shí)驗(yàn)來學(xué)習(xí)如何在驅(qū)動(dòng)中使用這四種機(jī)制。

原子操作實(shí)驗(yàn)

本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開發(fā)板光盤-> 2、Linux 驅(qū)動(dòng)例程-> 7_atomic。
本例程我們?cè)诘谒氖逭碌膅pioled.c 文件基礎(chǔ)上完成。在本節(jié)使用中我們使用原子操作來實(shí)現(xiàn)對(duì)LED 這個(gè)設(shè)備的互斥訪問,也就是一次只允許一個(gè)應(yīng)用程序可以使用LED 燈。

實(shí)驗(yàn)程序編寫

1、修改設(shè)備樹文件
因?yàn)楸菊聦?shí)驗(yàn)是在第四十五章實(shí)驗(yàn)的基礎(chǔ)上完成的,因此不需要對(duì)設(shè)備樹做任何的修改。

2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第四十五章實(shí)驗(yàn)驅(qū)動(dòng)文件gpioled.c 的基礎(chǔ)上修改而來。新建名為“7_atomic”的文件夾,然后在7_atomic 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“atomic”。將5_gpioled實(shí)驗(yàn)中的gpioled.c 復(fù)制到7_atomic 文件夾中,并且重命名為atomic.c。本節(jié)實(shí)驗(yàn)重點(diǎn)就是使用atomic 來實(shí)現(xiàn)一次只能允許一個(gè)應(yīng)用訪問LED,所以我們只需要在atomic.c 文件源碼的基礎(chǔ)上加上添加atomic 相關(guān)代碼即可,完成以后的atomic.c 文件內(nèi)容如下所示:

#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 <linux/of_gpio.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : atomic.c 作者 : 左忠凱 版本 : V1.0 描述 : 原子操作實(shí)驗(yàn),使用原子變量來實(shí)現(xiàn)對(duì)實(shí)現(xiàn)設(shè)備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創(chuàng)建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設(shè)備號(hào)個(gè)數(shù) */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關(guān)燈 */ #define LEDON 1 /* 開燈 *//* gpioled設(shè)備結(jié)構(gòu)體 */ struct gpioled_dev{dev_t devid; /* 設(shè)備號(hào) */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設(shè)備 */int major; /* 主設(shè)備號(hào) */int minor; /* 次設(shè)備號(hào) */struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */int led_gpio; /* led所使用的GPIO編號(hào) */atomic_t lock; /* 原子變量 */ };struct gpioled_dev gpioled; /* led設(shè)備 *//** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {/* 通過判斷原子變量的值來檢查L(zhǎng)ED有沒有被別的應(yīng)用使用 */if (!atomic_dec_and_test(&gpioled.lock)) {//V減1 不能使用驅(qū)動(dòng)atomic_inc(&gpioled.lock); /* 小于0的話就加1,使其原子變量等于0 */return -EBUSY; /* LED被使用,返回忙 */}filp->private_data = &gpioled; /* 設(shè)置私有數(shù)據(jù) */return 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設(shè)備寫數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開的文件描述符* @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù)* @param - cnt : 要寫入的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態(tài)值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關(guān)閉LED燈 */}return 0; }/** @description : 關(guān)閉/釋放設(shè)備* @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {struct gpioled_dev *dev = filp->private_data;/* 關(guān)閉驅(qū)動(dòng)文件的時(shí)候釋放原子變量 就是加1 */atomic_inc(&dev->lock);return 0; }/* 設(shè)備操作函數(shù) */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化原子變量 */atomic_set(&gpioled.lock, 1); /* 原子變量初始值為1 *//* 設(shè)置LED所使用的GPIO *//* 1、獲取設(shè)備節(jié)點(diǎn):gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設(shè)備樹中的gpio屬性,得到LED所使用的LED編號(hào) */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設(shè)置GPIO1_IO03為輸出,并且輸出高電平,默認(rèn)關(guān)閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊(cè)字符設(shè)備驅(qū)動(dòng) *//* 1、創(chuàng)建設(shè)備號(hào) */if (gpioled.major) { /* 定義了設(shè)備號(hào) */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設(shè)備號(hào) */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請(qǐng)?jiān)O(shè)備號(hào) */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號(hào)的主設(shè)備號(hào) */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號(hào)的次設(shè)備號(hào) */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個(gè)cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創(chuàng)建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創(chuàng)建設(shè)備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設(shè)備驅(qū)動(dòng) */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設(shè)備號(hào) */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第42 行,原子變量lock,用來實(shí)現(xiàn)一次只能允許一個(gè)應(yīng)用訪問LED 燈,led_init 驅(qū)動(dòng)入口函數(shù)會(huì)將lock 的值設(shè)置為1。

第57~60 行,每次調(diào)用open 函數(shù)打開驅(qū)動(dòng)設(shè)備的時(shí)候先申請(qǐng)lock,如果申請(qǐng)成功的話就表示LED 燈還沒有被其他的應(yīng)用使用,如果申請(qǐng)失敗就表示LED 燈正在被其他的應(yīng)用程序使用。每次打開驅(qū)動(dòng)設(shè)備的時(shí)候先使用atomic_dec_and_test 函數(shù)將lock減1,如果atomic_dec_and_test
函數(shù)返回值為真就表示lock 當(dāng)前值為0,說明設(shè)備可以使用。如果atomic_dec_and_test 函數(shù)返回值為假,就表示lock 當(dāng)前值為負(fù)數(shù)(lock 值默認(rèn)是1),lock 值為負(fù)數(shù)的可能性只有一個(gè),那就是其他設(shè)備正在使用LED。其他設(shè)備正在使用LED 燈,那么就只能退出了,在退出之前調(diào)用函數(shù)atomic_inc 將lock 加1,因?yàn)榇藭r(shí)lock 的值被減成了負(fù)數(shù),必須要對(duì)其加1,將lock 的值變?yōu)?。

第120 行,LED 燈使用完畢,應(yīng)用程序調(diào)用close 函數(shù)關(guān)閉的驅(qū)動(dòng)文件,led_release 函數(shù)執(zhí)行,調(diào)用atomic_inc 釋放lcok,也就是將lock加1

第143 行,初始化原子變量lock,初始值設(shè)置為1,這樣每次就只允許一個(gè)應(yīng)用使用LED燈。

3、編寫測(cè)試APP
新建名為atomicApp.c 的測(cè)試APP,在里面輸入如下所示內(nèi)容:

#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : atomicApp.c 作者 : 左忠凱 版本 : V1.0 描述 : 原子變量測(cè)試APP,測(cè)試原子變量能不能實(shí)現(xiàn)一次只允許一個(gè)應(yīng)用程序使用LED。 其他 : 無 使用方法 :./atomicApp /dev/gpioled 0 關(guān)閉LED燈./atomicApp /dev/gpioled 1 打開LED燈 論壇 : www.openedv.com 日志 : 初版V1.0 2019/1/30 左忠凱創(chuàng)建 ***************************************************************/#define LEDOFF 0 #define LEDON 1/** @description : main主程序* @param - argc : argv數(shù)組元素個(gè)數(shù)* @param - argv : 具體參數(shù)* @return : 0 成功;其他 失敗*/ int main(int argc, char *argv[]) {int fd, retvalue;char *filename;unsigned char cnt = 0;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打開beep驅(qū)動(dòng) */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要執(zhí)行的操作:打開或關(guān)閉 *//* 向/dev/gpioled文件寫入數(shù)據(jù) */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}/* 模擬占用25S LED */while(1) {sleep(5);cnt++;printf("App running times:%d\r\n", cnt);if(cnt >= 5) break;}printf("App running finished!");retvalue = close(fd); /* 關(guān)閉文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0; }

atomicApp.c 中的內(nèi)容就是在第四十五章的ledAPP.c 的基礎(chǔ)上修改而來的,重點(diǎn)是加入了第63~68 行的模擬占用25 秒LED 的代碼。測(cè)試x

運(yùn)行測(cè)試(運(yùn)行多個(gè)APP搶占資源)

1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為atomic.o,Makefile 內(nèi)容如下所示:

1 KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek ...... 4 obj-m := atomic.o ...... 11 clean: 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為atomic.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“atomic.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試atomicApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc atomicApp.c -o atomicApp

編譯成功以后就會(huì)生成atomicApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的atomic.ko 和atomicApp 這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載atomic.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe atomic.ko //加載驅(qū)動(dòng)

驅(qū)動(dòng)加載成功以后就可以使用atomicApp 軟件來測(cè)試驅(qū)動(dòng)是否工作正常,輸入如下命令以后臺(tái)運(yùn)行模式打開LED 燈,“&”表示在后臺(tái)運(yùn)行atomicApp 這個(gè)軟件:

./atomicApp /dev/gpioled 1& //打開LED 燈

輸入上述命令以后觀察開發(fā)板上的紅色LED 燈是否點(diǎn)亮,然后每隔5 秒都會(huì)輸出一行“App running times ”,如圖48.1.2.1 所示:

從圖48.1.2.1 可以看出,atomicApp 運(yùn)行正常,輸出了“App running times:1”和“App running times:2”,這就是模擬25S 占用,說明atomicApp 這個(gè)軟件正在使用LED 燈。此時(shí)再輸入如下命令關(guān)閉LED 燈:

./atomicApp /dev/gpioled 0 //關(guān)閉LED 燈

輸入上述命令以后會(huì)發(fā)現(xiàn)如圖48.1.2.2 所示輸入信息:

從圖48.1.2.2 可以看出,打開/dev/gpioled 失敗!原因是在圖48.1.2.1 中運(yùn)行的atomicAPP軟件正在占用/dev/gpioled,如果再次運(yùn)行atomicApp 軟件去操作/dev/gpioled 肯定會(huì)失敗。必須等待圖48.1.2.1中的atomicApp 運(yùn)行結(jié)束,也就是25S 結(jié)束以后其他軟件才能去操作/dev/gpioled。

這個(gè)就是采用原子變量實(shí)現(xiàn)一次只能有一個(gè)應(yīng)用程序訪問LED 燈。
如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:

rmmod atomic.ko

自旋鎖實(shí)驗(yàn)

上一節(jié)我們使用原子變量實(shí)現(xiàn)了一次只能有一個(gè)應(yīng)用程序訪問LED 燈,本節(jié)我們使用自旋鎖來實(shí)現(xiàn)此功能。在使用自旋鎖之前,先回顧一下自旋鎖的使用注意事項(xiàng):

①、自旋鎖保護(hù)的臨界區(qū)要盡可能的短,因此在open 函數(shù)中申請(qǐng)自旋鎖,然后在release 函數(shù)中釋放自旋鎖的方法就不可取。我們可以使用一個(gè)變量來表示設(shè)備的使用情況,如果設(shè)備被使用了那么變量就加一,設(shè)備被釋放以后變量就減1,我們只需要使用自旋鎖保護(hù)這個(gè)變量即可。
②、考慮驅(qū)動(dòng)的兼容性,合理的選擇API 函數(shù)。

綜上所述,在本節(jié)例程中,我們通過定義一個(gè)變量dev_stats 表示設(shè)備的使用情況,dev_stats為0 的時(shí)候表示設(shè)備沒有被使用,dev_stats 大于0 的時(shí)候表示設(shè)備被使用。驅(qū)動(dòng)open 函數(shù)中先判斷dev_stats 是否為0,也就是判斷設(shè)備是否可用,如果為0 的話就使用設(shè)備,并且將dev_stats加1,表示設(shè)備被使用了。使用完以后在release 函數(shù)中將dev_stats 減1,表示設(shè)備沒有被使用了。因此真正實(shí)現(xiàn)設(shè)備互斥訪問的是變量dev_stats,但是我們要使用自旋鎖對(duì)dev_stats 來做保護(hù)。

實(shí)驗(yàn)程序編寫

1、修改設(shè)備樹文件
本章實(shí)驗(yàn)是在上一節(jié)實(shí)驗(yàn)的基礎(chǔ)上完成的,同樣不需要對(duì)設(shè)備樹做任何的修改。

2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第上一節(jié)實(shí)驗(yàn)驅(qū)動(dòng)文件atomic.c 的基礎(chǔ)上修改而來。新建名為“8_spinlock”的文件夾,然后在8_spinlock 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“spinlock”。將7_atomic實(shí)驗(yàn)中的atomic.c 復(fù)制到8_spinlock 文件夾中,并且重命名為spinlock.c。將原來使用atomic 的地方換為spinlock 即可,其他代碼不需要修改,完成以后的spinlock.c 文件內(nèi)容如下所示(有省
略):

#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 <linux/of_gpio.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : spinlock.c 作者 : 左忠凱 版本 : V1.0 描述 : 自旋鎖實(shí)驗(yàn),使用自旋鎖來實(shí)現(xiàn)對(duì)實(shí)現(xiàn)設(shè)備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創(chuàng)建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設(shè)備號(hào)個(gè)數(shù) */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關(guān)燈 */ #define LEDON 1 /* 開燈 *//* gpioled設(shè)備結(jié)構(gòu)體 */ struct gpioled_dev{dev_t devid; /* 設(shè)備號(hào) */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設(shè)備 */int major; /* 主設(shè)備號(hào) */int minor; /* 次設(shè)備號(hào) */struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */int led_gpio; /* led所使用的GPIO編號(hào) */int dev_stats; /* 設(shè)備使用狀態(tài),0,設(shè)備未使用;>0,設(shè)備已經(jīng)被使用 */spinlock_t lock; /* 自旋鎖 */ };struct gpioled_dev gpioled; /* led設(shè)備 *//** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {unsigned long flags;filp->private_data = &gpioled; /* 設(shè)置私有數(shù)據(jù) */spin_lock_irqsave(&gpioled.lock, flags); /* 上鎖 */if (gpioled.dev_stats) { /* 如果設(shè)備被使用了 大于1 */spin_unlock_irqrestore(&gpioled.lock, flags);/* 解鎖 */return -EBUSY;}gpioled.dev_stats++; /* 如果設(shè)備沒有打開,那么就標(biāo)記已經(jīng)被使用了 */spin_unlock_irqrestore(&gpioled.lock, flags);/* 解鎖 */return 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設(shè)備寫數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開的文件描述符* @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù)* @param - cnt : 要寫入的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態(tài)值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關(guān)閉LED燈 */}return 0; }/** @description : 關(guān)閉/釋放設(shè)備* @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {unsigned long flags;struct gpioled_dev *dev = filp->private_data;/* 關(guān)閉驅(qū)動(dòng)文件的時(shí)候?qū)ev_stats減1 */spin_lock_irqsave(&dev->lock, flags); /* 上鎖 */if (dev->dev_stats) {dev->dev_stats--;}spin_unlock_irqrestore(&dev->lock, flags);/* 解鎖 */return 0; }/* 設(shè)備操作函數(shù) */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅(qū)動(dòng)入口函數(shù)* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化自旋鎖 */spin_lock_init(&gpioled.lock);/* 設(shè)置LED所使用的GPIO *//* 1、獲取設(shè)備節(jié)點(diǎn):gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設(shè)備樹中的gpio屬性,得到LED所使用的LED編號(hào) */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設(shè)置GPIO1_IO03為輸出,并且輸出高電平,默認(rèn)關(guān)閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊(cè)字符設(shè)備驅(qū)動(dòng) *//* 1、創(chuàng)建設(shè)備號(hào) */if (gpioled.major) { /* 定義了設(shè)備號(hào) */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設(shè)備號(hào) */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請(qǐng)?jiān)O(shè)備號(hào) */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號(hào)的主設(shè)備號(hào) */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號(hào)的次設(shè)備號(hào) */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個(gè)cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創(chuàng)建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創(chuàng)建設(shè)備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設(shè)備驅(qū)動(dòng) */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設(shè)備號(hào) */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第43 行,dev_stats 表示設(shè)備狀態(tài),如果為0 的話表示設(shè)備還沒有被使用,如果大于0 的話就表示設(shè)備已經(jīng)被使用了。

第44 行,定義自旋鎖變量lock

第61~67 行,使用自旋鎖實(shí)現(xiàn)對(duì)設(shè)備的互斥訪問,第61 行調(diào)用spin_lock_irqsave 函數(shù)獲取鎖,為了考慮到驅(qū)動(dòng)兼容性,這里并沒有使用spin_lock 函數(shù)來獲取鎖。第62 行判斷dev_stats 是否大于0,如果是的話表示設(shè)備已經(jīng)被使用了,那么就調(diào)用spin_unlock_irqrestore函數(shù)釋放鎖,并且返回-EBUSY。如果設(shè)備沒有被使用的話就在第66 行將dev_stats 加1,表示設(shè)備要被使用了,然后調(diào)用spin_unlock_irqrestore 函數(shù)釋放鎖。
自旋鎖的工作就是保護(hù)dev_stats 變量,真正實(shí)現(xiàn)對(duì)設(shè)備互斥訪問的是變量dev_stats

第126~131 行,在release 函數(shù)中將dev_stats 減1,表示設(shè)備被釋放了,可以被其他的應(yīng)用程序使用。將dev_stats 減1 的時(shí)候需要自旋鎖對(duì)其進(jìn)行保護(hù)。

第155 行,在驅(qū)動(dòng)入口函數(shù)led_init 中調(diào)用spin_lock_init 函數(shù)初始化自旋鎖。

3、編寫測(cè)試APP

測(cè)試APP 使用48.1.1 小節(jié)中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為spinlockApp.c 即可。

運(yùn)行測(cè)試

1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為spinlock.o,Makefile 內(nèi)容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := spinlock.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為spinlock.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“spinlock.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試spinlockApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc spinlockApp.c -o spinlockApp

編譯成功以后就會(huì)生成spinlockApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的spinlock.ko 和spinlockApp 這兩個(gè)文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載spinlock.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe spinlock.ko //加載驅(qū)動(dòng)

驅(qū)動(dòng)加載成功以后就可以使用spinlockApp 軟件測(cè)試驅(qū)動(dòng)是否工作正常,測(cè)試方法和48.1.2小節(jié)中一樣,先輸入如下命令讓spinlockAPP 軟件模擬占用25S 的LED 燈:

./spinlockApp /dev/gpioled 1& //打開LED 燈

緊接著再輸入如下命令關(guān)閉LED 燈:

./spinlockApp /dev/gpioled 0 //關(guān)閉LED 燈

看一下能不能關(guān)閉LED 燈,驅(qū)動(dòng)正常工作的話并不會(huì)馬上關(guān)閉LED 燈,會(huì)提示你“file /dev/gpioled open failed!”,必須等待第一個(gè)spinlockApp 軟件運(yùn)行完成(25S 計(jì)時(shí)結(jié)束)才可以再次操作LED 燈。

如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:

rmmod spinlock.ko

信號(hào)量實(shí)驗(yàn)

本節(jié)我們來使用信號(hào)量實(shí)現(xiàn)了一次只能有一個(gè)應(yīng)用程序訪問LED 燈,信號(hào)量可以導(dǎo)致休眠,因此信號(hào)量保護(hù)的臨界區(qū)沒有運(yùn)行時(shí)間限制,可以在驅(qū)動(dòng)的open 函數(shù)申請(qǐng)信號(hào)量,然后在release 函數(shù)中釋放信號(hào)量。但是信號(hào)量不能用在中斷中,本節(jié)實(shí)驗(yàn)我們不會(huì)在中斷中使用信號(hào)量。

實(shí)驗(yàn)程序編寫

1、修改設(shè)備樹文件
本章實(shí)驗(yàn)是在上一節(jié)實(shí)驗(yàn)的基礎(chǔ)上完成的,同樣不需要對(duì)設(shè)備樹做任何的修改。

2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第上一節(jié)實(shí)驗(yàn)驅(qū)動(dòng)文件spinlock.c 的基礎(chǔ)上修改而來。新建名為“9_semaphore”的文件夾,然后在9_semaphore 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“semaphore”。將8_spinlock 實(shí)驗(yàn)中的spinlock.c 復(fù)制到9_semaphore 文件夾中,并且重命名為semaphore.c。將原來使用到自旋鎖的地方換為信號(hào)量即可,其他的內(nèi)容基本不變,完成以后的semaphore.c 文件內(nèi)容如下所示(有省略):

#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 <linux/of_gpio.h> #include <linux/semaphore.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : semaphore.c 作者 : 左忠凱 版本 : V1.0 描述 : 信號(hào)量實(shí)驗(yàn),使用信號(hào)量來實(shí)現(xiàn)對(duì)實(shí)現(xiàn)設(shè)備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創(chuàng)建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設(shè)備號(hào)個(gè)數(shù) */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關(guān)燈 */ #define LEDON 1 /* 開燈 *//* gpioled設(shè)備結(jié)構(gòu)體 */ struct gpioled_dev{dev_t devid; /* 設(shè)備號(hào) */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設(shè)備 */int major; /* 主設(shè)備號(hào) */int minor; /* 次設(shè)備號(hào) */struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */int led_gpio; /* led所使用的GPIO編號(hào) */struct semaphore sem; /* 信號(hào)量 */ };struct gpioled_dev gpioled; /* led設(shè)備 *//** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {filp->private_data = &gpioled; /* 設(shè)置私有數(shù)據(jù) *//* 獲取信號(hào)量 */if (down_interruptible(&gpioled.sem)) { /* 獲取信號(hào)量,進(jìn)入休眠狀態(tài)的進(jìn)程可以被信號(hào)打斷 */return -ERESTARTSYS;} #if 0down(&gpioled.sem); /* 不能被信號(hào)打斷 */ #endifreturn 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設(shè)備寫數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開的文件描述符* @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù)* @param - cnt : 要寫入的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態(tài)值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關(guān)閉LED燈 */}return 0; }/** @description : 關(guān)閉/釋放設(shè)備* @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {struct gpioled_dev *dev = filp->private_data;up(&dev->sem); /* 釋放信號(hào)量,信號(hào)量值加1 */return 0; }/* 設(shè)備操作函數(shù) */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅(qū)動(dòng)入口函數(shù)* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化信號(hào)量 */sema_init(&gpioled.sem, 1);/* 設(shè)置LED所使用的GPIO *//* 1、獲取設(shè)備節(jié)點(diǎn):gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設(shè)備樹中的gpio屬性,得到LED所使用的LED編號(hào) */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設(shè)置GPIO1_IO03為輸出,并且輸出高電平,默認(rèn)關(guān)閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊(cè)字符設(shè)備驅(qū)動(dòng) *//* 1、創(chuàng)建設(shè)備號(hào) */if (gpioled.major) { /* 定義了設(shè)備號(hào) */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設(shè)備號(hào) */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請(qǐng)?jiān)O(shè)備號(hào) */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號(hào)的主設(shè)備號(hào) */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號(hào)的次設(shè)備號(hào) */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個(gè)cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創(chuàng)建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創(chuàng)建設(shè)備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設(shè)備驅(qū)動(dòng) */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設(shè)備號(hào) */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第14 行,要使用信號(hào)量必須添加<linux/semaphore.h>頭文件。

第43 行,在設(shè)備結(jié)構(gòu)體中添加一個(gè)信號(hào)量成員變量sem

第60~65 行,在open 函數(shù)中申請(qǐng)信號(hào)量,可以使用down 函數(shù),也可以使用down_interruptible函數(shù)。如果信號(hào)量值大于等于1 就表示可用,那么應(yīng)用程序就會(huì)開始使用LED 燈。如果信號(hào)量值為0 就表示應(yīng)用程序不能使用LED 燈,此時(shí)應(yīng)用程序就會(huì)進(jìn)入到休眠狀態(tài)。等到信號(hào)量值大于1 的時(shí)候應(yīng)用程序就會(huì)喚醒,申請(qǐng)信號(hào)量,獲取LED 燈使用權(quán)。

第123 行,在release 函數(shù)中調(diào)用up 函數(shù)釋放信號(hào)量,這樣其他因?yàn)闆]有得到信號(hào)量而進(jìn)入休眠狀態(tài)的應(yīng)用程序就會(huì)喚醒,獲取信號(hào)量。

第147 行,在驅(qū)動(dòng)入口函數(shù)中調(diào)用sema_init 函數(shù)初始化信號(hào)量sem 的值為1,相當(dāng)于sem是個(gè)二值信號(hào)量。

總結(jié)一下,當(dāng)信號(hào)量sem 為1 的時(shí)候表示LED 燈還沒有被使用,如果應(yīng)用程序A 要使用LED 燈,先調(diào)用open 函數(shù)打開/dev/gpioled,這個(gè)時(shí)候會(huì)獲取信號(hào)量sem,獲取成功以后sem 的值減1 變?yōu)?。如果此時(shí)應(yīng)用程序B 也要使用LED 燈,調(diào)用open 函數(shù)打開/dev/gpioled 就會(huì)因?yàn)樾盘?hào)量無效(值為0)而進(jìn)入休眠狀態(tài)。當(dāng)應(yīng)用程序A 運(yùn)行完畢,調(diào)用close 函數(shù)關(guān)閉/dev/gpioled
的時(shí)候就會(huì)釋放信號(hào)量sem,此時(shí)信號(hào)量sem 的值就會(huì)加1,變?yōu)?。信號(hào)量sem 再次有效,表示其他應(yīng)用程序可以使用LED 燈了,此時(shí)在休眠狀態(tài)的應(yīng)用程序B 就會(huì)獲取到信號(hào)量sem,獲取成功以后就開始使用LED 燈。

3、編寫測(cè)試APP
測(cè)試APP 使用48.1.1 小節(jié)中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為semaApp.c 即可。

運(yùn)行測(cè)試(第二條命令因?yàn)楂@取信號(hào)量失敗而進(jìn)入休眠狀態(tài))

1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為semaphore.o,Makefile 內(nèi)容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := semaphore.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為semaphore.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“semaphore.ko”的驅(qū)動(dòng)模塊文件。
2、編譯測(cè)試APP

輸入如下命令編譯測(cè)試semaApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc semaApp.c -o semaApp

編譯成功以后就會(huì)生成semaApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的semaphore.ko 和semaApp 這兩個(gè)文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令
加載semaphore.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe semaphore.ko //加載驅(qū)動(dòng)

驅(qū)動(dòng)加載成功以后就可以使用semaApp 軟件測(cè)試驅(qū)動(dòng)是否工作正常,測(cè)試方法和48.1.2 小
節(jié)中一樣,先輸入如下命令讓semaApp 軟件模擬占用25S 的LED 燈:

./ semaApp /dev/gpioled 1& //打開LED 燈

緊接著再輸入如下命令關(guān)閉LED 燈:

./ semaApp /dev/gpioled 0& //關(guān)閉LED 燈

注意兩個(gè)命令都是運(yùn)行在后臺(tái),第一條命令先獲取到信號(hào)量,因此可以操作LED 燈,將LED 燈打開,并且占有25S。
第二條命令因?yàn)楂@取信號(hào)量失敗而進(jìn)入休眠狀態(tài),等待第一條命令運(yùn)行完畢并釋放信號(hào)量以后才擁有LED 燈使用權(quán),將LED 燈關(guān)閉,運(yùn)行結(jié)果如圖48.3.2.1 所示:

如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:

rmmod semaphore.ko

互斥體實(shí)驗(yàn)(類似二值信號(hào)量,會(huì)休眠)

前面我們使用原子操作、自旋鎖和信號(hào)量實(shí)現(xiàn)了對(duì)LED 燈的互斥訪問,但是最適合互斥的就是互斥體mutex 了。本節(jié)我們來學(xué)習(xí)一下如何使用mutex 實(shí)現(xiàn)對(duì)LED 燈的互斥訪問。

實(shí)驗(yàn)程序編寫

1、修改設(shè)備樹文件
本章實(shí)驗(yàn)是在上一節(jié)實(shí)驗(yàn)的基礎(chǔ)上完成的,同樣不需要對(duì)設(shè)備樹做任何的修改。
2、LED 驅(qū)動(dòng)修改
本節(jié)實(shí)驗(yàn)在第上一節(jié)實(shí)驗(yàn)驅(qū)動(dòng)文件semaphore.c 的基礎(chǔ)上修改而來。新建名為“10_mutex”的文件夾,然后在10_mutex 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“mutex”。將9_semaphore實(shí)驗(yàn)中的semaphore.c 復(fù)制到10_mutex 文件夾中,并且重命名為mutex.c。將原來使用到信號(hào)量的地方換為mutex 即可,其他的內(nèi)容基本不變,完成以后的mutex.c 文件內(nèi)容如下所示(有省略):

#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 <linux/of_gpio.h> #include <linux/semaphore.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : mutex.c 作者 : 左忠凱 版本 : V1.0 描述 : 互斥體實(shí)驗(yàn),使用互斥體來實(shí)現(xiàn)對(duì)實(shí)現(xiàn)設(shè)備的互斥訪問 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/7/18 左忠凱創(chuàng)建 ***************************************************************/ #define GPIOLED_CNT 1 /* 設(shè)備號(hào)個(gè)數(shù) */ #define GPIOLED_NAME "gpioled" /* 名字 */ #define LEDOFF 0 /* 關(guān)燈 */ #define LEDON 1 /* 開燈 *//* gpioled設(shè)備結(jié)構(gòu)體 */ struct gpioled_dev{dev_t devid; /* 設(shè)備號(hào) */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設(shè)備 */int major; /* 主設(shè)備號(hào) */int minor; /* 次設(shè)備號(hào) */struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */int led_gpio; /* led所使用的GPIO編號(hào) */struct mutex lock; /* 互斥體 */ };struct gpioled_dev gpioled; /* led設(shè)備 *//** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動(dòng)的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量* 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {filp->private_data = &gpioled; /* 設(shè)置私有數(shù)據(jù) *//* 獲取互斥體,可以被信號(hào)打斷 */if (mutex_lock_interruptible(&gpioled.lock)) {return -ERESTARTSYS;} #if 0mutex_lock(&gpioled.lock); /* 不能被信號(hào)打斷 */ #endifreturn 0; }/** @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符)* @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū)* @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗*/ static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {return 0; }/** @description : 向設(shè)備寫數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開的文件描述符* @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù)* @param - cnt : 要寫入的數(shù)據(jù)長(zhǎng)度* @param - offt : 相對(duì)于文件首地址的偏移* @return : 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 獲取狀態(tài)值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開LED燈 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1); /* 關(guān)閉LED燈 */}return 0; }/** @description : 關(guān)閉/釋放設(shè)備* @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符)* @return : 0 成功;其他 失敗*/ static int led_release(struct inode *inode, struct file *filp) {struct gpioled_dev *dev = filp->private_data;/* 釋放互斥鎖 */mutex_unlock(&dev->lock);return 0; }/* 設(shè)備操作函數(shù) */ static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release, };/** @description : 驅(qū)動(dòng)入口函數(shù)* @param : 無* @return : 無*/ static int __init led_init(void) {int ret = 0;/* 初始化互斥體 */mutex_init(&gpioled.lock);/* 設(shè)置LED所使用的GPIO *//* 1、獲取設(shè)備節(jié)點(diǎn):gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 獲取設(shè)備樹中的gpio屬性,得到LED所使用的LED編號(hào) */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、設(shè)置GPIO1_IO03為輸出,并且輸出高電平,默認(rèn)關(guān)閉LED燈 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注冊(cè)字符設(shè)備驅(qū)動(dòng) *//* 1、創(chuàng)建設(shè)備號(hào) */if (gpioled.major) { /* 定義了設(shè)備號(hào) */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else { /* 沒有定義設(shè)備號(hào) */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申請(qǐng)?jiān)O(shè)備號(hào) */gpioled.major = MAJOR(gpioled.devid); /* 獲取分配號(hào)的主設(shè)備號(hào) */gpioled.minor = MINOR(gpioled.devid); /* 獲取分配號(hào)的次設(shè)備號(hào) */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一個(gè)cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、創(chuàng)建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、創(chuàng)建設(shè)備 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0; }/** @description : 驅(qū)動(dòng)出口函數(shù)* @param : 無* @return : 無*/ static void __exit led_exit(void) {/* 注銷字符設(shè)備驅(qū)動(dòng) */cdev_del(&gpioled.cdev);/* 刪除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注銷設(shè)備號(hào) */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第43 行,定義互斥體lock。
第60~65 行,在open 函數(shù)中調(diào)用mutex_lock_interruptible 或者mutex_lock 獲取mutex,成功的話就表示可以使用LED 燈,失敗的話就會(huì)進(jìn)入休眠狀態(tài),和信號(hào)量一樣。
第124 行,在release 函數(shù)中調(diào)用mutex_unlock 函數(shù)釋放mutex,這樣其他應(yīng)用程序就可以獲取mutex 了。
第148 行,在驅(qū)動(dòng)入口函數(shù)中調(diào)用mutex_init 初始化mutex。

互斥體和二值信號(hào)量類似,只不過互斥體是專門用于互斥訪問的。

3、編寫測(cè)試APP
測(cè)試APP 使用48.1.1 小節(jié)中的atomicApp.c 即可,將7_atomic 中的atomicApp.c 文件到本例程中,并將atomicApp.c 重命名為mutexApp.c 即可。

運(yùn)行測(cè)試

1、編譯驅(qū)動(dòng)程序
編寫Makefile 文件,本章實(shí)驗(yàn)的Makefile 文件和第四十章實(shí)驗(yàn)基本一樣,只是將obj-m 變量的值改為mutex.o,Makefile 內(nèi)容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := mutex.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設(shè)置obj-m 變量的值為mutex.o。
輸入如下命令編譯出驅(qū)動(dòng)模塊文件:

make -j32

編譯成功以后就會(huì)生成一個(gè)名為“mutex.ko”的驅(qū)動(dòng)模塊文件。

2、編譯測(cè)試APP
輸入如下命令編譯測(cè)試mutexApp.c 這個(gè)測(cè)試程序:

arm-linux-gnueabihf-gcc mutexApp.c -o mutexApp

編譯成功以后就會(huì)生成mutexApp 這個(gè)應(yīng)用程序。
3、運(yùn)行測(cè)試
將上一小節(jié)編譯出來的mutex.ko 和mutexApp 這兩個(gè)文件拷貝到rootfs/lib/modules/4.1.15目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/4.1.15 中,輸入如下命令加載mutex.ko 驅(qū)動(dòng)模塊:

depmod //第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令 modprobe mutex.ko //加載驅(qū)動(dòng)

驅(qū)動(dòng)加載成功以后就可以使用mutexApp 軟件測(cè)試驅(qū)動(dòng)是否工作正常,測(cè)試方法和48.3.2中測(cè)試信號(hào)量的方法一樣。

如果要卸載驅(qū)動(dòng)的話輸入如下命令即可:

rmmod mutex.ko

總結(jié)

以上是生活随笔為你收集整理的Linux并发与竞争实验(一次只允许一个应用程序操作LED灯)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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

97av影院 | 亚洲不卡av一区二区三区 | 一二三区高清 | 亚洲劲爆av| 久久久久黄 | 五月天婷婷狠狠 | 国产成本人视频在线观看 | 国产精品18久久久久久不卡孕妇 | 日本高清中文字幕有码在线 | 最新一区二区三区 | 天天看天天干天天操 | 在线视频一区二区 | 国产激情免费 | 91精品无人成人www | 精品视频在线免费 | 国产91学生粉嫩喷水 | 久久久91精品国产一区二区精品 | 国产综合精品久久 | 夜夜视频欧洲 | 99久热在线精品视频成人一区 | 欧美午夜性| 亚洲免费色 | 亚洲综合丁香 | 国产精品mv在线观看 | 国产精品久久中文字幕 | 午夜免费视频网站 | 久久福利综合 | 久久影视中文字幕 | 亚洲国产黄色片 | 国产夫妻性生活自拍 | 中文字幕久久精品亚洲乱码 | 久久免费视频这里只有精品 | 亚洲国产三级在线观看 | 欧美午夜剧场 | 成人免费在线播放 | 男女免费视频观看 | 国产一级久久 | 国产成人精品一区二区三区免费 | 欧美久久久一区二区三区 | 黄色的视频网站 | 亚洲网站在线看 | 天天天色综合a | 亚洲一区二区精品 | 日韩在线电影一区二区 | 久久国产美女 | 欧美日韩免费观看一区二区三区 | 欧美十八 | 久久网站最新地址 | 婷婷精品| 亚洲人精品午夜 | 97自拍超碰 | 中文字幕在线一二 | 在线亚洲精品 | 成年人在线看视频 | 人人揉人人揉人人揉人人揉97 | 婷婷av电影 | 狠狠狠色丁香综合久久天下网 | 日本韩国中文字幕 | 狠狠色狠狠色综合日日92 | 国产精品嫩草影院99网站 | 天天干天天看 | 色综合国产 | 911精品视频 | 日韩高清免费电影 | 亚洲精品视频二区 | 激情综合啪 | 日韩午夜大片 | 亚洲视频免费在线 | 999视频在线观看 | 日本黄色a级大片 | 久久1区 | 婷婷av色综合 | 天天艹日日干 | 日韩伦理一区二区三区av在线 | 天天躁天天躁天天躁婷 | 高清有码中文字幕 | 视频直播国产精品 | 97品白浆高清久久久久久 | 丰满少妇对白在线偷拍 | 色在线视频网 | avcom在线 | 国产精品网在线观看 | 日韩在线视频线视频免费网站 | 久久免费福利视频 | 少妇资源站 | 亚洲精品国偷拍自产在线观看蜜桃 | av天天在线观看 | 欧美日韩性视频在线 | 在线免费观看黄色 | 夜夜操天天操 | 久久这里有 | 免费日韩三级 | 国产精品成人国产乱一区 | 中午字幕在线观看 | 玖玖精品在线 | 成人在线一区二区 | 在线观看中文 | 欧美日韩中文国产一区发布 | 国产一级小视频 | 中国一级特黄毛片大片久久 | 91视频在线免费 | 夜夜操综合网 | av电影一区二区三区 | 久久人人爽人人爽 | av电影免费在线看 | 深夜免费福利在线 | 亚洲不卡在线 | 亚洲综合色播 | 一区二区三区在线视频观看58 | 日本性动态图 | 日日弄天天弄美女bbbb | 中文字幕之中文字幕 | 麻豆91在线播放 | 成人wwwxxx视频| 超碰人人草 | 久久久国产一区二区三区 | 日韩美精品视频 | 去看片| 五月天久久精品 | 国产成人在线免费观看 | 国产成人久久久久 | 亚洲每日更新 | 手机av资源| 91麻豆精品国产午夜天堂 | av福利在线导航 | 一级成人免费视频 | 91av社区 | 中文av日韩 | 69av在线视频| 日韩高清网站 | 五月婷婷久久综合 | 成人国产精品久久久春色 | 九九热在线观看视频 | 在线视频99 | 国产精品日韩高清 | 啪啪凸凸 | 美女网站一区 | 久久国产精品99久久久久久进口 | 最新av在线网站 | 91人人澡人人爽 | 日日草av| 九九在线精品视频 | 欧美一级免费片 | 深爱五月网 | 福利网址在线观看 | 国产1区2区3区精品美女 | 日韩在线观看不卡 | 久久不卡国产精品一区二区 | 中字幕视频在线永久在线观看免费 | 久久国产欧美日韩精品 | 国产一区二区不卡视频 | 99热最新精品 | 国产中文字幕久久 | 一级片免费视频 | 久草在线视频网站 | 狠狠色丁香婷婷综合视频 | 丁香六月婷 | 91香蕉视频720p | 亚洲视频六区 | 精品在线视频一区 | 婷婷av综合 | 日韩av免费一区 | 91麻豆产精品久久久久久 | 在线亚洲成人 | 狠狠狠干 | 国产精品二区三区 | 久久精品国产亚洲精品 | 久久久精品 一区二区三区 国产99视频在线观看 | 精品国产激情 | 西西44人体做爰大胆视频 | 九草视频在线观看 | 中文字幕91视频 | 婷婷色中文网 | 欧美日韩国产色综合一二三四 | 日韩av影片在线观看 | 五月天久久婷 | 亚洲v精品 | 狠狠88综合久久久久综合网 | 久久久久久电影 | 久久久久国产精品午夜一区 | 日韩欧美一区二区在线播放 | 亚洲午夜精品久久久 | 国际精品久久久 | 国产白浆在线观看 | 国产精品久久久久久久久久久久久久 | 色午夜影院| 天天鲁天天干天天射 | 91av短视频 | 狠狠综合久久av | 国产黄影院色大全免费 | 亚洲专区 国产精品 | av在线最新 | 久久国产精品一区二区三区四区 | 最近更新的中文字幕 | 96久久久| 久久中文欧美 | 免费久久网 | 国产中文字幕国产 | 免费看国产一级片 | 国产在线97| 在线看成人av | 人人插人人舔 | 97超碰福利久久精品 | 日日夜夜婷婷 | 日韩在线观看一区二区三区 | 毛片的网址 | 91av原创| 日韩在线观看不卡 | 欧美性色综合网 | 99久久久久免费精品国产 | 色婷婷午夜 | 国产精品 中文在线 | 久久精品一二三区白丝高潮 | 婷婷在线网站 | 人人爽人人搞 | 日本韩国精品一区二区在线观看 | 欧美日韩在线播放 | 麻豆精品视频 | 婷婷免费视频 | 中文字幕制服丝袜av久久 | 国产日韩精品在线 | 亚洲闷骚少妇在线观看网站 | 天堂入口网站 | 亚洲精品永久免费视频 | 97电影在线 | 深爱五月网 | 亚洲无吗视频在线 | 久久久久女人精品毛片九一 | 亚洲综合狠狠干 | 欧洲成人av | 91看片网址 | 国产一级二级视频 | 日韩a在线| 久久99爱视频 | 最近中文字幕久久 | 日韩a级黄色 | 久久久午夜影院 | 中文字幕一区二区三 | 国产精品一区二区av日韩在线 | 激情久久久久久久久久久久久久久久 | 亚洲精品久久久久久久不卡四虎 | 日本精品久久久一区二区三区 | 精品超碰 | 西西44人体做爰大胆视频 | 亚洲伦理一区二区 | 亚洲精品综合欧美二区变态 | 91视频-88av | 五月婷婷电影网 | 日日夜夜婷婷 | 日韩在线观看精品 | 国产91精品一区二区麻豆网站 | 黄在线 | 91精品视频观看 | 激情伊人五月天久久综合 | 日韩精品一区二区在线观看视频 | 久久影视网 | 91人人澡人人爽 | 91天天视频| 亚洲a在线观看 | 亚洲美女精品 | 免费在线电影网址大全 | 久久久久国产精品免费 | 天天综合日日夜夜 | 香蕉视频国产在线观看 | 中文字幕在线观看第一页 | 成年人免费电影在线观看 | 韩国一区二区在线观看 | 91在线入口 | 日日干干夜夜 | 亚洲永久av | 女人18毛片a级毛片一区二区 | 色婷婷综合久久久久 | 久久免费福利 | 91色国产 | 成人久久免费 | 精品9999| 九九热视频在线免费观看 | 国产精品96久久久久久吹潮 | 色噜噜噜| 天天操天天干天天插 | 久久久精品欧美 | 国产一级免费av | 中文字幕乱在线伦视频中文字幕乱码在线 | 国产第一页福利影院 | 国产在线精品一区二区三区 | 成人av免费在线播放 | 九九久久免费 | 国产精品亚 | 在线国产观看 | 成人黄大片 | 亚洲电影第一页av | 免费在线观看黄 | www在线观看视频 | 久久97超碰 | 亚洲精品视频大全 | 中文字幕视频三区 | 成人毛片在线观看 | 久久精品视频日本 | 一二三四精品 | 国产精品九色 | 色婷婷97| 中文字幕免费一区二区 | 午夜国产在线观看 | 亚洲一区精品二人人爽久久 | 亚洲第一区在线播放 | 中文字幕国内精品 | 黄色免费视频在线观看 | 五月婷婷色播 | 久久久久久激情 | 一区二区久久 | 成人欧美一区二区三区黑人麻豆 | 青青河边草免费 | 激情久久综合网 | 国产99精品 | www国产亚洲精品久久麻豆 | 欧美亚洲成人免费 | 啪啪精品| 欧美日韩一级久久久久久免费看 | 夜添久久精品亚洲国产精品 | 婷五月天激情 | 美女久久网站 | 免费观看www7722午夜电影 | 国产精品精品久久久久久 | 蜜臀91丨九色丨蝌蚪老版 | 久久久99精品免费观看 | 一级特黄aaa大片在线观看 | 国产一级电影网 | 天堂麻豆| 2022久久国产露脸精品国产 | 美国av片在线观看 | 91人人澡人人爽 | 亚洲一二视频 | 国产精品国产三级国产不产一地 | 天堂在线一区二区 | 国产91精品一区二区麻豆亚洲 | 国产成人精品一区二三区 | 久久久伊人网 | 一区二区三区三区在线 | 麻豆一精品传二传媒短视频 | 亚洲国产成人在线 | 99热精品视 | 亚州视频在线 | 香蕉久久久久 | 国产免费成人 | 亚洲精品乱码久久久久久按摩 | 一区视频在线 | 亚洲综合最新在线 | 日韩a在线播放 | 美女视频黄色免费 | 美女精品久久久 | 天天伊人网 | 爱爱一区 | 久久久黄视频 | 波多野结衣在线播放视频 | 91网页版免费观看 | 午夜国产一区 | 欧美爽爽爽 | 51久久夜色精品国产麻豆 | 国产精品久久久久久超碰 | 亚洲视频综合 | 亚洲色图22p| 一区二区三区在线免费观看视频 | 国产精品久久久av久久久 | 五月天综合在线 | 成人av动漫在线观看 | 中文字幕一区二区三区在线播放 | 在线观看一区二区视频 | 91精品视频播放 | 亚洲国产婷婷 | 波多野结衣在线视频免费观看 | 久久精品观看 | 国产精品国产三级国产不产一地 | 天天干天天操天天干 | 成人sm另类专区 | 一区三区视频在线观看 | 国产亚洲精品成人 | 精品综合久久久 | 精品久久久久久亚洲综合网 | 在线精品在线 | 色综合久久久 | 激情网第四色 | 日韩高清不卡一区二区三区 | 欧美精品久久久久久久久老牛影院 | 久久九九免费 | 亚洲国产偷 | 色狠狠一区二区 | 免费网站在线观看成人 | 一区二区视频在线观看免费 | 久久久久久久久久久高潮一区二区 | 激情在线五月天 | 日韩欧美高清在线 | 99中文字幕 | 天天爱天天操天天射 | 国产亚洲精品成人 | 99爱在线观看 | 精品国产一区二区三区在线 | 久久不卡免费视频 | 一区二区三区在线观看免费视频 | 色婷婷狠狠五月综合天色拍 | 成人久久影院 | 999久久久久 | 日韩精品久久中文字幕 | 久久久免费电影 | 国产在线精品国自产拍影院 | 美女在线观看网站 | 这里有精品在线视频 | 日韩免费看片 | 欧美视频一区二 | 国产手机av在线 | 91在线播 | 91精品国产综合久久久久久久 | 五月婷婷在线播放 | 成人av av在线 | 在线a亚洲视频播放在线观看 | 欧美一级日韩三级 | 国产午夜精品免费一区二区三区视频 | 久久久首页 | 日韩理论 | 午夜久久影院 | 99在线播放 | 国产91在线免费视频 | 成人免费xyz网站 | 超碰免费成人 | 免费高清无人区完整版 | 亚洲aaa毛片 | 亚洲爱爱视频 | 天天操综合网站 | 国产黄色a | 最新国产在线视频 | 久久er99热精品一区二区三区 | 丁香婷五月 | 亚洲精品在线观看的 | 超级av在线| 美女视频黄,久久 | 日韩精品一区二区在线观看 | 天天躁日日躁狠狠躁av中文 | 成人免费一区二区三区在线观看 | 91最新网址在线观看 | 日本精品视频在线 | 日韩高清毛片 | 免费污片 | 国产成人在线免费观看 | 久久亚洲人| 伊人小视频 | 亚洲精品免费视频 | 国产一区av在线 | 久久国产免费视频 | 欧美另类美少妇69xxxx | 亚洲精品国产综合久久 | 欧美a级免费视频 | 精品a在线| 亚洲夜夜网 | 热久久影视 | 午夜视频在线瓜伦 | a级国产乱理论片在线观看 伊人宗合网 | 四虎影视成人永久免费观看亚洲欧美 | 国产精品成人久久久 | 黄色a级片在线观看 | 天天av天天 | 国产v在线 | 在线激情影院一区 | 69精品| 免费看的黄色片 | 国产精品久久久av | 99热在线这里只有精品 | 一本一道久久a久久精品蜜桃 | 天天操夜操 | 亚洲精品免费观看视频 | 欧美日韩免费看 | 天天躁天天操 | 色网免费观看 | 久久久精品网 | 亚洲成年人av | 久久久久久中文字幕 | 五月婷婷久久丁香 | 99久久99久国产黄毛片 | 在线综合 亚洲 欧美在线视频 | 在线 国产 日韩 | 91热精品 | 日韩欧美一区二区三区免费观看 | 999久久久欧美日韩黑人 | 日韩专区在线观看 | 久久99国产精品二区护士 | 激情五月网站 | 狠狠躁日日躁狂躁夜夜躁av | 国产精品久久99综合免费观看尤物 | 日韩 精品 一区 国产 麻豆 | av网站在线观看播放 | 91理论片午午伦夜理片久久 | 日韩一级黄色av | 九草在线视频 | 中文字幕一区二区三区乱码不卡 | 91最新网址 | 99精品免费网 | 亚洲精品视频在 | 色综合国产 | 国产精品6999成人免费视频 | 欧美成年黄网站色视频 | 美女黄色网在线播放 | 久久兔费看a级 | 成人网色| 97久久精品午夜一区二区 | 五月婷婷亚洲 | 综合影视| 黄色片免费看 | 日日干天天爽 | 午夜精品久久久久久久99婷婷 | 久久人人干 | 中文国产字幕 | 亚洲欧美综合 | 欧美精品久久久久久久 | 91精品国产一区二区在线观看 | 国产精品午夜在线观看 | 四虎成人网 | 在线播放日韩 | 在线免费av播放 | av福利在线 | 亚洲综合激情网 | 免费瑟瑟网站 | av丁香| 免费亚洲精品视频 | 日韩最新理论电影 | 精品人妖videos欧美人妖 | 曰本三级在线 | 91亚洲在线| 日韩精品短视频 | 午夜视频在线瓜伦 | 日韩欧美一区二区三区在线观看 | 亚洲国产欧美一区二区三区丁香婷 | 久久99精品久久久久久 | 欧美男男tv网站 | 国产一级黄 | 最近中文字幕免费av | 正在播放国产一区 | 中文字幕九九 | 欧美日韩高清在线观看 | 啪啪资源| 亚洲三级黄色 | 中国黄色一级大片 | 国产成人一区二区三区影院在线 | 黄色不卡av | 永久免费在线 | 成人在线免费视频 | www.夜夜| av一区二区在线观看中文字幕 | 欧美极品少妇xbxb性爽爽视频 | 黄色小说在线免费观看 | 久久er99热精品一区二区三区 | 久久国产精品免费视频 | 啪一啪在线 | 在线观看韩日电影免费 | 中文字幕在线视频第一页 | 911精品视频 | 免费在线观看av网址 | 日韩免费精品 | 亚洲精品ww| 又污又黄网站 | 午夜久久久精品 | 超碰午夜 | 国产一区二区视频在线播放 | 久久久久亚洲精品成人网小说 | 丝袜少妇在线 | 在线免费中文字幕 | 中文字幕一区二区三区在线观看 | 99精品免费久久久久久久久日本 | 亚洲午夜大片 | 色亚洲网| 麻豆精品传媒视频 | 国产精品国产三级国产不产一地 | 国产精品va在线 | 国产成人333kkk| 久久的色| a天堂最新版中文在线地址 久久99久久精品国产 | 久久久免费看视频 | 热re99久久精品国产66热 | 五月天婷亚洲天综合网精品偷 | 日韩在线高清免费视频 | 日韩高清一区在线 | 成年人国产精品 | 99久e精品热线免费 99国产精品久久久久久久久久 | 国产又黄又猛又粗 | 亚洲精品视频免费 | 国产日韩欧美在线播放 | 日韩啪视频 | 99r在线视频 | 婷婷免费在线视频 | 国产一区成人 | 中文字幕av在线电影 | 欧美激情精品久久 | 天天在线操 | 日韩一区精品 | 亚洲视频电影在线 | 午夜久久福利视频 | 91.麻豆视频 | 久久国内精品99久久6app | 精品亚洲国产视频 | 久久爱www. | 黄色www免费 | 国产精品久久麻豆 | 久久精品久久精品 | 中文字幕免费一区 | 欧美日韩视频免费 | 三级动态视频在线观看 | 日韩精品欧美专区 | 欧美一级片在线观看视频 | 天天视频色版 | 国产成人精品一区二区三区免费 | 亚洲女人天堂成人av在线 | 亚洲精品资源在线 | 日韩在线视| 青草视频网 | 狠狠色丁香婷综合久久 | 色视频在线观看 | 日韩精品中文字幕在线 | 欧美日韩亚洲第一页 | 高清有码中文字幕 | 亚洲高清在线精品 | 99热99 | 91精品国产网站 | 人人藻人人澡人人爽 | 成人免费在线观看入口 | 91精品国产入口 | 日韩videos高潮hd | 人人玩人人添人人 | 在线 欧美 日韩 | 丁香六月久久综合狠狠色 | 有没有在线观看av | 五月婷婷毛片 | 深爱五月激情网 | 中文字幕视频播放 | 免费三及片 | 午夜精品99久久免费 | 99视频在线观看免费 | 91正在播放 | 在线免费观看黄色 | 香蕉久久久久久av成人 | 一区二区三区高清在线 | 日本最大色倩网站www | 国产在线不卡 | 欧美精品v国产精品 | 久久久久久久久免费视频 | av色一区 | 丰满少妇一级片 | 在线日韩中文字幕 | 国产成人99久久亚洲综合精品 | 在线影院 国内精品 | 91视频免费视频 | 黄色亚洲精品 | 亚洲黄色av| 麻豆国产精品一区二区三区 | 在线视频观看亚洲 | 精品国产黄色片 | 婷婷网站天天婷婷网站 | 韩国在线视频一区 | 日本中文字幕在线视频 | 国产精品一区二区免费在线观看 | 亚洲特级片 | 91理论片午午伦夜理片久久 | 欧美精品亚洲精品 | 欧美日韩午夜爽爽 | 热re99久久精品国产66热 | 在线看国产 | 麻豆影视在线观看 | 国产成人精品午夜在线播放 | japanesexxxhd奶水| 国产精品影音先锋 | 亚洲精品国产成人av在线 | 九九热精| 久草网站在线观看 | 亚洲三级影院 | 麻豆国产在线播放 | 91尤物国产尤物福利在线播放 | 天天色棕合合合合合合 | 狠狠色丁香婷婷综合最新地址 | 欧美另类v | 一区二区丝袜 | 人人爽人人爽人人片av免 | 在线精品视频免费观看 | 9999国产精品 | 99视频精品视频高清免费 | 一级性生活片 | 欧美日韩国产一区二区三区在线观看 | 蜜臀av性久久久久av蜜臀妖精 | 91天堂素人约啪 | 欧美在线视频第一页 | 99色婷婷 | 91av在线精品| 欧美日韩久久 | 欧美激情片在线观看 | 亚洲一区网站 | 天天做天天爱天天综合网 | 九九免费精品视频在线观看 | 婷婷国产在线观看 | 日日日干 | 精品久久久久久久久久国产 | 婷婷久久一区二区三区 | 欧美一二三区在线观看 | 欧美福利视频 | 日韩一级理论片 | 一级α片免费看 | 人人爽人人香蕉 | 超级碰99| 久草影视在线观看 | 久久精品一区二区三 | 久久永久免费视频 | 国产视频精选在线 | 久久涩视频 | 亚洲一级二级 | 国产一区二区三区高清播放 | 午夜视频在线观看一区 | 日操操 | 麻豆一区在线观看 | 成年人免费av | 天天干天天操天天爱 | 国产黄色片免费看 | 久久久福利视频 | 国产精品va视频 | 久久激情视频 久久 | 夜色成人网 | 黄色软件视频大全免费下载 | 日日干av| 99久高清在线观看视频99精品热在线观看视频 | 亚洲精品高清在线 | 精品一区 在线 | 亚洲九九影院 | 欧美性高跟鞋xxxxhd | 日日射av | 黄色免费大片 | 中文字幕在线观看免费 | 毛片www | 成人久久 | a在线播放 | 在线观看视频国产 | 日韩三区在线观看 | 午夜美女福利 | 精品亚洲免费视频 | 91丨九色丨高潮 | 精品人妖videos欧美人妖 | 中文字幕在线乱 | 最近中文字幕在线中文高清版 | 欧美综合国产 | 亚洲丝袜一区二区 | 三级黄色片在线观看 | 久草网在线视频 | 久久久久这里只有精品 | 久操视频在线免费看 | av黄色大片| 久草青青在线观看 | 成人免费视频网站 | 久久免费的精品国产v∧ | 日韩精品黄 | 狠狠狠色丁香婷婷综合久久五月 | 亚洲va欧美va国产va黑人 | 久久久伊人网 | 国产99区 | 探花视频在线版播放免费观看 | 天天射天天干 | 香蕉网址| 综合天堂av久久久久久久 | 一级一片免费看 | 免费在线观看污 | 成人黄色在线播放 | 国产精品青草综合久久久久99 | 欧美男同网站 | 中文字幕免费不卡视频 | 日韩精品免费一线在线观看 | 免费观看一级一片 | 黄色特级一级片 | 欧美孕交vivoestv另类 | 久久调教视频 | 亚洲理论片 | 伊人激情网 | 九七视频在线观看 | 国产二区免费视频 | 六月婷操 | 日日夜夜操操操操 | 超碰在线97免费 | 久草免费看| 操久在线 | 久久精品电影网 | 亚洲精品视频在线播放 | 激情综合网五月婷婷 | 色综合天天天天做夜夜夜夜做 | 99re国产视频 | 日韩黄在线观看 | 黄色精品一区 | 国产视频日韩视频欧美视频 | 在线观看岛国 | 日韩午夜小视频 | 最近中文字幕在线播放 | 亚洲精品国偷拍自产在线观看蜜桃 | 天天综合天天综合 | 亚洲资源片| 亚洲日本在线视频观看 | 日韩视频一区二区在线 | 国产午夜精品一区二区三区欧美 | 在线中文字幕av观看 | 久久情爱| 一区二区欧美在线观看 | 久久精品国产精品亚洲 | 国产精品免费久久久久久久久久中文 | 婷婷午夜激情 | 免费看黄在线网站 | 国产 色| 国产电影黄色av | 国产在线观看午夜 | 国产精品日韩欧美一区二区 | 免费观看www7722午夜电影 | 久久成人国产精品入口 | 久久久久久久久久免费视频 | 亚洲视频综合在线 | 69av视频在线观看 | 粉嫩av一区二区三区四区在线观看 | 日韩三级视频 | 精品国产免费一区二区三区五区 | 国产精品久久三 | 狠狠躁日日躁狂躁夜夜躁av | 国产精品久久久久9999吃药 | 欧美日韩精品在线观看视频 | 夜夜夜夜夜夜操 | 在线免费性生活片 | 成 人 黄 色视频免费播放 | 色综合a | 天堂av在线7 | 天天天天色射综合 | 在线中文日韩 | 日日日日干 | 日本中文字幕网 | 精品国产精品国产偷麻豆 | 久久经典国产 | 九九热国产视频 | www.超碰| 中文字幕亚洲精品在线观看 | 岛国精品一区二区 | 99视频导航 | 国产精品不卡在线观看 | 欧美男男tv网站 | 免费一级毛毛片 | 成年人在线观看网站 | 日韩伦理片一区二区三区 | 超级碰碰碰碰 | 韩日av在线 | 久久精品久久精品久久 | freejavvideo日本免费 | 久久亚洲欧美日韩精品专区 | 青青草国产在线 | 国产伦理久久精品久久久久_ | 欧美日韩一级久久久久久免费看 | 在线观看播放av | 久久99九九99精品 | 成 人 黄 色 片 在线播放 | 成人宗合网 | www.夜夜| 国产一级视频免费看 | 91精品一区国产高清在线gif | 99精品免费视频 | 九九热免费精品视频 | 日韩免费中文字幕 | 日本不卡123区 | 又爽又黄在线观看 | 四虎国产精品免费观看视频优播 | 欧美日韩国产在线 | 日韩中文在线观看 | 国产免费又粗又猛又爽 | 五月天网站在线 | 成年人免费在线观看 | 国产一级免费在线观看 | 免费在线观看av电影 | 麻豆视频国产精品 | 天天插天天狠天天透 | 午夜精品久久久久久久爽 | 亚洲专区路线二 | 久久视频网址 | 日日草av | 成人久久18免费网站图片 | 中文字幕精品一区二区精品 | 超碰国产在线 | 国产精品免费视频网站 | 亚洲色图激情文学 | 91麻豆操| 天天干夜夜操视频 | a电影免费看 | 中文字幕永久在线 | 在线影院中文字幕 | 不卡的av在线播放 | 中文 一区二区 | 欧美网址在线观看 | 日韩精品久久中文字幕 | 超碰97.com| 天天爽夜夜爽人人爽一区二区 | 中文字幕二区三区 | 六月久久婷婷 | 日韩在线观看视频在线 | 国内久久久 | 一区二区三区视频 | 91麻豆视频网站 | 亚洲国产午夜 | 国产理伦在线 | 免费在线观看av网站 | 91精品国自产拍天天拍 | 成人久久久久久久久久 | 欧美亚洲成人xxx | 亚洲美女视频在线观看 | 亚洲精品国产拍在线 | 玖玖精品在线 | www.久久久精品 | 中文字幕乱视频 | www.日日操.com| 国产一级黄色免费看 | 三上悠亚一区二区在线观看 | 久久精品免费观看 | 日韩 在线观看 | 午夜狠狠操 | 麻豆免费在线视频 | 中文字幕日韩有码 | 中文字幕在线看视频 | 超碰在线官网 | 成人一区二区三区在线 | 国产黄色大片免费看 | 精品免费久久 | 亚洲精品在线观看免费 | 在线色吧 | 日韩av电影国产 | 久久国产精品系列 | 园产精品久久久久久久7电影 | 欧美日韩午夜在线 | 天堂av在线7 | 日韩免费视频网站 | 国产精品久免费的黄网站 | 欧美精品乱码久久久久久 | 91精品视屏| 在线观看免费成人av | 久久香蕉国产 | 中文字幕网站 | av中文字幕网站 | 日韩免费不卡av | 日韩中文字幕一区 | 亚洲精品在线一区二区三区 | 国产在线免费观看 | 香蕉网站在线观看 | 精品视频免费播放 | 久草在线视频免赞 | 免费日韩一区二区三区 | 日日久视频 | 色噜噜狠狠狠狠色综合 | 香蕉网站在线观看 | 97夜夜澡人人双人人人喊 | 丁香花五月 | 色偷偷av男人天堂 | 日本久久影视 | 九九精品久久久 | 狠狠狠狠狠狠 | 在线观看黄色小视频 | 久久精品国产精品亚洲 | 在线亚洲人成电影网站色www | 91精品国自产拍天天拍 | 欧美日本不卡高清 | 免费看成年人 | 中文字幕4 | 成人免费视频在线观看 | 免费在线中文字幕 | 97碰碰碰| 国产精品久免费的黄网站 | 中文字幕网站视频在线 | 中文在线a√在线 | 欧美一级免费黄色片 | 亚洲动漫在线观看 | 国产免费又爽又刺激在线观看 | 91网免费观看 | 激情视频网页 | 三级毛片视频 | 午夜电影久久 | 国产情侣一区 | 精品视频在线观看 | 91大神免费在线观看 | 92中文资源在线 | 国产视频一区精品 | 狠狠狠干狠狠 | 在线小视频| 日日摸日日添夜夜爽97 | 欧美日韩有码 | 日本视频精品 | 97看片网 | 日韩在线电影一区二区 | 久久视频在线 | 99热9| 五月天伊人| 精品国产一区二区三区蜜臀 | 国产在线视频在线观看 | www色| 免费一级黄色 | 中文字幕永久在线 | 99精品免费久久久久久久久 | 一区二区成人国产精品 | 18做爰免费视频网站 | 国产日韩欧美中文 | 亚洲精品午夜国产va久久成人 | 欧美不卡视频在线 |