Time, Delays, and Deferred Work

Dealing with time involves the following tasks, in order of increasing complexity:
• Measuring time lapses and comparing times
• Knowing the current time
• Delaying operation for a specified amount of time
• Scheduling asynchronous functions to happen at a later time

Measuring Time Lapses

Every time a timer interrupt occurs, the value of an internal kernel counter is incremented. The counter is initialized to 0 at system boot, so it represents the number of clock ticks since last boot. The counter is a 64-bit variable (even on 32-bit architectures) and is called jiffies_64 . However, driver writers normally access the jiffies variable, an unsigned long that is the same as either jiffies_64 or its least significant bits. Using jiffies is usually preferred because it is faster, and accesses to the 64-bit jiffies_64 value are not necessarily atomic on all architectures.

在jiffies.h 里面有一段代码和注释:

/** The following defines establish the engineering parameters of the PLL* model. The HZ variable establishes the timer interrupt frequency, 100 Hz* for the SunOS kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the* OSF/1 kernel. The SHIFT_HZ define expresses the same value as the* nearest power of two in order to avoid hardware multiply operations.*/
#if HZ >= 12 && HZ < 24
# define SHIFT_HZ   4
#elif HZ >= 24 && HZ < 48
# define SHIFT_HZ   5
#elif HZ >= 48 && HZ < 96
# define SHIFT_HZ   6
#elif HZ >= 96 && HZ < 192
# define SHIFT_HZ   7
#elif HZ >= 192 && HZ < 384
# define SHIFT_HZ   8
#elif HZ >= 384 && HZ < 768
# define SHIFT_HZ   9
#elif HZ >= 768 && HZ < 1536
# define SHIFT_HZ   10
#elif HZ >= 1536 && HZ < 3072
# define SHIFT_HZ   11
#elif HZ >= 3072 && HZ < 6144
# define SHIFT_HZ   12
#elif HZ >= 6144 && HZ < 12288
# define SHIFT_HZ   13
#else
# error Invalid value of HZ.
#endif

HZ这个变量取决于中断频率,不同平台的硬件时钟频率不一。

SHIFT_HZ这个变量是用来描述系统的HZ值最接近是2的多少次幂。

比方说HZ == 32 那么这一串Macro 可以得到系统的SHIFT_HZ == 5 (2^5 == 32)

因为jiffies这个变量是由硬件的时钟中断引起的,于是不可避免的要谈谈溢出的问题。

This code has no problem with jiffies wrapping around, as long as different values are compared in the right way. Even though on 32-bit platforms the counter wraps around only once every 50 days when HZ is 1000, your code should be prepared to face that event.

在32位机器平台上,HZ == 1000的时候,几乎每50天这个计数器(jiffies)就会溢出。(很奇怪,我的PC的HZ只有250)

肿么办捏??

kernel工作人员早就搞定这个问题了,留出了接口供大家使用

