技术开发 频道

技术内幕:Android对Linux内核的增强

  【IT168技术】IT168移动频道在之前的技术文章中曾经介绍过Android对Linux内核的改动,本文将重点介绍Android对Linux内核的增强,主要包括Alarm(硬件时钟)、Ashmem(匿名内存共享)、Low Memory Killer(低内存管理)、Logger(日志设备),等等,让大家全方位了解为何Android能将Linux内核在移动领域运用的如此精湛,可以和苹果相抗衡。

   Alarm(硬件时钟)

  Alarm就是一个硬件时钟,前面我们已经知道它提供了一个定时器,用于把设备从睡眠状态唤醒,同时它也提供了一个在设备睡眠时仍然会运行的时钟基准。在应用层上,有关时间的应用都需要Alarm的支持,源代码位于“drivers/rtc/alarm.c”。

  Alarm的设备名为“/dev/alarm”。该设备的实现非常简单,我们首先打开源码,可以看到include ,其中定义了一些Alarm的相关信息。Alarm的类型枚举如下:

enum android_alarm_type {
    ANDROID_ALARM_RTC_WAKEUP,
    ANDROID_ALARM_RTC,
    ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
    ANDROID_ALARM_ELAPSED_REALTIME,
    ANDROID_ALARM_SYSTEMTIME,
    ANDROID_ALARM_TYPE_COUNT,
};

  主要包括了5种类型的Alarm,_WAKEUP类型表示在触发Alarm时需要唤醒设备,反之则不需要唤醒设备;ANDROID_ALARM_RTC类型表示在指定的某一时刻出发Alarm;ANDROID_ALARM_ELAPSED_REALTIME表示在设备启动后,流逝的时间达到总时间之后触发Alarm;ANDROID_ALARM_SYSTEMTIME类型则表示系统时间;ANDROID_ALARM_ TYPE_COUNT则是Alram类型的计数。

  注意 流逝的时间也包括设备睡眠的时间,流逝时间的计算点从它最后一次启动算起。

  Alarm返回标记的枚举类型如下:

enum android_alarm_return_flags {
    ANDROID_ALARM_RTC_WAKEUP_MASK
= 1U << ANDROID_ALARM_RTC_WAKEUP,
    ANDROID_ALARM_RTC_MASK
= 1U << ANDROID_ALARM_RTC,
    ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK
=
                1U
<< ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
    ANDROID_ALARM_ELAPSED_REALTIME_MASK
=
                1U
<< ANDROID_ALARM_ELAPSED_REALTIME,
    ANDROID_ALARM_SYSTEMTIME_MASK
= 1U << ANDROID_ALARM_SYSTEMTIME,
    ANDROID_ALARM_TIME_CHANGE_MASK
= 1U << 16
};

  Alarm返回标记会随着Alarm的类型而改变。最后还定义了一些宏,主要包括禁用Alarm、Alarm等待、设置Alarm等。下面我们来分析Alarm驱动的具体实现。

  首先,Alarm的初始化及退出由以下三个函数来完成:

  late_initcall(alarm_late_init);

  module_init(alarm_init);

  module_exit(alarm_exit);

  其中alarm_init函数对Alarm执行初始化操作,alarm_late_init需要在初始化完成之后进行调用,最后退出时需要调用alarm_exit来销毁和卸载Alarm接口及驱动。

  1.alarm_init

  在初始化过程中,首先需要初始化系统时间,通过platform_driver_register函数来注册Alarm驱动的相关参数,具体如下所示:

static struct platform_driver alarm_driver = {
    .suspend
= alarm_suspend,
    .resume
= alarm_resume,
    .driver
= {
        .name
= "alarm"
    }
};

  该参数主要指定了当系统挂起(suspend)和唤醒(Desume)所需要实现的分别为alarm_suspend和alarm_resume,同时将Alarm设备驱动的名称设置为了“alarm”。

  如果设置正确,那么继续通过如下代码来初始化SUSPEND lock,因为在使用它们之前必须执行初始化操作。

  wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm");

  wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc");

  紧接着通过class_interface_register函数来注册Alarm接口信息,主要包括设备的添加和移除操作,内容如下:

