1.休眠方式

在内核中,休眠方式有很多种,可以通过下面命令查看

# cat /sys/power/state

//来得到内核支持哪几种休眠方式.

常用的休眠方式有freeze,standby, mem, disk

freeze:  冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态,唤醒最快,耗电比其它standby, mem, disk方式高

standby:除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电比其它 mem, disk方式高

mem:      将运行状态数据存到内存,并关闭外设,进入等待模式,唤醒较慢,耗电比disk方式高

disk:      将运行状态数据存到硬盘,然后关机,唤醒最慢

示例:

# echo standby > /sys/power/state

// 命令系统进入standby休眠.

2.唤醒方式

当我们休眠时,如果想唤醒,则需要添加中断唤醒源,使得在休眠时,这些中断是设为开启的,当有中断来,则会退出唤醒,常见的中断源有按键,USB等.

3.以按键驱动为例(基于内核3.10.14)

在内核中,有个input按键子系统"gpio-keys"(位于driver/input/keyboard/gpio.keys.c),该平台驱动platform_driver已经在内核中写好了(后面会简单分析)

我们只需要在内核启动时,注册"gpio-keys"平台设备platform_device,即可实现一个按键驱动.

3.1首先使板卡支持input按键子系统(基于mips君正X1000的板卡)

查看Makefile,找到driver/input/keyboard/gpio.keys.c需要CONFIG_KEYBOARD_GPIO宏

方式1-修改对应板卡的defconfig文件,添加宏:

CONFIG_INPUT=y                            //支持input子系统(加载driver/input文件)

CONFIG_INPUT_KEYBOARD=y             //支持input->keyboards(加载driver/input/keyboard文件)

CONFIG_KEYBOARD_GPIO=y                  //支持input->keyboards->gpio按键(加载gpio.keys.c)

方式2-进入make menuconfig

-> Device Drivers

-> Input device support

->  [*]Keyboards

[*]  GPIO Buttons

3.2修改好后,接下来写my_button.c文件,来注册platform_device

#include

#include

#include

struct gpio_keys_button __attribute__((weak)) board_buttons[] = {

{

.gpio                = GPIO_PB(31),    //按键引脚

.code  = KEY_POWER,                    //用来定义按键产生事件时,要上传什么按键值

.desc                = "power key",    //描述信息,不填的话会默认设置为"gpio-keys"

.wakeup          =1,                  //设置为唤醒源

. debounce_interval =10,                //设置按键防抖动时间,也可以不设置

.type                = EV_KEY,

.active_low    = 1,                  //低电平有效

},

};

static struct gpio_keys_platform_data  board_button_data = {

.buttons  = board_buttons,

.nbuttons        = ARRAY_SIZE(board_buttons),

};

struct platform_device  my_button_device = {

.name              = "gpio-keys",

.id            = -1,

.num_resources      = 0,

.dev          = {

.platform_data      = &board_button_data,

}

};

static int __init button_base_init(void)

{

platform_device_register(&my_button_device);

return 0;

}

arch_initcall(button_base_init);

上面的arch_initcall()表示:

会将button_base_init函数放在内核链接脚本.initcall3.init段中,然后在内核启动时,会去读链接脚本,然后找到button_base_init()函数,并执行它.

通常,在内核中,platform 设备的初始化(注册)用arch_initcall()调用

而驱动的注册则用module_init()调用,因为module_init()在arch_initcall()之后才调用

因为在init.h中定义:

#define pure_initcall(fn)                  __define_initcall(fn, 0)

#define core_initcall(fn)                  __define_initcall(fn, 1)

#define core_initcall_sync(fn)                __define_initcall(fn, 1s)

#define postcore_initcall(fn)          __define_initcall(fn, 2)

#define postcore_initcall_sync(fn)        __define_initcall(fn, 2s)

#define arch_initcall(fn)        __define_initcall(fn, 3)            // arch_initcall()优先级为3,比module_init()先执行

#define arch_initcall_sync(fn)                __define_initcall(fn, 3s)

#define subsys_initcall(fn)              __define_initcall(fn, 4)

#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)

#define fs_initcall(fn)                      __define_initcall(fn, 5)

#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)

#define rootfs_initcall(fn)              __define_initcall(fn, rootfs)

#define device_initcall(fn)              __define_initcall(fn, 6)                    //module_init()优先级为6