/**  These inlines deal with timer wrapping correctly. You are * strongly encouraged to use them*    1. Because people otherwise forget* 2. Because if the timer wrap changes in future you won't have to*     alter your driver code.** time_after(a,b) returns true if the time a is after time b.** Do this with "<0" and ">=0" to only test the sign of the result. A* good compiler would generate better code (and a really good compiler* wouldn't care). Gcc is currently neither.*/
#define time_after(a,b)     \(typecheck(unsigned long, a) && \typecheck(unsigned long, b) && \((long)((b) - (a)) < 0))          //如果a在时间上大于b时间点,于是返回 1 否则返回 0
#define time_before(a,b)    time_after(b,a)#define time_after_eq(a,b)   \(typecheck(unsigned long, a) && \typecheck(unsigned long, b) && \((long)((a) - (b)) >= 0))       <span style="font-family: Arial, Helvetica, sans-serif;"> //如果a在时间上大于b时间点,于是返回 1 否则返回 0</span>
#define time_before_eq(a,b) time_after_eq(b,a)/** Calculate whether a is in the range of [b, c].*/
#define time_in_range(a,b,c) \(time_after_eq(a,b) && \time_before_eq(a,c)) // 时间点a是否位于bc之间/** Calculate whether a is in the range of [b, c).*/
#define time_in_range_open(a,b,c) \(time_after_eq(a,b) && \time_before(a,c))/* Same as above, but does so with platform independent 64bit types.* These must be used when utilizing jiffies_64 (i.e. return value of* get_jiffies_64() */
#define time_after64(a,b)   \(typecheck(__u64, a) &&    \typecheck(__u64, b) && \((__s64)((b) - (a)) < 0))
#define time_before64(a,b)  time_after64(b,a)#define time_after_eq64(a,b)   \(typecheck(__u64, a) && \typecheck(__u64, b) && \((__s64)((a) - (b)) >= 0))
#define time_before_eq64(a,b)   time_after_eq64(b,a)#define time_in_range64(a, b, c) \(time_after_eq64(a, b) && \time_before_eq64(a, c))/** These four macros compare jiffies and 'a' for convenience.*//* time_is_before_jiffies(a) return true if a is before jiffies */
#define time_is_before_jiffies(a) time_after(jiffies, a)/* time_is_after_jiffies(a) return true if a is after jiffies */
#define time_is_after_jiffies(a) time_before(jiffies, a)/* time_is_before_eq_jiffies(a) return true if a is before or equal to jiffies*/
#define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a)/* time_is_after_eq_jiffies(a) return true if a is after or equal to jiffies*/
#define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a)

get_jiffies_64的实现:

Retrieves jiffies_64 without race conditions.

static inline u64 get_jiffies_64(void)
{return (u64)jiffies;
}

然后是各种时间数据转换的API

extern unsigned int jiffies_to_msecs(const unsigned long j);
extern unsigned int jiffies_to_usecs(const unsigned long j);
extern unsigned long msecs_to_jiffies(const unsigned int m);
extern unsigned long usecs_to_jiffies(const unsigned int u);
extern unsigned long timespec_to_jiffies(const struct timespec *value);
extern void jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);
extern unsigned long timeval_to_jiffies(const struct timeval *value);
extern void jiffies_to_timeval(const unsigned long jiffies,struct timeval *value);extern clock_t jiffies_to_clock_t(unsigned long x);
static inline clock_t jiffies_delta_to_clock_t(long delta)
{return jiffies_to_clock_t(max(0L, delta));
}extern unsigned long clock_t_to_jiffies(unsigned long x);
extern u64 jiffies_64_to_clock_t(u64 x);
extern u64 nsec_to_clock_t(u64 x);
extern u64 nsecs_to_jiffies64(u64 n);
extern unsigned long nsecs_to_jiffies(u64 n);

unsigned long
mktime(const unsigned int year0, const unsigned int mon0,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec)的实现:

该函数把传入参数转化为秒为单位的时间