static struct class_interface rtc_alarm_interface = {
    .add_dev
= &rtc_alarm_add_device,
    .remove_dev
= &rtc_alarm_remove_device,
};

  如果在此过程中出现错误,那么需要销毁已经注册的SUSPEND lock,并且卸载Alarm驱动,代码如下:

  wake_lock_destroy(&alarm_rtc_wake_lock);

  wake_lock_destroy(&alarm_wake_lock);

  platform_driver_unregister(&alarm_driver);

  注意 wake lock是一种锁机制,只要有用户持有该锁,系统就无法进入休眠状态,该锁可以被用户态程序和内核获得。这个锁可以是超时的或者是没有超时的,超时的锁会在时间过期以后自动解锁。如果没有锁或者超时了,内核就会启动休眠机制进入休眠状态,后面在讲电源管理时还会进一步讲解该机制。

  2.alarm_late_init

  当Alarm启动之后,我们需要读取当前的RCT和系统时间,由于需要确保在这个操作过程中不被中断,或者在中断之后能告诉其他进程该过程没有读取完成,不能被请求,因此这里需要通过spin_lock_irqsave和spin_unlock_irqrestore来对其执行锁定和解锁操作。实现代码如下:

static int __init alarm_late_init(void)
{
    unsigned
long   flags;
    struct timespec system_time;

    spin_lock_irqsave(
&alarm_slock, flags);

    getnstimeofday(
&elapsed_rtc_delta);
    ktime_get_ts(
&system_time);
    elapsed_rtc_delta
= timespec_sub(elapsed_rtc_delta, system_time);

    spin_unlock_irqrestore(
&alarm_slock, flags);

    ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO,
        
"alarm_late_init: rtc to elapsed realtime delta %ld.%09ld\n",
        elapsed_rtc_delta.tv_sec, elapsed_rtc_delta.tv_nsec);
    return
0;
}

  3.alarm_exit

  当Alarm退出时,就需要通过class_interface_unregister函数来卸载在初始化时注册的Alarm接口,通过wake_lock_destroy函数来销毁SUSPEND lock,以及通过platform_driver_unregister函数来卸载Alarm驱动。实现代码如下:

static void  __exit alarm_exit(void)
{
    class_interface_unregister(
&rtc_alarm_interface);
    wake_lock_destroy(
&alarm_rtc_wake_lock);
    wake_lock_destroy(
&alarm_wake_lock);
    platform_driver_unregister(
&alarm_driver);
}

  4.添加和移除设备

  接下来是rtc_alarm_add_device和rtc_alarm_remove_device函数的实现。添加设备时,首先将设备转换成rtc_device类型,然后,通过misc_register函数将自己注册成为一个Misc设备。其包括的主要特性如下面的代码所示:

static struct file_operations alarm_fops = {
    .owner
= THIS_MODULE,
    .unlocked_ioctl
= alarm_ioctl,
    .open
= alarm_open,
    .release
= alarm_release,
};

static struct miscdevice alarm_device
= {
    .minor
= MISC_DYNAMIC_MINOR,
    .name
= "alarm",
    .fops
= &alarm_fops,
};

  其中alarm_device中的“.name”表示设备文件名称,而alarm_fops则定义了Alarm的常用操作,包括打开、释放和I/O控制。这里还需要通过rtc_irq_register函数注册一个rtc_task,用来处理Alarm触发的方法,其定义如下:

static struct rtc_task alarm_rtc_task = {
    .func
= alarm_triggered_func
};

  其中“alarm_triggered_func”则是Alarm需要触发的方法。

  注意 如果在添加设备的过程中出现错误,我们需要对已经执行的操作进行释放、销毁和卸载。但是,移除一个设备时同样需要判断设备是否是Alarm设备,然后再执行卸载等操作。另外,在处理挂起操作时,我们首先就需要对设备进行锁定,然后根据Alarm的类型执行不同的操作,同时要保存时间。

  alarm_open和alarm_release的实现很简单。最后需要说明的是,对于I/O操作而言,主要需要实现:设置时间、设置RTC、获取时间、设置Alarm等待等。

  本小节主要对Android中最简单的设备驱动——Alarm的实现流程进行了分析,大家应该可以自己绘制出一个流程图来了吧。对于Alarm的具体实现,大家可以参考源代码“drivers/rtc/alarm.c”中的实现方式。

0
相关文章