#define device_initcall_sync(fn)    __define_initcall(fn, 6s)

#define late_initcall(fn)          __define_initcall(fn, 7)

#define late_initcall_sync(fn)                  __define_initcall(fn, 7s)

... ...

#define  __initcall(fn)  device_initcall(fn)

#define  module_init(x)  __initcall(fn)         //module_init 等于 device_initcall

3.3然后将my_button.c文件添加到Makefile中

编译内核后,便实现一个简单的按键唤醒休眠了.

接下来开始分析platform_driver(位于driver/input/keyboard/gpio.keys.c),看看是如何注册按键和实现唤醒的.

4.分析driver/input/keyboard/gpio.keys.c

4.1该文件里有常用的函数有

static int gpio_keys_probe(struct platform_device *pdev);

设置按键和input_dev,注册input-key子系统

static int gpio_keys_setup_key(struct platform_device *pdev,struct input_dev *input, struct gpio_button_data *bdata,const struct gpio_keys_button *button);

设置GPIO,设置input结构体支持的按键值,设置中断,设置防抖动机制

static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id);

按键中断函数,如果是中断源,则通过pm_stay_awake()通知pm子系统唤醒,如果有防抖动,则延时并退出,否则通过schedule_work()来调用gpio_keys_gpio_work_func()一次

static void gpio_keys_gpio_timer(unsigned long _data);

定时器超时处理函数,用来实现防抖动,里面会通过schedule_work()来调用一次gpio_keys_gpio_work_func();

static void gpio_keys_gpio_work_func(struct work_struct *work);

处理gpio事件函数,用来上报input事件,并判断按键中断源,如果是的话,则调用pm_relax(),通知pm子系统唤醒工作结束

void pm_wakeup_event(struct device *dev, unsigned int msec);

通知pm(power manager), 唤醒休眠

static int gpio_keys_suspend(struct device *dev);

休眠函数,休眠之前会被调用

static int gpio_keys_resume(struct device *dev);

唤醒函数,唤醒之前被调用

static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);

SIMPLE_DEV_PM_OPS宏位于pm.h,它将会定义一个dev_pm_ops结构体,用来被pm子系统调用,实现休眠唤醒

4.2 首先来看probe函数

如下图所示,probe函数为gpio_keys_probe()

gpio_keys_probe()函数定义如下所示:

static int gpio_keys_probe(struct platform_device *pdev)

{

struct device *dev = &pdev->dev;                          //获取平台设备的.dev

const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);  //获取my_button.c文件的board_button_data成员

struct gpio_keys_drvdata *ddata;                          //按键驱动数据

const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);      //获取平台总线设备数据

if (!pdata) {

pdata = gpio_keys_get_devtree_pdata(dev);

if (IS_ERR(pdata))

return PTR_ERR(pdata);}

ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +

pdata->nbuttons * sizeof(struct gpio_button_data),

GFP_KERNEL);                                          //给平台设备数据分配空间

input = input_allocate_device();                        //分配input 按键子系统

if (!ddata || !input) {

dev_err(dev, "failed to allocate state\n");

error = -ENOMEM;

goto fail1;

}

ddata->pdata = pdata;

ddata->input = input;

mutex_init(&ddata->disable_lock);

platform_set_drvdata(pdev, ddata);

//将ddata保存到平台总线设备的私有数据。以后只需调用platform_get_drvdata()就能获取驱动数据

//设置pdev->dev->p结构体成员下的数据

//令pdev->dev->p->device = &pdev->dev

//pdev-> dev->p->driver_data = ddata

input_set_drvdata(input, ddata);

//将ddata保存到要注册的按键子系统驱动的私有数据中。以后只需调用input_get_drvdata()就能获取驱动数据

//设置input ->dev->p结构体成员下的数据

//令input ->dev->p->device = &pdev->dev

// input ->p->driver_data = ddata

input->name = pdata->name ? : pdev->name;          //等于"gpio-keys"

input->phys = "gpio-keys/input0";          //input 按键子系统处于/sys目录下的哪个路径

input->dev.parent = &pdev->dev;

input->open = gpio_keys_open;            //input打开操作,用来正常唤醒调用

input->close = gpio_keys_close;            //input关闭操作,用来正常休眠调用

input->id.bustype = BUS_HOST;            //设置总线

input->id.vendor = 0x0001;

input->id.product = 0x0001;

input->id.version = 0x0100;

