編輯:關於Android編程
阻塞操作是指在執行設備操作時,若不能獲得資源,則掛起進程直到滿足可操作的條件後再進行操作。被掛起的進程進入休眠狀態,被從調度器的運行隊列移走,直到等待的條件被滿足。
阻塞,默認的形式簡單直接效率低
非阻塞,相反,占用資源比較多
阻塞從字面上聽起來似乎意味著低效率,實則不然,如果設備驅動不阻塞,則用戶想獲取設備資源只能不停地查詢,這反而會無謂地耗費 CPU 資源。而阻塞訪問時,不能獲取資源的進程將進入休眠,它將 CPU 資源“禮讓”給其他進程。
介紹:以隊列為基礎數據結構,與進程調度機制緊密結合,能夠用於實現內核中的異步事件通知機制,也可以用來同步對系統資源的訪問。注意:雖然說的是隊列,但不是 fifo,沒有 fifo 的特性
在 Linux 驅動程序中,可以使用等待隊列(wait queue)來實現阻塞進程的喚醒。它以隊列為基礎數據結構,與進程調度機制緊密結合,能夠用於實現內核中的異步事件通知機制。等待隊列可以用來同步對系統資源的訪問,信號量在內核中依賴等待隊列來實現。
定義“等待隊列頭”
wait_queue_head_t my_queue;
初始化“等待隊列頭”
init_waitqueue_head(&my_queue);
而下面的 DECLARE_WAIT_QUEUE_HEAD()宏可以作為定義並初始化等待隊列頭的“快捷方式”。
DECLARE_WAIT_QUEUE_HEAD (name)
定義等待隊列
DECLARE_WAITQUEUE(name, tsk)
該宏用於定義並初始化一個名為 name 的等待隊列。
添加/移除等待隊列
void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
add_wait_queue()用於將等待隊列 wait 添加到等待隊列頭 q 指向的等待隊列鏈表中,而remove_wait_queue()用於將等待隊列 wait 從附屬的等待隊列頭 q 指向的等待隊列鏈表中移除。
等待事件
wait_event(wait_queue_head_t queue, condition) --> 深睡,不可以被信號打斷
wait_event_interruptible(wait_queue_head_t queue, condition) --> 淺睡,可以被信號打斷
wait_event_timeout(wait_queue_head_t queue, condition, timeout)
wait_event_interruptible_timeout(wait_queue_head_t queue, condition, timeout)
queue,作為等待隊列頭的等待隊列被喚醒
condition,條件,滿足 喚醒,否則 阻塞
timeout,阻塞等待的超時時間,單位是 jiffy,等待時間 timeout 後,無論條件滿足不滿足,都返回
喚醒隊列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
喚醒時會判斷 condition
①wake_up() -- wait_event() / wait_event_timeout()
②wake_up_interruptible() -- wait_event_interruptible() / wait_event_interruptible_timeout()
①可以喚醒處於 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 的進程
②可以喚醒處於 TASK_INTERRUPTIBLE 的進程
在等待隊列上睡眠:
sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
sleep_on 將目前進程的狀態置成 TASK_UNINTERRUPTIBLE,並定義一個等待隊列,之後把它附屬到等待隊列頭 q,直到資源可獲得,q 引導的等待隊列被喚醒。
wake_up_interruptible將目前進程的狀態置成 TASK_ INTERRUPTIBLE,並定義一個等待隊列,之後把它附屬到等待隊列頭q,直到資源可獲得,q引導的等待隊列被喚醒或者進程收到信號。
sleep_on()函數應該與 wake_up()成對使用,interruptible_sleep_on()應該wake_up_interruptible()
成對使用。
注意:
在許多設備驅動中,並不調用 sleep_on()或 interruptible_sleep_on(),而是親自進行進程的狀態改變和切換
為了讓 驅動支持 阻塞和非阻塞,需要在驅動中使用等待隊列:
waitqueue.c
#include#include #include #include #include #include #include #include #include MODULE_LICENSE ("GPL"); int hello_major = 250; int hello_minor = 0; int number_of_devices = 1; struct class *my_class; struct hello_device { char data[128]; int len; wait_queue_head_t rq, wq; struct semaphore sem; struct cdev cdev; } hello_device; static int hello_open (struct inode *inode, struct file *filp) { filp->private_data = container_of(inode->i_cdev, struct hello_device, cdev); printk (KERN_INFO "Hey! device opened\n"); printk (KERN_INFO "len = %d\n",(*((int *)filp->private_data))); return 0; } static int hello_release (struct inode *inode, struct file *filp) { printk (KERN_INFO "Hmmm... device closed\n"); return 0; } ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp) { ssize_t result = 0; struct hello_device *dev = filp->private_data; down(&dev->sem); while (hello_device.len == 0) { up(&dev->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->rq, (dev->len != 0))) return -ERESTARTSYS; down(&dev->sem); } if (count > dev->len) count = dev->len; if (copy_to_user (buff, dev->data, count)) { result = -EFAULT; } else { printk (KERN_INFO "read %d bytes\n", (int)count); dev->len -= count; result = count; memcpy(dev->data, dev->data+count, dev->len); } up(&dev->sem); wake_up(&dev->wq); return result; } ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos) { ssize_t ret = 0; struct hello_device *dev = filp->private_data; if (count > 128) return -ENOMEM; down(&dev->sem); while (dev->len == 128) { up(&dev->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->wq, (dev->len != 128))) return -ERESTARTSYS; down(&dev->sem); } if (count > (128 - dev->len)) count = 128 - dev->len; if (copy_from_user (dev->data+dev->len, buf, count)) { ret = -EFAULT; } else { printk (KERN_INFO "write %d bytes\n", (int)count); dev->len += count; ret = count; } up(&dev->sem); wake_up(&dev->rq); return ret; } struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write }; static void char_reg_setup_cdev (void) { int error; dev_t devno; devno = MKDEV (hello_major, hello_minor); cdev_init (&hello_device.cdev, &hello_fops); hello_device.cdev.owner = THIS_MODULE; error = cdev_add (&hello_device.cdev, devno , 1); if (error) printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error); } static int char_dev_create (void) { my_class = class_create(THIS_MODULE,"waitqueue_class"); if(IS_ERR(my_class)) { printk("Err: failed in creating class.\n"); return -1; } device_create(my_class, NULL, MKDEV (hello_major, hello_minor), NULL, "waitqueue"); return 0; } static int __init hello_2_init (void) { int result; dev_t devno; devno = MKDEV (hello_major, hello_minor); result = register_chrdev_region (devno, number_of_devices, "waitqueue"); if (result < 0) { printk (KERN_WARNING "hello: can't get major number %d\n", hello_major); goto err1; } char_dev_create(); char_reg_setup_cdev (); init_waitqueue_head(&hello_device.rq); init_waitqueue_head(&hello_device.wq); sema_init(&hello_device.sem, 1); memset(hello_device.data, 0, 128); hello_device.len = 0; printk (KERN_INFO "char device registered\n"); return 0; err1: device_destroy(my_class, devno); class_destroy(my_class); unregister_chrdev_region(devno, 1); return result; } static void __exit hello_2_exit (void) { dev_t devno = MKDEV (hello_major, hello_minor); cdev_del (&hello_device.cdev); device_destroy(my_class, devno); //delete device node under /dev//必須先刪除設備,再刪除class類 class_destroy(my_class); //delete class created by us unregister_chrdev_region (devno, number_of_devices); printk("waitqueue module exit \n"); return; } module_init (hello_2_init); module_exit (hello_2_exit);
test_write.c
#include#include #include #include #define N 90 int main() { int i, fd; char buf[N]; for (i=0; i<90; i++) { buf[i] = i + 33; } if ((fd = open("/dev/waitqueue", O_WRONLY)) < 0) { perror("fail to open"); } printf("wrote %d bytes\n", (int)write(fd, buf, N)); close(fd); return 0; }
test_read.c
#include#include #include #include #define N 90 int main() { int i, fd; char buf[N] = {0}; int num = 0; if ((fd = open("/dev/waitqueue", O_RDWR)) < 0) { perror("fail to open"); return -1; } puts("open is ok"); if((num = read(fd, buf, 10)) < 0) { printf("num = %d \n", num); perror("read:"); } printf("read num = %d \n", num); printf("Is:"); puts(buf); close(fd); return 0; }
Makefile
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /lib/modules/$(shell uname -r)/build #KERNELDIR ?= ~/wor_lip/linux-3.4.112 PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module* .PHONY: modules modules_install clean else obj-m := waitqueue.o endif
程序思路:
①在驅動中的讀和寫的方法中分別判斷 filp->f_flags 是不是 O_NONBLOCK ,如果標志是非阻塞,就馬上返回,
②在讀和寫的方法中還要加上信號量實現 PV 操作,防止多個函數讀的時候出現混亂的情況
③在條件不滿足(讀的時候,緩沖區中內容長度 = 0.寫的時候緩沖區長度 = 128)調用相應的讀寫等待隊列
程序的現象:
寫函數一次寫 90 個字符,
讀函數每次只讀 10 個字符,
如果讀了很多次,讀完了 buf 中的內容,就會阻塞的等在哪裡,打開另外的客戶端,進行寫操作,讀客戶端在寫完的剎那,能夠讀出數據
在有 A 客戶端讀完了,並且處於阻塞狀態,再開一個 B 客戶端依然讀阻塞,在 C 客戶端進行寫的時候,寫完後,A 客戶端會先得到數據, B 再得到數據
因為company項目中需要做九宮格抽獎活動,以前都沒有做過類似的功能,雖然之前在浏覽大神們的博客中,無意中也看到了好多關於抽獎的項目,但因為項目中沒有需要,一直都沒有點
Android輸入法框架(Input Method Framework,IMF)是Android中非常重要的模塊,它分布於三個部分(確切的說,是三個進程), 包含編輯框
Viewpager 在android界面布局中屬於常用類型 ,它可以做導航,頁面菜單,進入軟件是的歡迎界面 等等。比現在最流行的幾款手機軟件 ,QQ,微信,微博 等 ,
這個月接到一個項目,要寫一個像360助手一樣的對於軟件管理的APP;其中,遇到了一個問題:多線程斷點下載 這個 ,由於之前沒有寫過這方面的應用功能。所以,不免要自學了。然