http://blog.csdn.net/fanqipin/article/details/8089995

一. RTC及驱动简介

RTC即real time clock实时时钟,主要用于为操作系统提供可靠的时间;当系统处于断电 的情况下,RTC记录操作系统时间,并可在电池供电情况下继续正常工作,当系统正常启动后,系统可从RTC读取时间信息,来确保断电后时间运行连续性。

目前,很多CPU中都已集成RTC系统,且有许多独立的外接RTC芯片可用于实现RTC功能;

在内核中RTC驱动可分为两层,一层为于硬件无关的抽象层,主要用于管理RTC设备、设备节点,属性节点注册及操作;另一层为与硬件相关的RTC低层驱动层;抽象层程序在/drivers/rtc目录下,主要涉及下面几个文件:

class.c   用于管理和注册RTC设备结构、sysfs、procfs及RTC类;

rtc-dev.c 用于注册和管理RTC设备节点,为用户空间提供devfs操作接口,主要操作有rtc_read,rtc_ioctl;

rtc-proc.c  用于管理rtc的procfs属性节点,提供一些中断状态、标志查询;

rtc-sysfs.c 用于管理rtc设备的sysfs属性,如获取RTC设备名字、日期、时间等属性信息;

interface.c 为rtc-dev.c 和RTC低层驱动提供操作接口;

RTC系统结构示意图如图1所示;

图1 RTC系统结构图

如图1所示,RTC 设备驱动通过rtc_device_register接口向linux系统注册RTC设备,并成生proc、sys目录下的属性文件;当用户通过/dev/rtcx设备节点或proc、sysfs接口来操作RTC设备时,都需要先通过interface接口才能访问真实的设备驱动。

二. 部分数据结构分析

1. RTC设备结构

struct rtc_device
{

struct device dev;

struct module *owner;

int id;   设备编号

char name[RTC_DEVICE_NAME_SIZE];

const struct rtc_class_ops *ops;  rtc设备低层操作接口;

struct mutex ops_lock;

struct cdev char_dev;    RTC字符型设备结构;

unsigned long flags;

unsigned long irq_data;    中断数据;

spinlock_t irq_lock;

wait_queue_head_t irq_queue;   中断数据等待队列;

struct fasync_struct *async_queue;

struct rtc_task *irq_task;

spinlock_t irq_task_lock;

int irq_freq;         中断频率;

int max_user_freq;

struct timerqueue_head timerqueue;

struct rtc_timer aie_timer;                       报警中断定时器;

struct rtc_timer uie_rtctimer;                  更新中断定时器;

struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */     周期中断高精度定时器;

int pie_enabled;             周期中断使能标志;

struct work_struct irqwork;

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL    内部仿真update interrupt时所需;

struct work_struct uie_task;

struct timer_list uie_timer;

/* Those fields are protected by rtc->irq_lock */

unsigned int oldsecs;

unsigned int uie_irq_active:1;

unsigned int stop_uie_polling:1;

unsigned int uie_task_active:1;

unsigned int uie_timer_active:1;

#endif
};

2. RTC设备低层操作接口

struct rtc_class_ops {

int (*open)(struct device *);                           打开设备

void (*release)(struct device *);                    释放设备

int (*ioctl)(struct device *, unsigned int, unsigned long);

int (*read_time)(struct device *, struct rtc_time *);   读取RTC时间;

int (*set_time)(struct device *, struct rtc_time *);    设置RTC时间;

int (*read_alarm)(struct device *, struct rtc_wkalrm *);  读取RTC报警时间;

int (*set_alarm)(struct device *, struct rtc_wkalrm *);   设置RTC报警时间;

int (*proc)(struct device *, struct seq_file *);                    用于提供procfs查询rtc状态接口;

int (*set_mmss)(struct device *, unsigned long secs);   设置以S为单位RTC时间接口;

int (*read_callback)(struct device *, int data);

int (*alarm_irq_enable)(struct device *, unsigned int enabled);  中断使能接口;

};

三.RTC低层驱动实现

        RTC低层驱动实现相对比较简单,只需要通过rtc_device_register向系统注册RTC设备,并实现RTC低层操作接口;本文以samsung s3cxx系列中自带的RTC来进行分析,驱动为/drivers/rtc/rtc-s3c.c。