/* Enable auto repeat feature of Linux input subsystem */

if (pdata->rep)

__set_bit(EV_REP, input->evbit);

for (i = 0; i < pdata->nbuttons; i++) {

const struct gpio_keys_button *button = &pdata->buttons[i];  //获取每个按键

struct gpio_button_data *bdata = &ddata->data[i];

error = gpio_keys_setup_key(pdev, input, bdata, button);// gpio_keys_setup_key()里会执行:

//赋值按键,使 bdata->button = button

//通过gpio_request_one()来申请button->gpio管脚为输入模式

//判断 button->debounce_interval成员,是否设置防抖动时间

//获取按键对应的中断号,并赋值给bdata->irq

//通过__set_bit()来让input 按键子系统支持button->code

//通过setup_timer()设置bdata->timer结构体对应的超时函数

//通过request_any_context_irq()函数注册按键中断:

// ----> 中断号为bdata->irq,中断名叫: button.desc("power key")

// ----> 中断标志位为(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

// ----> 中断服务函数为gpio_keys_gpio_isr(),设置中断函数参数dev_id为bdata

if (error)

goto fail2;

if (button->wakeup)                //判断该按键是否是唤醒源

wakeup = 1;

}

error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);  //在/sys/devices/platform/gpio-keys下创建sys设备节点

if (error) {

dev_err(dev, "Unable to export keys/switches, error: %d\n",

error);

goto fail2;

}

error = input_register_device(input);  //注册input按键子系统

if (error) {

dev_err(dev, "Unable to register input device, error: %d\n",

error);

goto fail3;

}

device_init_wakeup(&pdev->dev, wakeup);        //如果按键有设置唤醒源,则设置标志位

}

4.3其中device_init_wakeup()函数定义如下:

static inline int device_init_wakeup(struct device *dev, bool val)

{

device_set_wakeup_capable(dev, val);  //设置dev->power.can_wakeup = val;

device_set_wakeup_enable(dev, val);  //设置dev->power.should_wakeup = val;

return 0;

}

然后休眠唤醒的时候,就会根据dev->power.can_wakeup和dev->power.should_wakeup来做不同的操作

4.4 其中gpio_keys_suspend()休眠函数定义如下所示:

static int gpio_keys_suspend(struct device *dev)

{

struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);

struct input_dev *input = ddata->input;

int i;

if (device_may_wakeup(dev)) //判断dev->power.can_wakeup和dev->power.should_wakeup,是否有中断源按键

{

for (i = 0; i < ddata->pdata->nbuttons; i++) //如果有,则遍历每个按键

{

struct gpio_button_data *bdata = &ddata->data[i];

if (bdata->button->wakeup)

enable_irq_wake(bdata->irq);  //将要睡眠的中断号屏蔽掉,实现休眠时保持中断唤醒

}

}

else

{

mutex_lock(&input->mutex);

if (input->users)

gpio_keys_close(input); //调用dev->platform_data-> disable成员函数

mutex_unlock(&input->mutex);

}

return 0;

}

从上面函数可以看到,进入休眠之前,我们需要调用enable_irq_wake()来设置唤醒源

4.5 然后在中断函数中,判断是否需要上报唤醒事件,中断函数如下所示:

static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)

{

struct gpio_button_data *bdata = dev_id;

BUG_ON(irq != bdata->irq);

if (bdata->button->wakeup)

pm_stay_awake(bdata->input->dev.parent);      //如果是唤醒源,则通知pm子系统,处理唤醒事件,并等待结束

if (bdata->timer_debounce)

mod_timer(&bdata->timer,jiffies + msecs_to_jiffies(bdata->timer_debounce));//如果设置防抖动,则启动定时器并退出

else

schedule_work(&bdata->work);  //否则调用bdata->work对应的函数gpio_keys_gpio_work_func()

return IRQ_HANDLED;

}

其中gpio_keys_gpio_work_func()函数如下所示:

static void gpio_keys_gpio_work_func(struct work_struct *work)

{

struct gpio_button_data *bdata= container_of(work, struct gpio_button_data, work);

gpio_keys_gpio_report_event(bdata); //上传input按键事件

if (bdata->button->wakeup)

pm_relax(bdata->input->dev.parent);      //如果是唤醒源,则通知pm子系统,唤醒中断处理结束。

}

从上面两个函数可以看到,唤醒休眠时,需要使用两个函数实现:

