(linux)tasklet
tasklet
Tasklet的使用比较简单,只需要定义tasklet及其处理函数并将两者关联
例子:
Void my_tasklet_func(unsigned long)
DECLARE_TASKLET(my_tasklet.my_tasklet_func,data)
代码DECLARE_TASKLET实现了定义名称为my_tasklet的tasklet并将其与my_tasklet_func这个函数绑定,而传入这个函数的参数为data。
需要调度tasklet的时候引用一个tasklet_schedule()函数就能使系统在适当的时候进行调度,如下所示
Tasklet_schedule(&my_tasklet)
下面给出驱动模板
- void xxx_do_tasklet(unsigned long);
- DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);
- void xxx_do_tasklet(unsigned long)
- {
- ……
- }
- irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
- {
- ……
- tasklet_schedule(&xxx_tasklet);
- ……
- }
- int _init xxx_init(void)
- {
- ……
- result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,”xxx”,NULL)
- ……
- }
- void _exit xxx_exit(void)
- {
- ……
- free_irq(xxx_irq,xxx_irq_interrupt);
- ……
- }
- void xxx_do_tasklet(unsigned long);
- DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);
- void xxx_do_tasklet(unsigned long)
- {
- ……
- }
- irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
- {
- ……
- tasklet_schedule(&xxx_tasklet);
- ……
- }
- int _init xxx_init(void)
- {
- ……
- result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,”xxx”,NULL)
- ……
- }
- void _exit xxx_exit(void)
- {
- ……
- free_irq(xxx_irq,xxx_irq_interrupt);
- ……
- }
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);
void xxx_do_tasklet(unsigned long)
{
……
}
irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
……
tasklet_schedule(&xxx_tasklet);
……
}
int _init xxx_init(void)
{
……
result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,”xxx”,NULL)
……
}
void _exit xxx_exit(void)
{
……
free_irq(xxx_irq,xxx_irq_interrupt);
……
}
二、tasklet函数详解
它对于中断处理特别有用:硬件中断必须尽快处理, 但大部分的数据管理可以延后到以后安全的时间执行。
tasklet 以一个数据结构形式存在,使用前必须被初始化。初始化能够通过调用一个特定函数或者通过使用某些宏定义声明结构:
- #include <linux/interrupt.h>
- struct tasklet_struct
- {
- struct tasklet_struct *next;
- unsigned long state;
- atomic_t count;
- void (*func)(unsigned long);
- unsigned long data;
- };
- void tasklet_init(struct tasklet_struct *t,
- void (*func)(unsigned long), unsigned long data);
- #define DECLARE_TASKLET(name, func, data) \
- struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
- #define DECLARE_TASKLET_DISABLED(name, func, data) \
- struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
- void tasklet_disable(struct tasklet_struct *t);
- /*函数暂时禁止给定的 tasklet被 tasklet_schedule 调度,直到这个 tasklet 被再次被enable;若这个 tasklet 当前在运行, 这个函数忙等待直到这个tasklet退出*/
- void tasklet_disable_nosync(struct tasklet_struct *t);
- /*和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU */
- void tasklet_enable(struct tasklet_struct *t);
- /*使能一个之前被disable的 tasklet;若这个 tasklet 已经被调度, 它会很快运行。 tasklet_enable 和tasklet_disable必须匹配调用, 因为内核跟踪每个 tasklet 的"禁止次数"*/
- void tasklet_schedule(struct tasklet_struct *t);
- /*调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己*/
- void tasklet_hi_schedule(struct tasklet_struct *t);
- /*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时, 它处理高优先级 tasklet 在其他软中断之前,只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期*/
- void tasklet_kill(struct tasklet_struct *t);
- /* 确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet 正在运行, 这个函数等待直到它执行完毕。若 tasklet 重新调度它自己,则必须阻止在调用 tasklet_kill 前它重新调度它自己,如同使用 del_timer_sync*/
- #include
- <linux/interrupt.h>
- struct tasklet_struct
- {
- struct tasklet_struct *next;
- unsigned long state;
- atomic_t count;
- void (*func)(unsigned long);
- unsigned long data;
- };
- void tasklet_init(struct tasklet_struct *t,
- void (*func)(unsigned long), unsigned long data);
- #define DECLARE_TASKLET(name, func, data) \
- struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
- #define DECLARE_TASKLET_DISABLED(name, func, data) \
- struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
- void tasklet_disable(struct tasklet_struct *t);
- /*函数暂时禁止给定的 tasklet被 tasklet_schedule 调度,直到这个 tasklet 被再次被enable;若这个
- tasklet 当前在运行, 这个函数忙等待直到这个tasklet退出*/
- void tasklet_disable_nosync(struct tasklet_struct *t);
- /*和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU */
- void tasklet_enable(struct tasklet_struct *t);
- /*使能一个之前被disable的 tasklet;若这个 tasklet 已经被调度, 它会很快运行。 tasklet_enable
- 和tasklet_disable必须匹配调用, 因为内核跟踪每个 tasklet 的"禁止次数"*/
- void tasklet_schedule(struct tasklet_struct *t);
- /*调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行;
- 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己*/
- void tasklet_hi_schedule(struct tasklet_struct *t);
- /*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时, 它处理高优先级 tasklet
- 在其他软中断之前,只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期*/
- void tasklet_kill(struct tasklet_struct *t);
- /*确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet 正在运行,
- 这个函数等待直到它执行完毕。若 tasklet 重新调度它自己,则必须阻止在调用 tasklet_kill 前它重新调度它自己,如同使用
- del_timer_sync*/
#include
<linux/interrupt.h>
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data);
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
void tasklet_disable(struct tasklet_struct *t);
/*函数暂时禁止给定的 tasklet被 tasklet_schedule 调度,直到这个 tasklet 被再次被enable;若这个
tasklet 当前在运行, 这个函数忙等待直到这个tasklet退出*/
void tasklet_disable_nosync(struct tasklet_struct *t);
/*和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU */
void tasklet_enable(struct tasklet_struct *t);
/*使能一个之前被disable的 tasklet;若这个 tasklet 已经被调度, 它会很快运行。 tasklet_enable
和tasklet_disable必须匹配调用, 因为内核跟踪每个 tasklet 的"禁止次数"*/
void tasklet_schedule(struct tasklet_struct *t);
/*调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行;
这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己*/
void tasklet_hi_schedule(struct tasklet_struct *t);
/*和tasklet_schedule类似,只是在更高优先级执行。当软中断处理运行时, 它处理高优先级 tasklet
在其他软中断之前,只有具有低响应周期要求的驱动才应使用这个函数, 可避免其他软件中断处理引入的附加周期*/
void tasklet_kill(struct tasklet_struct *t);
/*确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet 正在运行,
这个函数等待直到它执行完毕。若 tasklet 重新调度它自己,则必须阻止在调用 tasklet_kill 前它重新调度它自己,如同使用
del_timer_sync*/
工作队列
一、 工作队列的使用
定义一个工作队列
Struct work_struct my_wq;
Void my_wq_func(unsigned long);
通过INIT_WORK可以初始化这个工作队列并将工作队列与处理函数绑定
INIT_WORK(&my_wq,(void(*)(void*))my_wq_func,NULL)
调度工作队列
Schedule_work(&my_wq)
使用模板
- struct work_struct xxx_wq;
- void xxx_do_work(unsigned long);
- void xxx_do_work(unsigned long)
- {
- ……
- }
- irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
- {
- ……
- schedule_work(&xxx_wq);
- ……
- }
- int init(void)
- {
- ……
- result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,“xxx”,NULL)
- ……
- INIT_WORK(&xxx_wq,(void(*)(void*))xxx_do_work,NULL);
- …….
- }
- void xxx_exit(void)
- {
- ……
- free_irq(xxx_irq,xxx_interrupt);
- ……
- }
- struct work_struct xxx_wq;
- void xxx_do_work(unsigned long);
- void xxx_do_work(unsigned long)
- {
- ……
- }
- irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
- {
- ……
- schedule_work(&xxx_wq);
- ……
- }
- int init(void)
- {
- ……
- result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,“xxx”,NULL)
- ……
- INIT_WORK(&xxx_wq,(void(*)(void*))xxx_do_work,NULL);
- …….
- }
- void xxx_exit(void)
- {
- ……
- free_irq(xxx_irq,xxx_interrupt);
- ……
- }
struct work_struct xxx_wq;
void xxx_do_work(unsigned long);
void xxx_do_work(unsigned long)
{
……
}
irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
……
schedule_work(&xxx_wq);
……
}
int init(void)
{
……
result=request_irq(xxx_irq,xxx_interrupt,SA_INTERRUPT,“xxx”,NULL)
……
INIT_WORK(&xxx_wq,(void(*)(void*))xxx_do_work,NULL);
…….
}
void xxx_exit(void)
{
……
free_irq(xxx_irq,xxx_interrupt);
……
}
二、 工作队列函数详解
工作队列有 struct workqueue_struct 类型,在 <linux/workqueue.h> 中定义。一个工作队列必须明确的在使用前创建,宏为:
struct workqueue_struct *create_workqueue(const char *name); |
每个工作队列有一个或多个专用的进程("内核线程"), 这些进程运行提交给这个队列的函数。 若使用 create_workqueue, 就得到一个工作队列它在系统的每个处理器上有一个专用的线程。在很多情况下,过多线程对系统性能有影响,如果单个线程就足够则使用 create_singlethread_workqueue 来创建工作队列。
提交一个任务给一个工作队列,在这里《LDD3》介绍的内核2.6.10和我用的新内核2.6.22.2已经有不同了,老接口已经不能用了,编译会出错。这里我只讲2.6.22.2的新接口,至于老的接口我想今后内核不会再有了。从这一点我们可以看出内核发展。
- /*需要填充work_struct或delayed_work结构,可以在编译时完成, 宏如下: */
- struct work_struct {
- atomic_long_t data;
- #define WORK_STRUCT_PENDING 0 /* T if work item pending execution */
- #define WORK_STRUCT_FLAG_MASK (3UL)
- #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
- struct list_head entry;
- work_func_t func;
- };
- struct delayed_work {
- struct work_struct work;
- struct timer_list timer;
- };
- DECLARE_WORK(n, f)
- /*n 是声明的work_struct结构名称, f是要从工作队列被调用的函数*/
- DECLARE_DELAYED_WORK(n, f)
- /*n是声明的delayed_work结构名称, f是要从工作队列被调用的函数*/
- /*若在运行时需要建立 work_struct 或 delayed_work结构, 使用下面 2 个宏定义:*/
- INIT_WORK(struct work_struct *work, void (*function)(void *));
- PREPARE_WORK(struct work_struct *work, void (*function)(void *));
- INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
- PREPARE_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
- /* INIT_* 做更加全面的初始化结构的工作,在第一次建立结构时使用. PREPARE_* 做几乎同样的工作, 但是它不初始化用来连接 work_struct或 delayed_work 结构到工作队列的指针。如果这个结构已经被提交给一个工作队列, 且只需要修改该结构,则使用 PREPARE_* 而不是 INIT_* */
- /*有 2 个函数来提交工作给一个工作队列:*/
- int queue_work(struct workqueue_struct *queue, struct work_struct *work);
- int queue_delayed_work(struct workqueue_struct *queue, struct delayed_work *work, unsigned long delay);
- /* 每个都添加work到给定的workqueue。如果使用 queue_delay_work, 则实际的工作至少要经过指定的 jiffies 才会被执行。 这些函数若返回 1 则工作被成功加入到队列; 若为0,则意味着这个 work 已经在队列中等待,不能再次加入*/
- /*
- 需要填充work_struct或delayed_work结构,可以在编译时完成, 宏如下: */
- struct work_struct {
- atomic_long_t data;
- #define WORK_STRUCT_PENDING 0 /* T if work item pending execution
- */
- #define WORK_STRUCT_FLAG_MASK (3UL)
- #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
- struct list_head entry;
- work_func_t func;
- };
- struct delayed_work {
- struct work_struct work;
- struct timer_list timer;
- };
- DECLARE_WORK(n, f)
- /*n 是声明的work_struct结构名称, f是要从工作队列被调用的函数*/
- DECLARE_DELAYED_WORK(n, f)
- /*n是声明的delayed_work结构名称, f是要从工作队列被调用的函数*/
- /*若在运行时需要建立 work_struct 或 delayed_work结构, 使用下面 2 个宏定义:*/
- INIT_WORK(struct work_struct *work, void (*function)(void *));
- PREPARE_WORK(struct work_struct *work, void (*function)(void *));
- INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
- PREPARE_DELAYED_WORK(struct delayed_work *work, void (*function)(void
- *));
- /* INIT_* 做更加全面的初始化结构的工作,在第一次建立结构时使用. PREPARE_* 做几乎同样的工作, 但是它不初始化用来连接
- work_struct或delayed_work 结构到工作队列的指针。如果这个结构已经被提交给一个工作队列, 且只需要修改该结构,则使用
- PREPARE_* 而不是 INIT_* */
- /*有 2 个函数来提交工作给一个工作队列:*/
- int queue_work(struct workqueue_struct *queue, struct work_struct
- *work);
- int queue_delayed_work(struct workqueue_struct *queue, struct
- delayed_work *work, unsigned long delay);
- /*每个都添加work到给定的workqueue。如果使用 queue_delay_work, 则实际的工作至少要经过指定的 jiffies
- 才会被执行。 这些函数若返回 1 则工作被成功加入到队列; 若为0,则意味着这个 work 已经在队列中等待,不能再次加入*/
/*
需要填充work_struct或delayed_work结构,可以在编译时完成, 宏如下: */
struct work_struct {
atomic_long_t data;
#define WORK_STRUCT_PENDING 0 /* T if work item pending execution
*/
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
struct list_head entry;
work_func_t func;
};
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
DECLARE_WORK(n, f)
/*n 是声明的work_struct结构名称, f是要从工作队列被调用的函数*/
DECLARE_DELAYED_WORK(n, f)
/*n是声明的delayed_work结构名称, f是要从工作队列被调用的函数*/
/*若在运行时需要建立 work_struct 或 delayed_work结构, 使用下面 2 个宏定义:*/
INIT_WORK(struct work_struct *work, void (*function)(void *));
PREPARE_WORK(struct work_struct *work, void (*function)(void *));
INIT_DELAYED_WORK(struct delayed_work *work, void (*function)(void *));
PREPARE_DELAYED_WORK(struct delayed_work *work, void (*function)(void
*));
/* INIT_* 做更加全面的初始化结构的工作,在第一次建立结构时使用. PREPARE_* 做几乎同样的工作, 但是它不初始化用来连接
work_struct或delayed_work 结构到工作队列的指针。如果这个结构已经被提交给一个工作队列, 且只需要修改该结构,则使用
PREPARE_* 而不是 INIT_* */
/*有 2 个函数来提交工作给一个工作队列:*/
int queue_work(struct workqueue_struct *queue, struct work_struct
*work);
int queue_delayed_work(struct workqueue_struct *queue, struct
delayed_work *work, unsigned long delay);
/*每个都添加work到给定的workqueue。如果使用 queue_delay_work, 则实际的工作至少要经过指定的 jiffies
才会被执行。 这些函数若返回 1 则工作被成功加入到队列; 若为0,则意味着这个 work 已经在队列中等待,不能再次加入*/
在将来的某个时间, 这个工作函数将被传入给定的 data 值来调用。这个函数将在工作线程的上下文运行, 因此它可以睡眠 (你应当知道这个睡眠可能影响提交给同一个工作队列的其他任务) 工作函数不能访问用户空间,因为它在一个内核线程中运行, 完全没有对应的用户空间来访问。
取消一个挂起的工作队列入口项可以调用:
int cancel_delayed_work(struct delayed_work *work); |
如果这个入口在它开始执行前被取消,则返回非零。内核保证给定入口的执行不会在调用 cancel_delay_work 后被初始化. 如果 cancel_delay_work 返回 0, 但是, 这个入口可能已经运行在一个不同的处理器, 并且可能仍然在调用 cancel_delayed_work 后在运行. 要绝对确保工作函数没有在 cancel_delayed_work 返回 0 后在任何地方运行, 你必须跟随这个调用来调用:
void flush_workqueue(struct workqueue_struct *queue); |
在 flush_workqueue 返回后, 没有在这个调用前提交的函数在系统中任何地方运行。
而cancel_work_sync会取消相应的work,但是如果这个work已经在运行那么cancel_work_sync会阻塞,直到work完成并取消相应的work。
当用完一个工作队列,可以去掉它,使用:
void destroy_workqueue(struct workqueue_struct *queue); |
三、与tasklet的联系
工作队列类似 tasklets,允许内核代码请求在将来某个时间调用一个函数,不同在于:
(1)tasklet 在软件中断上下文中运行,所以 tasklet 代码必须是原子的。而工作队列函数在一个特殊内核进程上下文运行,有更多的灵活性,且能够休眠。
(2)tasklet 只能在最初被提交的处理器上运行,这只是工作队列默认工作方式。
(3)内核代码可以请求工作队列函数被延后一个给定的时间间隔。
(4)tasklet 执行的很快, 短时期, 并且在原子态, 而工作队列函数可能是长周期且不需要是原子的,两个机制有它适合的情形。
代码列子:
1 #include<linux/module.h>
2 #include<linux/init.h>
3 #include<linux/interrupt.h>
4
5 MODULE_LICENSE("GPL");
6
7 struct tasklet_struct tasklet_test;
8
9 void tasklet_hander_function(unsigned long data)
10 {
11 printk("Hello tasklet\n");
12 printk("tasklet data is %ld \n",data);
13 }
14
15 static int __init tasklet_test_init(void)
16 {
17 printk("tasklet init\n");
18 tasklet_init(&tasklet_test,tasklet_hander_function,tasklet_test.data);
19 tasklet_test.data = 10;
20 tasklet_schedule(&tasklet_test);
21
22 return 0;
23 }
24
25 static void __exit tasklet_test_exit(void)
26 {
27 printk("tasklet exit\n");
28 tasklet_kill(&tasklet_test);
29 }
30
31 module_init(tasklet_test_init);
32 module_exit(tasklet_test_exit);
(linux)tasklet的更多相关文章
- linux tasklet工作队列
工作队列是, 表面上看, 类似于 taskets; 它们允许内核代码来请求在将来某个时间调用 一个函数. 但是, 有几个显著的不同在这 2 个之间, 包括: tasklet 在软件中断上下文中运行的结 ...
- Linux tasklet 和workqueue学习
中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化.但是,中断是一个随机事件,它随时会到来,如果关中断的时间太长,CPU就不能及时响应其他的中断请求,从而造成中断的丢失.因 ...
- linux Tasklet 实现
记住 tasklet 是一个特殊的函数, 可能被调度来运行, 在软中断上下文, 在一个系统决 定的安全时间中. 它们可能被调度运行多次, 但是 tasklet 调度不累积; ; tasklet 只 运 ...
- Linux软中断、tasklet和工作队列
Linux内核中的软中断.tasklet和工作队列详解 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的“下半部”(bottom half)演变而来 ...
- linux 中断softirq tasklet
硬中断为什么不能休眠--- 中断线程以及软中断解决了什么问题---- 软中断cb函数是否允许相应本地中断,什么时候开启中断关闭中断---- 什么是软中断上下文------- 什么是tasklet 和软 ...
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- linux设备驱动归纳总结(六):3.中断的上半部和下半部——tasklet【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(六):3.中断的上半部和下半部——tasklet x ...
- linux内核--软中断与tasklet
硬件中断通常都需要在最短的时间内执行完毕,如果将所有硬件中断相关的处理都放在硬件中断处理程序中,那么就达不到这个目的. 通过linux提供的软中断和tasklet,可以将硬件中断处理程序中可以延迟处理 ...
- linux下面的中断处理软件中断tasklet机制
參考: <Linux内核设计与实现> http://blog.csdn.net/fontlose/article/details/8279113 http://blog.chinaunix ...
随机推荐
- 【angularjs学习】简单的语法
<div ng-app="" ng-init="names=[{name:'Jani',country:'Norway'},{name:'Hege',country ...
- JS控制背景音乐 没有界面
建立一个HTML5页面,放置<audio>标签,设置音频文件源,设置循环播放.准备两张图片,分别表示开启和暂停背景音乐两种状态,可以点击. <audio id="music ...
- dos中定义变量与获取常见的引用变量以及四则运算、备份文件(set用法)
在dos中使用set定义变量: set a=8 (注意等号两边没有空格) 引用变量如: echo %a% 将打印a的值 (%a%是获取变量a的值) dos中 ...
- pyquery操作
pyquery和我们之前用的jQuery有着异曲同工之处,使用起来更加方便,基本能满足大部分时候我们的需求. 先引入一个小事例展示pyquery的操作: html = ''' <div> ...
- poj1149最大流经典构图神题
题意:n个顾客依次来买猪,有n个猪房,每个顾客每次可以开若干个房子,买完时,店主可以调整这位顾客 开的猪房里的猪,共m个猪房,每个猪房有若干猪,求最多能卖多少猪. 构图思想:顾客有先后,每个人想要的猪 ...
- idea抛异常方式
选中需要抛异常的行,按alt+enter或者ctrl+alt+t,然后上下键选择自己抛异常的方式即可,如下图:
- HDC与CDC相互转换
转自loop_k原文 HDC与CDC相互转换 概念 首先说一下什么是DC(设备描述表):Windows应用程序通过为指定设备(屏幕,打印机等)创建一个设备描述表(Device Context, DC) ...
- Spring-boot和Spring-Cloud遇到的问题
1.spring cloud 使用 feign 启动报错 错误信息 org/springframework/cloud/client/loadbalancer/LoadBalancedRetryFa ...
- 天津政府应急系统之GIS一张图(arcgis api for flex)解说(二)鹰眼模块
解说GIS功能模块实现之前,先大概说一下flexviewer的核心配置文件config.xml,系统额GIS功能widget菜单布局.系统的样式.地图资源等等都是在这里配置的,这里对flexviewe ...
- apk解包——修改后缀为zip
将apk修改后缀为zip后,可直接解包查看资源文件