1. 设备驱动注册及卸载

        对于集成在CPU内部的RTC一般可通过注册平台驱动的方式进行注册,如果RTC为外扩芯片,则可以根据相应来的接口来选择注册方式,如I2C接口芯片DS1307通过i2c_add_driver方式进行注册;在S3C6410中RTC为集成在系统内部,所以通过平台方式注册,平台驱动结构及注册卸载函数接口如下所示:
static struct platform_driver s3c_rtc_driver ={
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.id_table = s3c_rtc_driver_ids,  用于匹配平台设备
.driver = {
.name = "s3c-rtc",

.owner = THIS_MODULE,

},

}
static int __init s3c_rtc_init(void)
{

return platform_driver_register(&s3c_rtc_driver)

}
static void __exit s3c_rtc_exit(void)
{

platform_drvier_unregister(&s3c_rtc_driver)

}
module_init(s3c_rtc_init);
mosule_exit(s3c_rtc_exit)
        当linux系统启动后会自动将S3C RTC平台设备驱动添加到系统中,然后会遍历platform 总线上的设备通过match_table,id_table或驱动和设备名字来进行匹配设备,如果找到对应的平台设备就会调用平台驱动中的probe函数;S3C RTC平台设备结构如下所示:
static struct resource s3c_rtc_resource[] = {

[0] = {

.start  = S3C_PA_RTC,                                          RTC寄存器起始地址空间;

.end  = S3C_PA_RTC + 0xff,

.flags  = IORESOURCE_MEM,                                resource为内存类型

},
[1] = {
.start  = IRQ_RTC_ALARM,                                    RTC报警中断号

.end  = IRQ_RTC_ALARM,

.flags  = IORESOURCE_IRQ,                                     resource为中断类型

},
[2] = {
.start  = IRQ_RTC_TIC,                                            RTC TICK中断

.end  = IRQ_RTC_TIC,

.flags  = IORESOURCE_IRQ

}

};
struct platform_device s3c_device_rtc = {
.name  = "s3c64xx-rtc",
.id   = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};
通过platform的match函数匹配完后就会调用平台驱动的probe函数,s3c_rtc_probe函数主要部分如下所示:
static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct rtc_time rtc_tm;
struct resource *res;
int ret;
pr_debug("%s: probe=%p\n", __func__, pdev);
/* 从平台设备中获取相关的设备资源*/
s3c_rtc_tickno = platform_get_irq(pdev, 1);                         
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev, "no irq for rtc tick\n");

return -ENOENT;

}
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev, "no irq for alarm\n");

return -ENOENT;

}
/*内核中,在对内存进行操作前得先申请内存空间,通过ioremap映射完后才能使用*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory region resource\n");

return -ENOENT;

}
s3c_rtc_mem = request_mem_region(res->start, resource_size(res), pdev->name);
if (s3c_rtc_mem == NULL) {
dev_err(&pdev->dev, "failed to reserve memory region\n");

ret = -ENOENT;

goto err_nores;

}
s3c_rtc_base = ioremap(res->start, resource_size(res));
if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");

ret = -EINVAL;

goto err_nomap;

}
/*获取并使能RTC 系统时钟信号*/
rtc_clk = clk_get(&pdev->dev, "rtc");
if (IS_ERR(rtc_clk)) {
dev_err(&pdev->dev, "failed to find rtc clock source\n");

ret = PTR_ERR(rtc_clk);

rtc_clk = NULL;

goto err_clk;

}
clk_enable(rtc_clk);
/* 配置S3C RTC 控制寄存器,使能RTCEN,只有当RTCEN有效时才能配置BCD等寄存器,当系统断电前得清除RTCEN,防止对BCD寄存器进行写操作; */
s3c_rtc_enable(pdev, 1);
device_init_wakeup(&pdev->dev, 1);
/*注册RTC设备,s3c_rtcops为需要实现的低层操作接口*/
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");

ret = PTR_ERR(rtc);