pm_stay_awake();                  //在中断入口调用,告知启动唤醒

pm_relax();                        //在中断出口调用,告知结束唤醒

在中断前调用pm_stay_awake(),中断结束时再调用一次pm_relax()函数.

4.6 如果想延时唤醒,也可以使用另一种唤醒休眠,则只需要一个函数实现:

pm_wakeup_event(struct device *dev, unsigned int msec);

//通知pm子系统在msec后处理唤醒事件, msec=0,则表示立即唤醒

4.7 接下来来看gpio_keys_setup_key(),如何设置按键的(只加了重要的部分)

static int gpio_keys_setup_key(struct platform_device *pdev,

struct input_dev *input,

struct gpio_button_data *bdata,

const struct gpio_keys_button *button)

{

const char *desc = button->desc ? button->desc : "gpio_keys"; //获取平台设备设置的名字

//… …

error = gpio_request_one(button->gpio, GPIOF_IN, desc);//申请button->gpio引脚,并将引脚设为输入引脚,名字设置为desc

if (button->debounce_interval)

{

bdata->timer_debounce =button->debounce_interval;      //设置防抖动时间

}

irq = gpio_to_irq(button->gpio);                    //获取管脚对应的中断号

if (irq < 0)

{

//… …

goto fail;

}

bdata->irq = irq;

INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);

//初始化bdata->work,使bdata->work与gpio_keys_gpio_work_func()函数关联起来

//后面当调用schedule_work(&bdata->work)时,便会执行gpio_keys_gpio_work_func()函数

setup_timer(&bdata->timer, gpio_keys_gpio_timer, (unsigned long)bdata);

//设置gpio_keys_gpio_timer()定时器超时函数,用来实现防抖动,函数参数为bdata

irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

//中断标志位

isr = gpio_keys_gpio_isr;

input_set_capability(input, button->type ?: EV_KEY, button->code); //使input 支持EV_KEY键盘事件,并使键盘事件支持button->code按键值

error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata);

//通过request_any_context_irq()函数注册按键中断:

//中断号为bdata->irq,中断名叫: button.desc("power key")

//中断标志位为(IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

//中断服务函数为gpio_keys_gpio_isr(),设置中断函数参数dev_id为bdata

return 0;

}

通过gpio.keys.c,得出唤醒流程:

休眠时:

enable_irq_wake (bdata->irq);

//将要睡眠的中断号屏蔽掉,实现休眠时保持中断唤醒

唤醒后:

disable_irq_wake(bdata->irq);      //关闭唤醒

中断时,有两种唤醒PM模式

模式1-使用两个函数实现:

•进入中断时调用一次pm_stay_awake().

•退出时也调用一次pm_relax(bdata->input->dev.parent);

模式2-只需一个函数实现:

•进入中断时调用pm_wakeup_event(struct device *dev, unsigned int msec).

5.接下来,我们自己写个���键字符驱动,实现休眠唤醒

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define MYKEY_GPIO GPIO_PB(31)

static DECLARE_WAIT_QUEUE_HEAD(mykey_waitqueue);

struct mykey_button {

unsigned int gpio;

const char *desc;

int wakeup;                                /*唤醒源*/

int debounce_interval;                    /* 防抖动 时间ms*/

int wait_event;                            /*等待队列事件*/

int key_val;                              /*按键值*/

int irq;

struct timer_list timer;                  /*防抖动定时器*/

struct work_struct work;

struct device *dev;

};

static struct mykey_button mykey_data={

.gpio = MYKEY_GPIO,

.desc = "mykey",

.wakeup = 1,

.debounce_interval = 10,                //10ms

.wait_event = 0,

};

static void mykey_func(struct work_struct *work)

{

struct mykey_button *data = container_of(work, struct mykey_button, work);  //通过work成员变量找到父结构体

if(data->wakeup)

{

pm_wakeup_event(data->dev, 0);

}

data->key_val =gpio_get_value(data->gpio);

data->wait_event =1;

wake_up_interruptible(&mykey_waitqueue);                        //唤醒队列

}

static void mykey_irq_timer(unsigned long _data)

{

struct mykey_button *data  =(struct mykey_button *)_data;

schedule_work(&data->work);                                        //调用mykey_func()函数

}

static irqreturn_t  mykey_irq(int irq, void *dev_id)