/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.* Assumes input in normal date format, i.e. 1980-12-31 23:59:59* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.** [For the Julian calendar (which was used in Russia before 1917,* Britain & colonies before 1752, anywhere else before 1582,* and is still in use by some communities) leave out the* -year/100+year/400 terms, and add 10.]** This algorithm was first published by Gauss (I think).** WARNING: this function will overflow on 2106-02-07 06:28:16 on* machines where long is 32-bit! (However, as time_t is signed, we* will already get problems at other places on 2038-01-19 03:14:08)*/
unsigned long
mktime(const unsigned int year0, const unsigned int mon0,const unsigned int day, const unsigned int hour,const unsigned int min, const unsigned int sec)
{unsigned int mon = mon0, year = year0;/* 1..12 -> 11,12,1..10 */if (0 >= (int) (mon -= 2)) {mon += 12; /* Puts Feb last since it has leap day */year -= 1;}return ((((unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +year*365 - 719499)*24 + hour /* now have hours */)*60 + min /* now have minutes */)*60 + sec; /* finally seconds */
}

牛人的代码分析:http://blog.csdn.net/axx1611/article/details/1792827?reload

简直帅!

jit.c

一开始就创建了 8 个proc设备!

int __init jit_init(void)
{proc_create("currentime", 0, NULL, &jit_currentime_proc_fops);proc_create_data("jitbusy", 0, NULL, &jit_fn_proc_fops, (void *)JIT_BUSY);proc_create_data("jitsched",0, NULL, &jit_fn_proc_fops, (void *)JIT_SCHED);proc_create_data("jitqueue",0, NULL, &jit_fn_proc_fops, (void *)JIT_QUEUE);proc_create_data("jitschedto", 0, NULL, &jit_fn_proc_fops, (void *)JIT_SCHEDTO);proc_create("jitimer", 0, NULL, &jit_timer_proc_fops);proc_create("jitasklet", 0, NULL, &jit_tasklet_proc_fops);proc_create_data("jitasklethi", 0, NULL, &jit_tasklet_proc_fops, (void *)1);return 0; /* success */
}

这里其实就四种文件操作的方式(文件操作结构体) ——

jit_currentime_proc_fops

jit_fn_proc_fops

jit_timer_proc_fops

jit_tasklet_proc_fops

首先看第一个设备currentime

currentime用了单独的文件操作结构体jit_currentime_proc_fops

static const struct file_operations jit_currentime_proc_fops = {.open       = jit_currenttime_proc_open,.read      = seq_read,.llseek     = seq_lseek,.release   = single_release,
};

open系统调用通过jit_currenttime_proc_open打开文件

static int jit_currenttime_proc_open(struct inode *inode, struct file *file)
{return single_open(file, jit_currenttime_proc_show, NULL);
}

每次打开的时候实际上是调用jit_currenttime_proc_show函数

static int jit_currenttime_proc_show(struct seq_file *m, void *v)
{struct timeval tv1; //结构体timeval储存秒和毫秒struct timespec tv2;//结构体timespec储存秒和纳秒unsigned long  j1;u64 j2;/* get them four */j1 = jiffies;   //时钟滴答,如果是32位机,jiffies是取jiffies_64的低32位值j2 = get_jiffies_64();//取64-bit jiffies值。对于64位机器上,jiffies和jieffies_64实际上是同一个do_gettimeofday(&tv1);tv2 = current_kernel_time();/* print */seq_printf(m,"0x%08lx 0x%016Lx %10i.%06i\n""%40i.%09i\n",j1, j2,(int) tv1.tv_sec, (int) tv1.tv_usec,(int) tv2.tv_sec, (int) tv2.tv_nsec);return 0;
}


可以看见第一个打印的是j1的值,第二个是j2,我的机器是64位的Linux,于是j1和j2是相等的。

jiffies_64 和 jiffies都被看作只读变量

结构体tv1和tv2的.sec域都是记录的秒值,于是两者的秒部分相同——1407758614

1407758614/(3600*24*365) = 44(取整),所以?get到什么吗?1970+44,你懂的。反正我不担心什么2038什么的哈哈~

这个秒值是记录的从1970年来一共过了多少秒!

jiffies是由系统时钟中断决定的.

In the screenshot above, there are two interesting things to note. First, the current_kernel_time value, though expressed in nanoseconds, has only clock-tick granularity; do_gettimeofday consistently reports a later time but not later than the next timer tick. Second, the 64-bit jiffies counter has the least-significant bit of the upper 32-bit word set.

OK,currentime这个设备就这么多,如果以后有新发现再update。

Time delay

jitbusy jitsched  jitqueuejitschedto这四个设备都是共同调用jit_fn_proc_fops文件操作结构体

而jit_fn_proc_fops通过jit_files这个枚举变量和switch语句来区辨设备,open的时候到底是open的谁捏?

switch语句来搞定。

/* use these as data pointers, to implement four files in one function */
enum jit_files {JIT_BUSY,JIT_SCHED,JIT_QUEUE,JIT_SCHEDTO
};/** This function prints one line of data, after sleeping one second.* It can sleep in different ways, according to the data pointer*/
static int jit_fn_proc_show(struct seq_file *m, void *v)
{unsigned long j0, j1; /* jiffies */wait_queue_head_t wait;init_waitqueue_head (&wait);j0 = jiffies;j1 = j0 + delay;//delay是HZ变量的一个程序运行时的copyswitch((long)m->private) {case JIT_BUSY:while (time_before(jiffies, j1))cpu_relax();break;case JIT_SCHED:while (time_before(jiffies, j1)) { //通过time_before实现delay时长的延时schedule();}break;case JIT_QUEUE:wait_event_interruptible_timeout(wait, 0, delay); //使当前进程休眠在等待队列上 <span style="white-space:pre">                                                    </span>                                             // <span style="font-family: Arial, Helvetica, sans-serif;">超过delay(第三个参数)的jiffie</span><span style="font-family: Arial, Helvetica, sans-serif;">s时长之后,等待结束</span>break;case JIT_SCHEDTO:set_current_state(TASK_INTERRUPTIBLE);//置当前进程为可中断模式,进入等待队列schedule_timeout (delay);//在delay的jiffies时长之后,唤醒当前进程(前提是之前已经set_current_state)break;}j1 = jiffies; /* actual value after we delayed */seq_printf(m, "%9li %9li\n", j0, j1); //j0 j1分别记录了switch语句之前和结束时候的jiffies值,于是通过比较这两个值,可以<span style="white-space:pre">                              </span>//知道switch语句里面的部分花了多久return 0;
}

其实我只是有点奇怪,这里的HZ居然是250,而不是1000(高手路过求解答)

include <linux/wait.h>
long wait_event_timeout(wait_queue_head_t q, condition, long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q,condition, long timeout);

These functions sleep on the given wait queue, but they return after the timeout (expressed in jiffies) expires. Thus, they implement a bounded sleep that does not go on forever.

下面是各种延时的API

#include <linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);Introduces delays of an integer number of nanoseconds, microseconds, and mil-liseconds. The delay achieved is at least the requested value, but it can be more.
The argument to each function must not exceed a platform-specific limit (usu-ally a few thousands).void msleep(unsigned int millisecs);unsigned long msleep_interruptible(unsigned int millisecs);void ssleep(unsigned int seconds);
Puts the process to sleep for the given number of milliseconds (or seconds, in the case ofssleep).

Kernel Timers

Whenever you need to schedule an action to happen later, without blocking the current process until that time arrives, kernel timers are the tool for you. These timers are used to schedule execution of a function at a particular time in the future, based on the clock tick, and can be used for a variety of tasks;

注意事项:

A number of actions require the context of a process in order to be executed. When you are outside of process context (i.e., in interrupt context), you must observe the following rules:

• No access to user space is allowed. Because there is no process context, there is no path to the user space associated with any particular process.

• Thecurrentpointer is not meaningful in atomic mode and cannot be used since the relevant code has no connection with the process that has been interrupted.

• No sleeping or scheduling may be performed. Atomic code may not call schedule or a form of wait_event, nor may it call any other function that could sleep.

The Timer API

The kernel provides drivers with a number of functions to declare, register, and remove kernel timers. The following excerpt shows the basic building blocks:

#include <linux/timer.h>
struct timer_list {
/* ... */
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
};void init_timer(struct timer_list *timer);struct timer_list TIMER_INITIALIZER(_function, _expires, _data);void add_timer(struct timer_list * timer);int del_timer(struct timer_list * timer);
static int jit_timer_proc_open(struct inode *inode, struct file *file)
{return single_open(file, jit_timer_proc_show, NULL);
}static const struct file_operations jit_timer_proc_fops = {.open      = jit_timer_proc_open, //通过调用jit_timer_proc_open打开.read        = seq_read,.llseek     = seq_lseek,.release   = single_release,
};
/** The timer example follows*/
void jit_timer_fn(unsigned long arg)
{struct jit_data *data = (struct jit_data *)arg;unsigned long j = jiffies;seq_printf(data->m, "%9li  %3li     %i    %6i   %i   %s\n",j, j - data->prevjiffies, in_interrupt() ? 1 : 0,current->pid, smp_processor_id(), current->comm); //j - data->prevjiffies就得到了timer的时间差,                                                                                                                          //即tdelayif (--data->loops) {data->timer.expires += tdelay; //期望的延时data->prevjiffies = j; //储存之前的jiffies值add_timer(&data->timer); //调用时钟中断} else {wake_up_interruptible(&data->wait);}
}/* the /proc function: allocate everything to allow concurrency */
static int jit_timer_proc_show(struct seq_file *m, void *v)
{struct jit_data *data;unsigned long j = jiffies;data = kmalloc(sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;init_timer(&data->timer);init_waitqueue_head (&data->wait);/* write the first lines in the buffer */seq_puts(m, "   time   delta  inirq    pid   cpu command\n");seq_printf(m, "%9li  %3li     %i    %6i   %i   %s\n",j, 0L, in_interrupt() ? 1 : 0,current->pid, smp_processor_id(), current->comm);/* fill the data for our timer function */data->prevjiffies = j;data->m = m;data->loops = JIT_ASYNC_LOOPS;//5/* register the timer */data->timer.data = (unsigned long)data;data->timer.function = jit_timer_fn;data->timer.expires = j + tdelay; /* parameter */add_timer(&data->timer); //这里有个add_timer,前面jit_timer_fn里面也有个add_timer确保loops减少之后还有                                                         //定时器继续定时,loops到0的时候if判断就进入wake_up_interruptible/* wait for the buffer to fill */wait_event_interruptible(data->wait, !data->loops);if (signal_pending(current))return -ERESTARTSYS;kfree(data);return 0;
}

由于tdelay变量是10于是延时了10个单位的jiffies

Kernel Timers 相关API

#include <asm/hardirq.h>
int in_interrupt(void);
int in_atomic(void);
Returns a Boolean value telling whether the calling code is executing in inter-rupt context or atomic context. Interrupt context is outside of a process con-text, either during hardware or software interrupt processing. Atomic context is when you can’t schedule either an interrupt context or a process’s context with a spinlock held.

#include <linux/timer.h>
void init_timer(struct timer_list * timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
This function and the static declaration of the timer structure are the two ways to initialize atimer_listdata structure.
void add_timer(struct timer_list * timer);
Registers the timer structure to run on the current CPU.
int mod_timer(struct timer_list *timer, unsigned long expires);
Changes the expiration time of an already scheduled timer structure. It can also act as an alternative toadd_timer.
int timer_pending(struct timer_list * timer);
Macro that returns a Boolean value stating whether the timer structure is already registered to run.
void del_timer(struct timer_list * timer);
void del_timer_sync(struct timer_list * timer);
Removes a timer from the list of active timers. The latter function ensures that the timer is not currently running on another CPU.

注意,每想延时一次就得add_timer一次,timer达到“用掉”之后,就不需要del_timer了.

Tasklets

Another kernel facility related to timing issues is the tasklet mechanism.

Tasklets resemble kernel timers in some ways. They are always run at interrupt time, they always run on the same CPU that schedules them, and they receive an unsigned long argument. Unlike kernel timers, however, you can’t ask to execute the function at a specific time. By scheduling a tasklet, you simply ask for it to be executed at a later time chosen by the kernel. This behavior is especially useful with interrupt handlers, where the hardware interrupt must be managed as quickly as possible, but most of the data management can be safely delayed to a later time.

static int jit_tasklet_proc_open(struct inode *inode, struct file *file)
{return single_open(file, jit_tasklet_proc_show, PDE_DATA(file_inode(file)));
}static const struct file_operations jit_tasklet_proc_fops = {.open        = jit_tasklet_proc_open,.read      = seq_read,.llseek     = seq_lseek,.release   = single_release,
};

关于PDE_DATA

void jit_tasklet_fn(unsigned long arg)
{struct jit_data *data = (struct jit_data *)arg;unsigned long j = jiffies;seq_printf(data->m, "%9li  %3li     %i    %6i   %i   %s\n",j, j - data->prevjiffies, in_interrupt() ? 1 : 0,current->pid, smp_processor_id(), current->comm);if (--data->loops) {data->prevjiffies = j;if (data->hi)tasklet_hi_schedule(&data->tlet);elsetasklet_schedule(&data->tlet);} else {wake_up_interruptible(&data->wait);}
}
/* the /proc function: allocate everything to allow concurrency */
static int jit_tasklet_proc_show(struct seq_file *m, void *v)
{struct jit_data *data;unsigned long j = jiffies;long hi = (long)m->private;data = kmalloc(sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;init_waitqueue_head (&data->wait);/* write the first lines in the buffer */seq_puts(m, "   time   delta  inirq    pid   cpu command\n");seq_printf(m, "%9li  %3li     %i    %6i   %i   %s\n",j, 0L, in_interrupt() ? 1 : 0,current->pid, smp_processor_id(), current->comm);/* fill the data for our tasklet function */data->prevjiffies = j;data->m = m;data->loops = JIT_ASYNC_LOOPS;/* register the tasklet */tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data);data->hi = hi;if (hi)tasklet_hi_schedule(&data->tlet);elsetasklet_schedule(&data->tlet);/* wait for the buffer to fill */wait_event_interruptible(data->wait, !data->loops);if (signal_pending(current))return -ERESTARTSYS;kfree(data);return 0;
}

Tasklets  相关API

#include <linux/interrupt.h>
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);The first two macros declare a tasklet structure, while thetasklet_init function initializes a tasklet structure that has been obtained by allocation or other means. The secondDECLAREmacro marks the tasklet as disabled.
void tasklet_disable(struct tasklet_struct *t);
void tasklet_disable_nosync(struct tasklet_struct *t);
void tasklet_enable(struct tasklet_struct *t);Disables and reenables a tasklet. Eachdisablemust be matched with anenable (you can disable the tasklet even if it’s already disabled). The function tasklet_disable waits for the tasklet to terminate if it is running on another CPU. The no syncversion doesn’t take this extra step.
void tasklet_schedule(struct tasklet_struct *t);
void tasklet_hi_schedule(struct tasklet_struct *t);Schedules a tasklet to run, either as a “normal” tasklet or a high-priority one. When soft interrupts are executed, high-priority tasklets are dealt with first, while normal tasklets run last.
void tasklet_kill(struct tasklet_struct *t);Removes the tasklet from the list of active ones, if it’s scheduled to run. Like tasklet_disable, the function may block on SMP systems waiting for the tasklet to terminate if it’s currently running on another CPU

Time, Delays, and Deferred Work LDD3 学习笔记 + jiffies.h 分析相关推荐

  1. Qt学习笔记,再次分析EVA源码之后得出的结论-QListView,QListViewItem(Qt3);Q3ListView,Q3ListViewItem(Qt4)...

    Qt学习笔记,再次分析EVA源码之后得出的结论-QListView,QListViewItem(Qt3);Q3ListView,Q3ListViewItem(Qt4) 今天再次分析了Eva的源码,也看 ...

  2. oracle in查询 一直等待,学习笔记:Oracle awr 分析解决inactive transaction branch等待事件...

    天萃荷净 通过Oracle AWR报告分析inactive transaction branch等待事件的原因 分析一份awr,发现不太熟悉的等待事件"inactive transactio ...

  3. RT-1052学习笔记 - GPIO架构分析

    RT-1052学习笔记 - GPIO架构分析 一.RT1052之GPIO与IOMUX 二.GPIO架构分析 分析GPIO的外设框架 库函数操作 1.初始化MUX 2.初始化PAD 3.初始化GPIO外 ...

  4. ARM学习笔记--LPC1788引脚分析

    http://zgmgypb.blog.163.com/blog/static/962028192012730104640980/ RM学习笔记--LPC1788引脚分析 一.电源分析 要弄清楚单片机 ...

  5. HFSS学习笔记 21 扫频分析

    HFSS学习笔记之扫频分析-----------锋锋的快乐小窝 通过求解设置,可以对模型结构进行自适应网格剖分,并计算指定的网络剖分频率点处的 S S S参数和场解 b如果需要分析或计算某个频段范围内 ...

  6. Replica Island 学习笔记 03 - Activity分析

    MainMenuActivity Activity界面 MainMenuActivity启动的时候,会用fade in动画逐个显示菜单按钮,并在屏幕最下方显示一个Ticker:     点击任一菜单按 ...

  7. 学习笔记 | 独立成分分析(ICA, FastICA)及应用

    学习笔记 | 独立成分分析[ICA, FastICA]及应用 1 背景说明 2 算法原理 2.1 ICA简介 2.2 形式化表达 3 算法步骤与代码 4 算法改进:FastICA 5 ICA实例与应用 ...

  8. 自控原理学习笔记-系统稳定性分析(2)-环路分析及Nyquist-Bode判据

    自控原理学习笔记 自控原理学习笔记专栏 文章目录 3. 环路分析 3.1环路分析基本思想: 3.2 稳定程度的性能指标(相对稳定) 3.3 环路整形 4.Nyquist判据 4.1 与幅角原理关系 4 ...

  9. 学习笔记-抖音分析报告

    学习笔记-产品分析报告-抖音 一. 产品概述 1. 背景 2. 体验环境 3. 产品概括 4. 产品定位 5. 用户需求分析 6. 市场现状和分析 7. 竞品分析 二. 产品分析 1. 产品结构图 2 ...

  10. go-pitaya学习笔记(2)-chatDemo分析

    学习笔记: 我家别墅靠大海/pitaya-learn 尝试集成功能:我家别墅靠大海/pitaya-game 如果你正在看此笔记,请你左边放笔记,右边放chatdemo的代码!! 我是按代码的顺序记的笔 ...

最新文章

  1. 2008秋季-计算机软件基础-有序表合并 教材 P79, ex3
  2. 洛谷 p2066 机器分配(资源型)
  3. python正确的字符串常量_4.1 字符串常量(python)
  4. vmware安装问题:Microsoft Runtime DLL安装程序未能完成安装
  5. linux查看日志文件内容命令tail、cat、tac、head、echo、vi
  6. 【日本软件外包】设计书中常用到的文型
  7. Kindle阅读软件,手机阅读器软件、mobi、azw3、epub格式书籍阅读器软件分享
  8. python之简易飞机大战
  9. SIEBEL配置学习笔记
  10. WIN10重置网络后网络适配器不见了并且适配器驱动感叹号
  11. outlook中网址连接打不开解决办法
  12. java excel 批注_Java 添加、读取和删除 Excel 批注的操作代码
  13. 电脑计算机硬盘怎么加,电脑加装一个硬盘后怎么设置
  14. Vue之watch监听
  15. 淘宝新店运营怎么选品?API接口助您高效选品
  16. yum安装软件时报错:Errors during downloading metadata for repository ‘AppStream‘的处理
  17. PhoneGap移动开发框架
  18. Python 常用的标准库以及第三方库有哪些?
  19. 先卸载 nvidia-387.26驱动,再安装nvidia-384.81 驱动
  20. 澳媒:澳大利亚日盛典即将上演 入籍人数或创新高

热门文章

  1. Java利用递归算法实现24点游戏
  2. Matlab+cpp矩量法代码演示
  3. mac系统怎么打开ftp服务器,mac系统怎么打开ftp服务器
  4. QGIS 导入图层到 PostGIS “导入某些图层失败! 图层“public“.‘xxxx‘载入失败 “
  5. 单射、满射、双射(一一映射)
  6. [VBA][EXCEL]插入分页符
  7. 我的世界玩服务器虚拟内存高崩,服务端崩溃 - 崩溃解答 - MC百科社群 - MC百科|最大的Minecraft中文MOD百科...
  8. python实现web可视化_数据可视化怎么做【python数据可视化的效果如何在web页面中展示】...
  9. 优矿量化向导式因子选股
  10. ezw证件照芯片压缩算法