goto err_nortc;

}
s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
/* Check RTC Time */
s3c_rtc_gettime(NULL, &rtc_tm);
if (rtc_valid_tm(&rtc_tm)) {
rtc_tm.tm_year = 100;

rtc_tm.tm_mon = 0;

rtc_tm.tm_mday = 1;

rtc_tm.tm_hour = 0;

rtc_tm.tm_min = 0;

rtc_tm.tm_sec = 0;

s3c_rtc_settime(NULL, &rtc_tm);

dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");

}
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
rtc->max_user_freq = 32768;

else

rtc->max_user_freq = 128;

platform_set_drvdata(pdev, rtc);
/*设置时钟频率为1HZ*/
s3c_rtc_setfreq(&pdev->dev, 1);
clk_disable(rtc_clk);
return 0;

err_nortc:

s3c_rtc_enable(pdev, 0);
clk_disable(rtc_clk);
clk_put(rtc_clk);
 err_clk:

iounmap(s3c_rtc_base);

err_nomap:

release_resource(s3c_rtc_mem);

err_nores:

return ret;

}
2. RTC低层操作函数接口
static const struct rtc_class_ops s3c_rtcops = {
.open  = s3c_rtc_open,
.release  = s3c_rtc_release,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc  = s3c_rtc_proc,
.alarm_irq_enable = s3c_rtc_setaie,
};
static int s3c_rtc_open(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
int ret;
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,IRQF_DISABLED,  "s3c2410-rtc alarm", rtc_dev);
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);

return ret;

}
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq, IRQF_DISABLED,  "s3c2410-rtc tick", rtc_dev);
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
goto tick_err;
}
return ret;

tick_err:

free_irq(s3c_rtc_alarmno, rtc_dev);
return ret;
}
static void s3c_rtc_release(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
free_irq(s3c_rtc_alarmno, rtc_dev);
free_irq(s3c_rtc_tickno, rtc_dev);
}
       在s3c_rtc_open()和s3c_rtc_release()用于申请和释放s3c rtc 的报警和TICK中断资源;因为中断信号线资源有限,所以最好在打开设备时申请,关闭设备时释放;
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
unsigned int have_retried = 0;
void __iomem *base = s3c_rtc_base;
clk_enable(rtc_clk);
 retry_get_time:

/*读取年、月、日、天、小时、分、秒数据*/

rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec  = readb(base + S3C2410_RTCSEC);
if (rtc_tm->tm_sec == 0 && !have_retried) {
have_retried = 1;

goto retry_get_time;

}

/*将读取出来的数据进行BCD转二进制操作*/

rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
rtc_tm->tm_year += 100;
rtc_tm->tm_mon -= 1;
clk_disable(rtc_clk);
return rtc_valid_tm(rtc_tm);
}
s3c_rtc_gettime用于从S3C RTC里读出RTC 日期和时间,RTC时间1S更新一次,在读时间的过程中如果秒为0,表示从秒从59到0跳变,则需要重新读取时间,S3C RTC的时候为BCD码形式,需要进行简单的转换才能得到正确时间信息。
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
void __iomem *base = s3c_rtc_base;
int year = tm->tm_year - 100;
clk_enable(rtc_clk);
/* we get around y2k by simply not supporting it */
if (year < 0 || year >= 100) {
dev_err(dev, "rtc only supports 100 years\n");

return -EINVAL;

}
/*将时间数据转成BCD码形式后存入到相应寄存器*/
writeb(bin2bcd(tm->tm_sec),  base + S3C2410_RTCSEC);
writeb(bin2bcd(tm->tm_min),  base + S3C2410_RTCMIN);
writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);
writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
clk_disable(rtc_clk);
return 0;
}
s3c_rtc_settime()设置RTC时间,在应用层用hwclock -w来进行同步系统和RTC时间时会调用该函数。 
static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *alm_tm = &alrm->time;
void __iomem *base = s3c_rtc_base;
unsigned int alm_en;
clk_enable(rtc_clk);
alm_tm->tm_sec  = readb(base + S3C2410_ALMSEC);
alm_tm->tm_min  = readb(base + S3C2410_ALMMIN);
alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
alm_tm->tm_mon  = readb(base + S3C2410_ALMMON);
alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);
alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
alm_en = readb(base + S3C2410_RTCALM);
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
/* decode the alarm enable field */
if (alm_en & S3C2410_RTCALM_SECEN)
alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);

else

alm_tm->tm_sec = -1;