{

struct mykey_button *data =  dev_id;

if(data->debounce_interval)

mod_timer(&data->timer, jiffies+msecs_to_jiffies(10));

else

schedule_work(&data->work);

return IRQ_HANDLED;

}

static ssize_t  mykey_read(struct file *file, char __user *user, size_t count, loff_t *ppos)

{

wait_event_interruptible(mykey_waitqueue,mykey_data.wait_event );            //进入等待队列休眠,如果中断来数据,则跳出

copy_to_user(user, &mykey_data.key_val, sizeof(mykey_data.key_val));

mykey_data.wait_event =0;

return 0;

}

static int mykey_open(struct inode *inode, struct file *file)

{

int err=0;

int irq;

err=gpio_request_one(mykey_data.gpio, GPIOF_DIR_IN,  mykey_data.desc);                //获取管脚,并设置管脚为输入

if (err < 0) {

printk("mykey_open err : gpio_request_one  err=%d\n",err);

return -EINVAL;

}

irq = gpio_to_irq(mykey_data.gpio);                      //获取IRQ中断号,用来注册中断

if(irq<0)

{

err =irq;

printk("mykey_open err : gpio_to_irq err=%d\n",irq);

goto fail;

}

mykey_data.irq = irq;

INIT_WORK(&mykey_data.work, mykey_func);            //初始化工作队列

err=request_irq(irq,mykey_irq,IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING, mykey_data.desc,&mykey_data);

if (err) {

printk("mykey_open err : request_irq err=%d\n",err);

goto fail;

}

if(mykey_data.wakeup)

enable_irq_wake(irq);                      //将引脚设为唤醒源

if(mykey_data.debounce_interval)

setup_timer(&mykey_data.timer, mykey_irq_timer, (unsigned long)&mykey_data);  //设置定时器

add_timer(&mykey_data.timer);

return 0;

fail:

if (gpio_is_valid(mykey_data.gpio))

gpio_free(mykey_data.gpio);

return err;

}

static int mykey_release(struct inode *inode, struct file *file)

{

free_irq(mykey_data.irq,&mykey_data);

cancel_work_sync(&mykey_data.work);

if(mykey_data.wakeup)

disable_irq_wake(mykey_data.irq);

if(mykey_data.debounce_interval)

del_timer_sync(&mykey_data.timer);

gpio_free(mykey_data.gpio);

return 0;

}

struct file_operations mykey_ops={

.owner  = THIS_MODULE,

.open    = mykey_open,

.read    =  mykey_read,

.release=mykey_release,

};

static int major;

static struct class *cls;

static int mykey_init(void)

{

struct device *mydev;

major=register_chrdev(0,"mykey", &mykey_ops);

cls=class_create(THIS_MODULE, "mykey");

mydev = device_create(cls, 0, MKDEV(major,0),&mykey_data,"mykey");

mykey_data.dev = mydev;

return 0;

}

static void mykey_exit(void)

{

device_destroy(cls, MKDEV(major,0));

class_destroy(cls);

unregister_chrdev(major, "mykey");

}

module_init(mykey_init);

module_exit(mykey_exit);

MODULE_LICENSE("GPL");

应用测试代码如下:

#include

#include

#include

#include

#include

int main(int argc,char **argv)

{

int fd,ret;

unsigned int val=0;

fd=open("/dev/mykey",O_RDWR);

if(fd<0)

{printf("can't open!!!\n");

return -1;}

while(1)

{

ret=read(fd,&val,1);          //读取一个值,(当在等待队列时,本进程就会进入休眠状态)

if(ret<0)

{

printf("read err!\n");

continue;

}

printf("key_val=%d\r\n",val);

}

return 0;

}

试验:

./mykey_text &

echo mem > /sys/power/state      //然后按GPB31对应的按键来唤醒休眠