if (alm_en & S3C2410_RTCALM_MINEN)
alm_tm->tm_min = bcd2bin(alm_tm->tm_min);

else

alm_tm->tm_min = -1;

if (alm_en & S3C2410_RTCALM_HOUREN)

alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);

else

alm_tm->tm_hour = -1;

if (alm_en & S3C2410_RTCALM_DAYEN)

alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);

else

alm_tm->tm_mday = -1;

if (alm_en & S3C2410_RTCALM_MONEN) {

alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);

alm_tm->tm_mon -= 1;

} else {

alm_tm->tm_mon = -1;

}
if (alm_en & S3C2410_RTCALM_YEAREN)
alm_tm->tm_year = bcd2bin(alm_tm->tm_year);

else

alm_tm->tm_year = -1;

clk_disable(rtc_clk);
return 0;
}
       s3c_rtc_getalarm()函数读 ALARM 中BCD码时间信息,并根据ALARM控制寄存器的值来进行BCD转二进制,如果ALARM控制寄存器中使能相应位,则进行BCD转换,否则相应数据填-1处理;
static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *tm = &alrm->time;
void __iomem *base = s3c_rtc_base;
unsigned int alrm_en;
clk_enable(rtc_clk);
alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
writeb(0x00, base + S3C2410_RTCALM);
if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
alrm_en |= S3C2410_RTCALM_SECEN;

writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC);

}
if (tm->tm_min < 60 && tm->tm_min >= 0) {
alrm_en |= S3C2410_RTCALM_MINEN;

writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN);

}
if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
alrm_en |= S3C2410_RTCALM_HOUREN;

writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);

}
writeb(alrm_en, base + S3C2410_RTCALM);
s3c_rtc_setaie(dev, alrm->enabled);
clk_disable(rtc_clk);
return 0;
}
s3c_rtc_setalarm()检查填入的报警时间是否合法,如果合法则将时间信息存入ALARM时间寄存器,并使能小时、分、秒相应ALARM使能位;当正常BCD寄存器里的时间和ALARM寄存器里的时间相等时,发生报警中断 ;中断发生后就会调用在s3c_rtc_open函数中申请alarm中断服务程序s3c_rtc_alarmirq();
static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
{
struct rtc_device *rdev = id;
clk_enable(rtc_clk);
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
clk_disable(rtc_clk);
return IRQ_HANDLED;
}
s3c_rtc_alarmirq()中断服务程序主要通过rtc_update_irq来更新中断数据;

3. 使用过程中出现问题及解决方法

       在使用hwclock命令进行同步系统和硬件时间时,调试口输出“select() to /dev/rtc0 to wait for clock tick timed out.”;后来跟踪hwclock源码发现,原来在用hwclock进行显示,设置RTC硬件时间之前都会先通过进行时钟tick同步,同步方法是开启1S 一次update interrupt,每过1S RTC都会产生一次报警中断,并更新相应的中断数据,在应用层通过select来监控是否有RTC数据可读,如果有数据则会继续显示或设置硬件时间操作,否则输出

“select() to /dev/rtc0 to wait for clock tick timed out.”

linux系统中/proc/interrupt可以用来查询中断号,中断使用次数等信息,由于S3C RTC中的中断申请是在s3c_rtc_open中,所以每次使用完就会在s3c_rtc_release中释放,所以无法在/proc/interrupt中查询到rtc相应信息,为了确定是否有中断产生,把中断申请和释放移到s3c_rtc_probe和s3c_rtc_remove函数中。更新程序后发现S3C RTC中的alarm 和tick中断相应的次数都为0,说明根本就没有产生中断。后来经过测试和分析发现,原来S3C RTC中的低层操作接口在每次设置完相应的寄存器后都会关闭RTC时钟,即clk_disable(rtc_clk);而没有RTC时钟就不会产生相应ALARM 和TICK中断,所以解决的方法就是使能RTC时钟;解决方法是在打开 RTC设备时,就一直使能时钟直到关闭设备;

        后来在看源码修改历史时,发现源码中已经解决并更新了这个BUG,有兴趣的可以去看源码修改记录;