linux待机唤醒_Linux电源管理-休眠与唤醒相关推荐

  1. 休眠后gpio状态_1.Linux电源管理-休眠与唤醒

    1.休眠方式 在内核中,休眠方式有很多种,可以通过下面命令查看 常用的休眠方式有freeze,standby, mem, disk freeze:冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲 ...

  2. linux 电源管理源码分析,Linux 3.8.1 电源管理之OMAP Voltage Domain分析

    本系列文章将分析Linux对于OMAP的电源管理功能,以AM33XX作为实例(目前的硬件平台先主要关注AM335xStarterKit开发板),在必要时穿插其他相关内容. 在linux-3.8.1/a ...

  3. 嵌入式linux系统电源管理器,嵌入式Linux系统的动态电源管理技 - 嵌入式操作系统 - 电子发烧友网...

    嵌入式Linux系统的动态电源管理技术 通过用户层制定策略与内核提供管理功能交互,实时调整电源参数而同时满足系统实时应用的需求,允许电源管理参数在短时间的空闲或任务运行在低电源需求时,可以被频繁地.低 ...

  4. linux 电源管理 唤醒,Android 4.0 中的PowerManager(电源管理--休眠唤醒) [转]

    转发请注明出处: 最近,在学习让系统进入休眠的过程中,学习了电源管理的相关知识.为了备忘和理清思路现整理成文,以便日后查阅. 移动设备由于自身的限制有一个硬伤就是电量的消耗问题.由于电池的电量有限,所 ...

  5. linux驱动编写(电源管理驱动)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 对于嵌入式设备来说,合适的电源管理,不仅可以延长电池的寿命,而且可以省电,延长设备运行时间,在提 ...

  6. linux内核acpi,Linux内核中ACPI电源管理部分解析

    ACPI包括很多功能,电源管理是其功能之一,具体的ACPI的介绍可以参考ACPI的技术文档. Linux中利用模块机制,实现ACPI对电源的管理: static struct cpufreq_driv ...

  7. linux 如何设置待机时间_linux 设置系统休眠时间

    显示当前系统正在执行的进程的相关信息,包括进程 ID.内存占用率.CPU 占用率等 常用参数: -c 显示完整的进程命令 -s 保密模式 -p 指定进程显示 -n 循环显示次数 1 2 3 4 实例: ...

  8. linux 休眠定时唤醒_LINUX 休眠唤醒问题

    问题:LINUX中,通过电源管理驱动(自行开发!)调用LINUX内/kernel/power/内的功能函数,实现将系统资源SUSPENDING,后等待外部中断(按鍵或USB等)或定时中断,把系统唤醒. ...

  9. 关闭linux服务器电源,linux关闭ACPI电源管理模块

    一.运行环境 # cat /etc/redhat-release CentOS release 6.2 (Final) # uname -a Linux web-server- 2.6.-.el6.x ...

最新文章

  1. 文件流下载到本地 - 待完成
  2. HTTP 方法:GET 对比 POST
  3. 很多网站,软件对自定义的dpi支持不好
  4. eplise怎么连接数据库_eclipse连接mysql
  5. Linux基础知识之用户和用户组以及 Linux 权限管理
  6. c++ regex用法实例
  7. Java中直接输出一个类的对象
  8. 欢迎大家关注我的博客,让我们一起在知识的海洋中并肩自由翱翔!
  9. PowerShell: 如何使用powershell处理Excel
  10. 【每天一个 Linux 命令】tree命令
  11. 基于JAVA的教务排课系统毕业设计
  12. 摩尔庄园服务器维护,《摩尔庄园》6月11日维护公告
  13. 【模拟电路】电源效率与散热
  14. pytorch 计算模型的GFlops和total params的方法
  15. Html开屏广告源码,开屏(Splash)广告样式
  16. Ansible系列-基础篇-Ansible Inventory的合理化配置
  17. 小程序源码:uni-app云开发的网盘助手-多玩法安装简单
  18. 编程范式:函数式编程防御式编程响应式编程契约式编程流式编程
  19. 哲学家进餐_我如何通过预算学习计划进餐
  20. 嵌入式系统概述2-嵌入式系统组成和应用

热门文章

  1. linux_ rpm管理(Synaptic Package Manager)
  2. fitbit android app,Fitbitapp下载_Fitbit安卓版下载[生活服务]-下载之家
  3. 一图看懂 pytz 模块:现代以及历史版本的世界时区定义数据库,资料整理+笔记(大全)
  4. 红白机基本原理(二) CPU
  5. 大话卫星导航中的信号处理系列文章——全球卫星导航系统介绍
  6. Altium designer软件常用最全封装库,包含原理图库、PCB库和3D模型库
  7. 深度学习笔记(五) 代价函数的梯度求解过程和方法
  8. golang学习笔记(基础篇)
  9. 算法日记(十三)之动态规划
  10. 谷歌seo新站如何快速排名?如何提高Google自然排名