RTC系统的更多相关文章

  1. RTC系统【转】

    http://blog.csdn.net/fanqipin/article/details/8089995 转自:http://www.cnblogs.com/muhuacat/p/5276306.h ...

  2. [RTC]系统时间NTP和RTC同步,Debian的时区配置

    转自:http://www.cnblogs.com/jiu0821/p/5999566.html Debian的时区配置 一. 修改时区 1. 查看当前时区 命令 : "date -R&qu ...

  3. 吃我一记咸鱼突刺——使用板载RTC定时开机

    前言 原创文章,转载引用务必注明链接.水平有限,欢迎指正. 2016年3月30日 Lemuntu(Base On Jessie) 3.10.37 原载于Lemaker论坛.汇总于此. 看ATC2603 ...

  4. 扩展 GRTN:云原生趋势下的 RTC 架构演进

    在 2021 LiveVideoStackCon 音视频技术大会上海站,聚焦 "轻端重云和边缘架构新模式" 专场,阿里云视频云的 RTC 传输专家杨成立(忘篱)带来 "基 ...

  5. Linux-NTP-Server+Client

    GMT/UTC/CST;/etc/localtime,/usr/share/zoneinfo/*时区文件,/etc/profile加TZ变量;硬件时间RTC,系统时间;date,hwclock,tzs ...

  6. 上手 WebRTC DTLS 遇到很多 BUG?浅谈 DTLS Fragment

    上一篇<详解 WebRTC 传输安全机制:一文读懂 DTLS 协议>详细阐述了 DTLS.本文将结合 DTLS 开发中遇到的问题,详细解读 DTLS 的一些基础概念以及 Fragment ...

  7. ZEGO音视频服务的高可用架构设计与运营

    前言: ZEGO 即构科技作为一家实时音视频的提供商,系统稳定性直接影响用户的主观体验,如何保障服务高可用且用户体验最优是行业面临的挑战,本文结合实际业务场景进行思考,介绍 ZEGO 即构在高可用架构 ...

  8. 2012高校GIS论坛

    江苏省会议中心 南京·钟山宾馆(2012年4月21-22日) 以"突破与提升"为主题的"2012高校GIS论坛"将于4月在南京举行,由南京大学和工程中心共同承办 ...

  9. Linux系统时间与RTC时间【转】

    http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782 Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符 ...

随机推荐

  1. Unable to get setting value Parameter name: profileName

    Today when I am building my application, everything works well but when I try to run Azure Worker Ro ...

  2. IIS URL Rewrite redirect from one Domain to another

    IIS URL Rewrite enables Web administrators to create powerful rules to implement URLs that are easie ...

  3. SWING

    第一个图形界面应用程序.图形用户界面简称GUI(Graphical User Interface),通过GUI用户可以更好地与计算机进行交互.Swing简介Swing工具包提供了一系列丰富的GUI 组 ...

  4. 使用dbms_system追踪其它session

    dbms_system是内部包,建议在官方指导下使用该包. SQL> desc dbms_system PROCEDURE ADD_PARAMETER_VALUE Argument Name T ...

  5. Protostuff序列化

    前言: Java序列化是Java技术体系当中的一个重要议题,序列化的意义在于信息的交换和存储,通常会和io.持久化.rmi技术有关(eg:一些orm框架会要求持久化的对象类型实现Serializabl ...

  6. 使用Genymotion来运行Android Studio开发的程序

    访问网址:https://www.genymotion.com,注册账户,之后下载,下载时需要注意不要选择(withou virtualbox那个) 之后运行安装, 运行Genymotion运行启动G ...

  7. 2.Spring Web MVC的优势

    清晰的角色划分:前端控制器(DispatcherServlet).请求到处理器映射(HandlerMapping).处理器适配器(HandlerAdapter).视图解析器(ViewResolver) ...

  8. PostgreSQL Loadbalance Analysis CPU

    Before we can even begin to decide on a processor count, we need a baseline. With a working PostgreS ...

  9. 常用SQL操作(MySQL或PostgreSQL)与相关数据库概念

    本文对常用数据库操作及相关基本概念进行总结:MySQL和PostgreSQL对SQL的支持有所不同,大部分SQL操作还是一样的. 选择要用的数据库(MySQL):use database_name; ...

  10. Groupon面经:Find paths in a binary tree summing to a target value

    You are given a binary tree (not necessarily BST) in which each node contains a value. Design an alg ...