2010-11-10 14:58:00 evanwu_85 阅读数 6096

将从google 官网(http://code.google.com/p/android/downloads/list )下载的AndroidLinux Kernel 和标准的Linux 2.6.25 版本的Kernel 比较,Android 系统的Kernel 主要增加如下内容“

1. 基于 ARM 架构增加 Gold-Fish 平台 , 相应增加的目录如下 :
kernel/arch/arm/mach-goldfish
kernel/include/asm-arm/arch-goldfish

Gold-Fish 平台采用的是 ARM926T CPU 作为 BaseBand 处理器 , CPU 主频至少为 200MHZ. 而要流畅运行Android 系统的CPU 主频至少要在400MHZ 以上,也就是说如果你要上Android 系统,最好是选用基于ARM11 及以上平台进行开发。

2. 增加了 yaffs2 FLASH 文件系统 , 增加的目录为 :kernel/fs/yaffs2
实际上, Android 包经过编译后生成的 system.img ramdisk.img 文件就 yaffs2 格式的包 .

3. 增加了 Android 的相关 Driver, 相应目录为 :
kernel/drivers/android

主要分为 :
Android IPC 系统 : Binder
Android 日志系统 : Logger
Android 电源管理 : Power
Android 闹钟管理 : Alarm
Android 内存控制台 : Ram_console
Android 时钟控制的 gpio: Timed_gpio

4. 增加了 switch 处理 , 相应的目录为 :kernel/drivers/switch/

5. 增加了一种新的共享内存处理方式 , 相应增加的文件为 :kernel/mm/ashmem.c

6. 其他为 Linux-2.6.25 内核所做的补丁等等,例如 BlueTooth,

另外 GoldFish 平台相关的驱动文件如下 :

1. 字符输出设备 :
kernel/drivers/char/goldfish_tty.c

2. 图象显示设备 : (Frame Buffer)
kernel/drivers/video/goldfishfb.c

3. 键盘输入设备 :
kernel/drivers/input/keyboard/goldfish_events.c

4. RTC 设备 : (Real Time Clock)
kernel/drivers/rtc/rtc-goldfish.c

5. USB Device 设备 :
kernel/drivers/usb/gadget/android_adb.c

6. SD 卡设备 :
kernel/drivers/mmc/host/goldfish.c

7. FLASH 设备 :
kernel/drivers/mtd/devices/goldfish_nand.c
kernel/drivers/mtd/devices/goldfish_nand_reg.h

8. LED 设备 :
kernel/drivers/leds/ledtrig-sleep.c

9. 电源设备 :
kernel/drivers/power/goldfish_battery.c

10. 音频设备 :
kernel/arch/arm/mach-goldfish/audio.c

11. 电源管理 :
kernel/arch/arm/mach-goldfish/pm.c

12. 时钟管理 :
kernel/arch/arm/mach-goldfish/timer.c

下面一段摘自http://www.kandroid.org/android_pdk/intro_source_code.html

Linux Kernel

The Android Linux kernel includes enhancements to the Linux 2.6 kernel that provide additional drivers to support the Android platform, including:

  • Binder: an OpenBinder-based driver to facilitate inter-process communication (IPC) in the Android platform.
  • Android Power Management: a light weight power management driver built on top of standard Linux power management but optimized for embedded systems.
  • Low Memory Killer: Based on hints from the userspace, the low memory killer can kill off processes to free up memory as necessary. It is designed to provide more flexibility than the Out Of Memory (OOM) killer in the standard kernel.
  • Logger: A light weight logging device used to capture system, radio, logdata, etc.
  • USB Gadget: Uses the USB function framework.
  • Android/PMEM: The PMEM (physical memory) driver is used to provide contiguous physical memory regions to userspace libraries that interact with the digital signal processor (DSP) and other hardware that cannot cope with scatter-gather.
  • Android Alarm: A driver which provides timers that can wake the device up from sleep and a monotonic timebase that runs while the device is asleep.

Look for Android-specific enhancements in the following directories:

  • /drivers/android
  • /drivers/misc
  • /include/linux

 

2014-03-12 17:30:28 cnclenovo 阅读数 2598

Android Kernel移植流程

参考文档
Documentation/android.txt
修改kernel config

Android Driver

  • Android Binder:基于openBinder框架的驱动,进程间通信底层主要驱动,没有可它,android无法工作
  • Android Logger:是一个轻量级的日志系统,在内核里是misc(杂项)设备驱动,它与logcat配合实现调试。
    代码位置:driver/staging/android/logger.c
  • Android Low memory killer:低内存管理器,是linux标准OOM(Out Of Memory)改进版,当系统内存不足时,会杀死一些不重要的进程,释放空间
    代码位置:driver/staging/android/lowmemorykiller.c
  • Android Ram Console:辅助调试的内核机制,它用一段物理内存虚拟一个console设备,这样printk的时候会把调试信息写一份到这块ram,最后通过/proc文件系统输出
  • Android Timed Device timed_gpio:基于platform driver实现的一个增强的gpio驱动,它与内核定时器绑定在了一起,实现了一种时钟控制的gpio
  • Android Switch:android新引进的驱动,用于检查一些开关量,如耳机插入,USB设备插入,通过sysfs实现
  • Android Power Mananger:基于标准linux电源管理系统的轻量级电源管理驱动
  • Android Ashmem:匿名共享内存,为进程间通信提供大块共享内存,同时为内核提供回收和管理这些内存的机制
  • Android PMEM:用于向用户空间提供连续的物理内存,DSP和其他需要连续物理内存的设备需要它的提供服务
    代码位置:driver/misc/pmem.c
  • Android alarm:提供一个定时器用于把设备从睡眠状态唤醒,同时它也提供了一个即使在设备睡眠时也会运行的时钟基准。
    代码位置:driver/rtc/alarm.c
  • USB Gadget:一个基于标准Linux USB gadget驱动框架的设备驱动,Android的USB驱动是基于gadget框架的
    代码位置:driver/usb/gadget

移植Drivers

Connectivity
     Bluetooth
     GPS
     Wi-Fi
Display Drivers
Input Devices
     Keymaps and Keyboard
Lights
Multimedia
     Audio
     Camera/Video
Power Management
Sensors
Telephony
     Radio Interface Layer
     SIM Toolkit Application(STK)

Android Linux Kernel 特性

从Linux 2.6.33开始,Google智能手机操作系统Android核心代码被删除。
Android 2.2 froyo使用Linux Kernel版本为2.6.32
Linux 2.6.32版本上Android添加了如下几个模块

几个重要的makefile

  • Android.mk     编译源码mk文件,每个module和package目录下有这么一个文件
  • main.mk      定义编译全部代码的依赖关系
  • config.mk     用于配制编译系统,决定如何编译
  • envsetup.mk    定义了编译环境配置
  • product_config.mk   读取AndroidProducts.mk生成TARGET_DEVICE变量
  • AndroidProducts.mk  定义了某厂商所有产品文件列表
  • BoardConfig.mk  定义开发板软件相关配置项,将来影响系统条件编译。

编译Android系统

  1. source build/envsetup.sh
    加载编译用到的环境变量
    定义了一些命令参数,这些命令函数方便用户选择Android产品编译选项,如help,lunch等
    加载vendor目录下自定义厂商产品编译项文件vendorsetup.sh,以此让编译系统编译不同产品的定制系统。
  2. lunch [build-option]
    用于选定编译平台
    不加build-option打印所有的系统编译项,设置主要的环境变量
    TARGET_RPODUCT,TARGET_BUILD_VARIANT、TARGET_BUILD_TYPE
  3. make
    执行编译
    make 依据当前目录下Makefile编译目标文件
    -j4 表示4线程同时编译(多核心)
    make snod make //sdk no depend 只编译SDK,生成system.img
    make ramdisk
    make framework
    make sdk
    m 编译全部
    mm 只编译当前目录
    mmm 编译指定目录

编译结果

保存目录:out/target/product/<products>/
ramdisk.img 虚拟内存盘,用内存来模拟磁盘,它在Linux内核启动后被挂载,该镜像里保存有Android最基本的文件系统以及一些命令
system.img Android系统主要的文件系统镜像,里面包含有Android系统运行必须的库,程序和配置文件
userdata.img 用户数据镜像,它是用户应用程序,用户信息保存目录
recovery.img 系统恢复镜像,当系统进入恢复模式时的启动镜像

Android系统磁盘分区(一般情况)

boot分区 存储boot.img镜像
system分区 存放system.img镜像
userdata分区 存放userdata.img镜像
recovery分区 存放recovery.img镜像
cache分区 应用程序缓存分区,加快程序启动
misc分区 系统设置厂商硬件设置信息分区


Android 系统移植流程

Android.mk模板

2017-04-25 13:41:56 linus_linux 阅读数 442

Linux kernel 帧的接收

本文以e1000驱动为例,基于3.10.0-514.10.2版本内核。


驱动注册 - e1000_init_module

[root@10-254-0-111 ~]# modprobe e1000  #插入e1000模块
    或者
[root@10-254-0-111 ~]# insmod /path/to/e1000.ko   #insmod后面指定e1000.ko文件路径

上面的操作对应的实现代码如下:

/*** drivers/net/ethernet/intel/e1000/e1000_main.c ***/

 //该e1000驱动对应的pci驱动实例
 static struct pci_driver e1000_driver = {
        .name     = e1000_driver_name, //e1000驱动程序名称,默认e1000
        .id_table = e1000_pci_tbl,     //该驱动程序所支持的网卡设备列表
        .probe    = e1000_probe,       //设备初始化函数,当PCI子系统检测到该驱动所支持的设备被插入到总线上时,调用该函数对设备进行初始化操作。
        .remove   = e1000_remove,      //移除设备时(热插拔或驱动程序被移除时)调用该函数
#ifdef CONFIG_PM //电源管理
        /* Power Management Hooks */
        .suspend  = e1000_suspend,     //系统休眠时调用
        .resume   = e1000_resume,      //系统被唤醒时调用
#endif                  
        .shutdown = e1000_shutdown,    //系统关闭时调用
        .err_handler = &e1000_err_handler //错误处理器
};

/**
 * e1000_init_module - 驱动注册函数
 *
 * e1000_init_module is the first routine called when the driver is
 * loaded. All it does is register with the PCI subsystem.
 **/
static int __init e1000_init_module(void)
{
    int ret;
    pr_info("%s - version %s\n", e1000_driver_string, e1000_driver_version);

    pr_info("%s\n", e1000_copyright);

    /* 注册pci驱动 - 把e1000驱动程序以pci_driver形式注册到pci子系统中 */
    ret = pci_register_driver(&e1000_driver);
    if (copybreak != COPYBREAK_DEFAULT) {
        if (copybreak == 0)
            pr_info("copybreak disabled\n");
        else
            pr_info("copybreak enabled for "
                   "packets <= %u bytes\n", copybreak);
    }
    return ret;
}

module_init(e1000_init_module);

pci_register_driver() 注册的是驱动程序,是把驱动程序安装到内核,准确的说是安装到内核的PCI子系统中。此时还没有设备出现,但是内核已经具备管理e1000设备的能力。

设备发现和初始化 - e1000_probe

刚刚我们通过e1000_init_module()把e1000驱动程序注册到PCI子系统,这样当有e1000设备插入到PCI总线的时候,PCI子系统就可以发现该设备,并调用之前注册的函数e1000_probe()对设备进行初始化

/*** drivers/net/ethernet/intel/e1000/e1000_main.c ***/

/* 设备的操作函数 */
static const struct net_device_ops e1000_netdev_ops = {
        .ndo_open               = e1000_open,   //打开设备
        .ndo_stop               = e1000_close,   //关闭设备
        .ndo_start_xmit         = e1000_xmit_frame,
        .ndo_get_stats          = e1000_get_stats,
        .ndo_set_rx_mode        = e1000_set_rx_mode,
        .ndo_set_mac_address    = e1000_set_mac,
        .ndo_tx_timeout         = e1000_tx_timeout,
        .ndo_change_mtu         = e1000_change_mtu,
        .ndo_do_ioctl           = e1000_ioctl,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_vlan_rx_add_vid    = e1000_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = e1000_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = e1000_netpoll,
#endif
        .ndo_fix_features       = e1000_fix_features,
        .ndo_set_features       = e1000_set_features,
};

/**
 * e1000_probe - 设备初始化函数
 * @pdev: PCI device information struct
 * @ent: entry in e1000_pci_tbl
 *
 * Returns 0 on success, negative on failure
 *
 * e1000_probe initializes an adapter identified by a pci_dev structure.
 * The OS initialization, configuring of the adapter private structure,
 * and a hardware reset occur.
 **/
static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
    struct net_device *netdev;
    struct e1000_adapter *adapter;   //设备私有数据
    struct e1000_hw *hw;

    ...

    err = -ENOMEM;
    /* 为设备分配net_device结构体 */
    netdev = alloc_etherdev(sizeof(struct e1000_adapter));
    if (!netdev)
        goto err_alloc_etherdev;

    ...

    /* 设置设备的私有数据 */
    adapter = netdev_priv(netdev);
    adapter->netdev = netdev;
    adapter->pdev = pdev;
    adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
    adapter->bars = bars;
    adapter->need_ioport = need_ioport;

    ...

    /* 设置设备的操作函数 */
    netdev->netdev_ops = &e1000_netdev_ops;
    e1000_set_ethtool_ops(netdev);

    /**
     * 初始化该设备的napi,用于下半部接收数据;
     * 该函数初始化napi字段,并把napi注册到全局napi_hash表中。
     **/
    netif_napi_add(netdev, &adapter->napi, e1000_clean, 64);

    /* 设置设备名称 */
    strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);

    ...

    /* 初始化延时任务,如看门狗任务 */
    INIT_DELAYED_WORK(&adapter->watchdog_task, e1000_watchdog);
    INIT_DELAYED_WORK(&adapter->fifo_stall_task,
              e1000_82547_tx_fifo_stall_task);
    INIT_DELAYED_WORK(&adapter->phy_info_task, e1000_update_phy_info_task);
    INIT_WORK(&adapter->reset_task, e1000_reset_task);

    ...

    /* reset the hardware with the new settings */
    e1000_reset(adapter);

    strcpy(netdev->name, "eth%d");

    /* 注册网络设备net_device */
    err = register_netdev(netdev);
    if (err)
        goto err_register;

    ...
}

开启设备 - e1000_open

当使用用户空间工具(如ifconfig、iproute)开启网络设备时,PCI子系统就会调用函数e1000_open()

/*** drivers/net/ethernet/intel/e1000/e1000_main.c ***/

/**
 * e1000_open - Called when a network interface is made active
 * @netdev: network interface device structure
 *
 * Returns 0 on success, negative value on failure
 *
 * The open entry point is called when a network interface is made
 * active by the system (IFF_UP).  At this point all resources needed
 * for transmit and receive operations are allocated, the interrupt
 * handler is registered with the OS, the watchdog task is started,
 * and the stack is notified that the interface is ready.
 **/
static int e1000_open(struct net_device *netdev)
{
    struct e1000_adapter *adapter = netdev_priv(netdev);
    struct e1000_hw *hw = &adapter->hw;
    int err;

    /* disallow open during test */
    if (test_bit(__E1000_TESTING, &adapter->flags))
        return -EBUSY;

    netif_carrier_off(netdev);

    /* allocate transmit descriptors */
    err = e1000_setup_all_tx_resources(adapter);
    if (err)
        goto err_setup_tx;

    /* allocate receive descriptors */
    err = e1000_setup_all_rx_resources(adapter);
    if (err)
        goto err_setup_rx;

    e1000_power_up_phy(adapter);

    adapter->mng_vlan_id = E1000_MNG_VLAN_NONE;
    if ((hw->mng_cookie.status &
              E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) {
        e1000_update_mng_vlan(adapter);
    }

    /* before we allocate an interrupt, we must be ready to handle it.
     * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
     * as soon as we call pci_request_irq, so we have to setup our
     * clean_rx handler before we do so.
     */
    e1000_configure(adapter);

    //注册中断请求,中断处理函数为e1000_intr
    err = e1000_request_irq(adapter);
    if (err)
        goto err_req_irq;

    /* From here on the code is the same as e1000_up() */
    clear_bit(__E1000_DOWN, &adapter->flags);

    napi_enable(&adapter->napi);

    e1000_irq_enable(adapter);

    netif_start_queue(netdev);

    /**
     * fire a link status change interrupt to start the watchdog,
     * 触发链路状态变更中断,启动看门狗
    **/
    ew32(ICS, E1000_ICS_LSC);

    return E1000_SUCCESS;

err_req_irq:
    e1000_power_down_phy(adapter);
    e1000_free_all_rx_resources(adapter);
err_setup_rx:
    e1000_free_all_tx_resources(adapter);
err_setup_tx:
    e1000_reset(adapter);

    return err;
}

设备中断 - e1000_intr

当网卡接收到数据就会发出中断请求(IRQ),对应的中断处理函数就是e1000_intr(),该函数运行在中断上下文中,不可休眠。

/*** drivers/net/ethernet/intel/e1000/e1000_main.c ***/

/**
 * e1000_intr - Interrupt Handler
 * @irq: interrupt number
 * @data: pointer to a network interface device structure
 **/
static irqreturn_t e1000_intr(int irq, void *data)
{
    struct net_device *netdev = data;
    struct e1000_adapter *adapter = netdev_priv(netdev);
    struct e1000_hw *hw = &adapter->hw;
    u32 icr = er32(ICR);

    if (unlikely((!icr)))
        return IRQ_NONE;  /* Not our interrupt */

    /* we might have caused the interrupt, but the above
     * read cleared it, and just in case the driver is
     * down there is nothing to do so return handled
     */
    if (unlikely(test_bit(__E1000_DOWN, &adapter->flags)))
        return IRQ_HANDLED;

    if (unlikely(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC))) {
        hw->get_link_status = 1;
        /* guard against interrupt when we're going down */
        if (!test_bit(__E1000_DOWN, &adapter->flags)){
            //启动看门狗任务
            schedule_delayed_work(&adapter->watchdog_task, 1);
        }
    }

    /* disable interrupts, without the synchronize_irq bit */
    ew32(IMC, ~0);
    E1000_WRITE_FLUSH();

    if (likely(napi_schedule_prep(&adapter->napi))) {
        adapter->total_tx_bytes = 0;
        adapter->total_tx_packets = 0;
        adapter->total_rx_bytes = 0;
        adapter->total_rx_packets = 0;

        /**
         * 调度接收
         * 1、把napi加入到cpu的softnet_data.poll_list
         * 2、触发软中断(softirq)NET_RX_SOFTIRQ 准备接收数据
        **/
        __napi_schedule(&adapter->napi);
    } else {
        /* this really should not happen! if it does it is basically a
         * bug, but not a hard error, so enable ints and continue
         */
        if (!test_bit(__E1000_DOWN, &adapter->flags))
            e1000_irq_enable(adapter);
    }

    return IRQ_HANDLED;
}

下半部处理 - 软中断

设备初始化完毕并开启后,就进入就绪状态,当有数据到达网卡就触发中断(硬件中断),内核执行对应的中断处理程序(中断处理程序是上半部,要简短、迅速),当执行完中断处理程序后内核触发NET_RX_SOFTIRQ软中断,进行下半部处理,NET_RX_SOFTIRQ对应的软中断处理函数是net_rx_action(),注册的地方是在系统初始化时:

/*** drivers/net/ethernet/intel/e1000/e1000_main.c ***/

static int __init net_dev_init(void)
{
        int i, rc = -ENOMEM;

        BUG_ON(!dev_boot_phase);

        ...

        open_softirq(NET_TX_SOFTIRQ, net_tx_action);
        open_softirq(NET_RX_SOFTIRQ, net_rx_action); //注册网络收包的软中断处理函数

        hotcpu_notifier(dev_cpu_callback, 0);
        dst_subsys_init();
        rc = 0;
        ...
}

net_rx_action()函数

/*** net/core/dev.c ***/

static void net_rx_action(struct softirq_action *h)
{
        //获取当前CPU的softnet_data数据
        struct softnet_data *sd = this_cpu_ptr(&softnet_data);

        //设置处理时限
        unsigned long time_limit = jiffies + 2;
        //设置此次最大处理包数
        int budget = netdev_budget;

        LIST_HEAD(list);
        LIST_HEAD(repoll);

        local_irq_disable();

        //把poll_list连接到list上,然后把poll_list清空(把poll_list中的napi缓存到list中)
        list_splice_init(&sd->poll_list, &list);
        local_irq_enable();

        //对list中的napi循环处理
        for (;;) {
                struct napi_struct *n;

                if (list_empty(&list)) {
                        if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
                                return;
                        break;
                }

                n = list_first_entry(&list, struct napi_struct, poll_list);
                /**
                 * 调用napi->poll函数从设备接收数据,poll函数是由设备驱动程序提供,并在
                 * 设备初始化时通过netif_napi_add()函数放到设备的private data中。
                 * poll函数从硬件中读取数据然后通过napi_gro_receive()函数把skb发送到
                 * 网络协议栈做进一步处理。
                **/
                budget -= napi_poll(n, &repoll);

                /* If softirq window is exhausted then punt.
                 * Allow this to run for 2 jiffies since which will allow
                 * an average latency of 1.5/HZ.
                 */
                if (unlikely(budget <= 0 ||
                             time_after_eq(jiffies, time_limit))) {
                        sd->time_squeeze++;
                        break;
                }
        }

        __kfree_skb_flush();
        local_irq_disable();

        //把未完成的napi加入到CPU全局poll_list中,待下次处理软中断时再次处理
        list_splice_tail_init(&sd->poll_list, &list);
        list_splice_tail(&repoll, &list);
        list_splice(&list, &sd->poll_list);

        //poll_list非空,此次处理超过时限或超过最大处理包数,重新出发软中断进行处理。
        if (!list_empty(&sd->poll_list))
                __raise_softirq_irqoff(NET_RX_SOFTIRQ);

        net_rps_action_and_irq_enable(sd);
}

看门狗

看门狗是用来检测设备状态的,在e1000_open()中启动。

/**
 * e1000_watchdog - work function
 * @work: work struct contained inside adapter struct
 **/
static void e1000_watchdog(struct work_struct *work)
{
    struct e1000_adapter *adapter = container_of(work,
                             struct e1000_adapter,
                             watchdog_task.work);
    struct e1000_hw *hw = &adapter->hw;
    struct net_device *netdev = adapter->netdev;
    struct e1000_tx_ring *txdr = adapter->tx_ring;
    u32 link, tctl;

    link = e1000_has_link(adapter);
    //链路已激活并有载波->链路状态正常,去更新统计数据
    if ((netif_carrier_ok(netdev)) && link)
        goto link_up;

    if (link) {
        if (!netif_carrier_ok(netdev)) {
            //链路已激活但是没有载波,检测到设备启动
            u32 ctrl;
            bool txb2b = true;
            /* update snapshot of PHY registers on LSC */
            e1000_get_speed_and_duplex(hw,
                           &adapter->link_speed,
                           &adapter->link_duplex);

            ctrl = er32(CTRL);
            pr_info("%s NIC Link is Up %d Mbps %s, "
                "Flow Control: %s\n",
                netdev->name,
                adapter->link_speed,
                adapter->link_duplex == FULL_DUPLEX ?
                "Full Duplex" : "Half Duplex",
                ((ctrl & E1000_CTRL_TFCE) && (ctrl &
                E1000_CTRL_RFCE)) ? "RX/TX" : ((ctrl &
                E1000_CTRL_RFCE) ? "RX" : ((ctrl &
                E1000_CTRL_TFCE) ? "TX" : "None")));

            /* adjust timeout factor according to speed/duplex */
            adapter->tx_timeout_factor = 1;
            switch (adapter->link_speed) {
            case SPEED_10:
                txb2b = false;
                adapter->tx_timeout_factor = 16;
                break;
            case SPEED_100:
                txb2b = false;
                /* maybe add some timeout factor ? */
                break;
            }

            /* enable transmits in the hardware */
            tctl = er32(TCTL);
            tctl |= E1000_TCTL_EN;
            ew32(TCTL, tctl);

            netif_carrier_on(netdev);
            if (!test_bit(__E1000_DOWN, &adapter->flags))
                schedule_delayed_work(&adapter->phy_info_task,
                              2 * HZ);
            adapter->smartspeed = 0;
        }
    } else {
        if (netif_carrier_ok(netdev)) {
            //链路未激活但有载波,检测到设备关闭
            adapter->link_speed = 0;
            adapter->link_duplex = 0;
            pr_info("%s NIC Link is Down\n",
                netdev->name);
            netif_carrier_off(netdev);

            if (!test_bit(__E1000_DOWN, &adapter->flags))
                schedule_delayed_work(&adapter->phy_info_task,
                              2 * HZ);
        }

        e1000_smartspeed(adapter);
    }

link_up:
    e1000_update_stats(adapter);

    hw->tx_packet_delta = adapter->stats.tpt - adapter->tpt_old;
    adapter->tpt_old = adapter->stats.tpt;
    hw->collision_delta = adapter->stats.colc - adapter->colc_old;
    adapter->colc_old = adapter->stats.colc;

    adapter->gorcl = adapter->stats.gorcl - adapter->gorcl_old;
    adapter->gorcl_old = adapter->stats.gorcl;
    adapter->gotcl = adapter->stats.gotcl - adapter->gotcl_old;
    adapter->gotcl_old = adapter->stats.gotcl;

    e1000_update_adaptive(hw);

    if (!netif_carrier_ok(netdev)) {
        if (E1000_DESC_UNUSED(txdr) + 1 < txdr->count) {
            /* We've lost link, so the controller stops DMA,
             * but we've got queued Tx work that's never going
             * to get done, so reset controller to flush Tx.
             * (Do the reset outside of interrupt context).
             */
            adapter->tx_timeout_count++;
            schedule_work(&adapter->reset_task);
            /* exit immediately since reset is imminent */
            return;
        }
    }

    /* Simple mode for Interrupt Throttle Rate (ITR) */
    if (hw->mac_type >= e1000_82540 && adapter->itr_setting == 4) {
        /* Symmetric Tx/Rx gets a reduced ITR=2000;
         * Total asymmetrical Tx or Rx gets ITR=8000;
         * everyone else is between 2000-8000.
         */
        u32 goc = (adapter->gotcl + adapter->gorcl) / 10000;
        u32 dif = (adapter->gotcl > adapter->gorcl ?
                adapter->gotcl - adapter->gorcl :
                adapter->gorcl - adapter->gotcl) / 10000;
        u32 itr = goc > 0 ? (dif * 6000 / goc + 2000) : 8000;

        ew32(ITR, 1000000000 / (itr * 256));
    }

    /* Cause software interrupt to ensure rx ring is cleaned */
    ew32(ICS, E1000_ICS_RXDMT0);

    /* Force detection of hung controller every watchdog period */
    adapter->detect_tx_hung = true;

    /* Reschedule the task 2HZ后再次调用看门狗检测设备状态*/
    if (!test_bit(__E1000_DOWN, &adapter->flags)){
        schedule_delayed_work(&adapter->watchdog_task, 2 * HZ);
    }
}

欢迎交流学习!

2013-10-27 18:12:23 wangpeihuixyz 阅读数 1837

来源:百度质量部

前言

Linux基金会在4月3日公布了Linux开发年报,向我们展示了linux kernel作为世上最大开源合作项目之一的魅力。自2005年以来,共有800家公司7800名开发人员参与Linux kernel开发,最近一年也有200家公司共1000名开发人员参与。目前在Linus Torvalds的监督之下,Linux核心约2到3个月发布一个新的稳定版本,每次更新大约包含8000到12000项修改,平均每小时有提交6.88次修改。内核所有文件代码已超过1500万行,大约以每年10%的代码量在增长。

规模组织如此巨大,开发又活跃,且对质量有非常高要求的项目,不禁好奇它是如何做好质量保证的,于是查阅了网上较多资料,探索Linux kernel的质量保证之道。

名词解释

Linux Kernel和GNU/Linux。Linux kernel作为系统的最底层,是负责管理硬件、执行任务调度、维护整体安全和完整性的系统软件。一个内核不是一套完整的操作系统,人们将 Linux 内核和 GNU 系统组成完整的自由系统:基于 Linux 的 GNU 系统(或简称为 GNU/Linux 系统)。咱们平时所说的Linux系统多是指GNU/Linux。

Patch。升级内核时,不必要每次都下载或提交整个的源码文件,而是使用patch,或者叫做补丁或升级包。很多不同版本的内核源代码是以一组补丁(补丁集)甚至是若干独立补丁的形式存在的。这些补丁应该应用在特定版本的内核树上。

内核树。树的意思是“包含内核源代码的目录的内容”。另外一层意思是“内核源码的开发分支”。不同的分支命名也不一样,比较流行的分支(树)有-next树(前身是Andrew Morton维护的-mm树),-rt 树(实时树,包含把linux转换为实时系统的补丁)。

-next 树,由Stephen Rothwell维护,是一些其他树以及各种独立的实验性补丁,它集合了主线和多种内核子系统(子系统树)维护人所使用的树,子系统树中含有被认为是实验性的和还没准备好合并到主线中的各种补丁。换句话说,-next树的每个版本都是带有额外修改和一些bug的主线的未来快照。Bug尽可能在这些补丁合并到主线之前得到修正。

开发流程

要了解测试,先得看看整个项目的开发过程。

Linux kernel开发流程是基于一个松散的、定时发布的滚动模型,每2-3个月发布一个新稳定版本。2-3个月被认为是一个合适的时间周期,能加快开发速度的同时,也减少发行商需要处理的外部修改,更短则导致测试时间太少,新内核测试不充分;更长则导致发布版本之间会堆积太多工作。此模型从2005年正式确认,尽早将新功能融入内核主线,将发布给用户的延期降至最低。

在准备提交Linux kernel代码前,开发者将升级拆分为一个个小的互相独立的单元,叫做补丁(patch)。每个补丁通常只做一件事情,并且应当保证系统在打上此补丁之后是依然能正常编译和工作的。这导致每个补丁都可以做代码质量和正确性评审,同时也导致每个版本提交的补丁数较大。

提交的补丁并不是直接进入到内核主线,参考图一的代码流动过程,只有一个人能将补丁放入到内核主线:Linus Torvalds。但是随着越来越多的补丁加入到内核,大约只有2%的补丁是Linux直接选入的。内核代码从逻辑上是拆分成一系列的子系统集合。每个子系统负责内核的一部分(比如网络、SCSI驱动、特定架构支持、内存管理、视频设备等),并且拥有指定的维护者,维护者对该子系统的代码总体负责。当前有过超过100个子系统。补丁先是进入到一个子系统树,子系统维护者接受修改时,补丁就会附加“由谁签收通过” 的一行签字。该信息表明此补丁可以合入到内核。


图一 代码流动过程

Linux-next树本意是在Linus合并代码前将各子系统的代码整合到一起,并进行测试,挑选出可能存在的冲突和跨子系统问题。

实际例子

一个新版本2.6.20的发布过程如下图二。在每一个版本开始时,Linus会打开合并窗口(merge window),从那时起,被认为是可靠稳定的代码会被合并到内核主线,新的功能或驱动等得以加入进来,合并窗口会开启两周,然后Linus会宣布窗口关闭,并发布第一个rc版本,如下图2.6.20-rc1。此-rc1版本的发布意味着新功能的加入已经完成,以后再加入的补丁只能是修复问题,以确保此版本的稳定,但也有即少数包含新功能的补丁会被认为是不友好的。问题修复的代码不断进入主线,Linus接下来大约每周发布一个新的rc版本,通常会在6-9个rc版本后认为此版本已经稳定并且最终发布2.6.20版本,再进入下一个三位版本的迭代。发布之后,此2.6.x版本就移交给稳定组(stable team),稳定组时而会再发布2.6.x.y版本,每个四位版本的发布会有两个条件:(1)修复重大的bug;(2)该补丁也已被融入到下个三位版本开发的主线中了。稳定组通常维护一个稳定版本6个月左右,之后就由使用该版内核的发行版发行商负责。


图二 一个版本的发布过程

测试

对Linux kernel测试面临的挑战巨大:如前面的数据所显示,(1)其快速活跃的开发;(2)经常性的版本发布,每次发布都包含大量修改,以及内核本身支持配置的灵活性、可扩展性,待测点太多(3)需要覆盖各种平台,Linux已支持二十多种平台体系,是支持平台最广的系统;(4)过时的case需要维护以及大量遗留代码。

与松散的开发流程相对应,没有看到正式的测试流程,开源软件很大一部分的测试执行是依靠用户实际运行使用。如下图三所示。

图三 开源测试的大体流程

测试项目简介

Linux kernel与测试相关较出名的开源项目有:

LTP:Linux Test Project http://ltp.sourceforge.net/。

Autotest:http://autotest.github.com/

CrackerJack Project:http://ossipedia.ipa.go.jp/crackerjack/index.html。

LTP是一个联合项目主要验证Linux系统的可靠性、健壮性和稳定性,最先由SGI™启动,并由IBM®负责维护。2012年4月发布的最新稳定版本已拥有3000+的case(case增长并不多,06年时就有2900+的case),用于测试Linux kernel以及相关功能。使用的编程语言主要是ANSI-C(占94%),以及Bash脚本(占5%),还有Perl(占0.62%)。它也有一套使用ANSI-C和Bash写case的模板。

Autotest是后启之秀,最早被用于Linux kernel自动化测试的框架,使用python语言,现在也被多个其它项目所应用。新case能较容易添加,ANSI-C或者Bash写的case也能较方便地融入此框架。针对Linux kernel的测试项目地址是http://test.kernel.org,用于交流、共享和分析测试数据。Autotest框架分服务端和客户端,在监听到有新版本发布(三位版本、四位版本、-rc版本、-git版本)时,会自动触发执行自动化,执行过程见下图四。


图四 Autotest自动化执行流程

CrackerJack 是一个主要负责linux kernel兼容性测试的项目,找出不同内核版本系统调用的diff情况,已支持317个系统调用。它也是使用Autotest框架执行,diff结果是个矩阵可见下图五或者网址http://ossipedia.ipa.go.jp/crackerjack/compare_results.html。Diff比较是智能的,并非单独地比较输出结果,比如time时间调用每次系统返回都是不一样的,比较时就是检查两次调用之间的时间差。

图五 CrackerJack diff结果

测试方式

测试手段是多样的:单测、集成测试、功能测试、性能测试、压力测试、回归测试等,但没有一种手段在任意时候都合适。质量保证是多方面的,除了要求开发者写出高质量代码外,静态代码检查、还要有频繁且严格的code review,下面列举一些针对Linux kernel特点的测试。

1. 开发者测试。鼓励开发者写单元测试,但很多时候有太多的依赖假设,单测等是很难的,比如要测试U盘在读写过程中被拔掉,再插上的情况,只有实际运行执行才能验证程序是否正确。无论开发人员使用何种方式,都需要保证负责的代码是经过测试了的。不需要在所有版本上运行,但需要确保代码质量,不能假定的字节序、字节长度,都应该使用标准接口。有些时候是修复别人报的bug,而开发人员又没有复现该bug的环境,此时修改也需要由bug提交者确认在其环境测试通过或者在其它能复现该bug的环境验证通过。对修改代码的质量保证方法还包括交由其它人评审通过或测试通过。编译器的报警也是需要修复的。

2. 社区测试。社区开发模式,也强调社区测试,鼓励大家在做好数据备份前提下使用最新发布的版本,类似软件正式发布前的试用版本,以确保在不同的机器不同的平台上能实际正常运行。对此类版本的使用是有风险的,有可能导致系统崩溃。通常在系统安装后,启动时也会比较小心,逐项加载启动,以检查每步都是正常的。还会做一些非常规的操作,也就是异常测试。

3. 配置测试。Linux kernel的配置也较复杂,以支持较好的灵活性和可扩展性,测试需要尽量地覆盖不同的配置组合条件。有的做法是随机配置,再编译运行启动,7*24小时重复不间断地做,以找出可能存在的问题。做的过程中也有优化,比如关闭一些不必要的选项,减少编译的时间。

有些配置项是有助于测试执行时监控问题和分析问题的,比如打印出debug日志,或者在出错时打印尽可能多的信息,kernel在某些设置下自身也会做一些运行过程中的检查,如CONFIG_DETECT_SOFTLOCKUP能检查出内核部分是否在内核模式中循环超过60秒的bug,这类配置通常会在测试时打开。

4. 硬件测试。验证kernel对不同平台的支持时,会使用些不常用的硬件,以及不常用的硬件组合,不同的体系架构。

5. 对待变化。由于Linux kernel经常变化,不可能每次变动都同等对待。针对-rc候选发布版本,需要严格地测试,因为它们一旦被认为是稳定的之后就会正式发布,并很可能由发行商选中到发行版中。而针对-next树或者以前的-mm树,因为它们太容易变化了,没有时间和资源运行全量测试,通常只执行最基本的测试。

6. 工具。Linux测试工具众多,可见http://ltp.sourceforge.net/tooltable.php,覆盖率、安全性、调试、网络、性能等方方面面工具都应有尽有。

7. 性能测试。Linux kernel对性能要求也比较高,性能测试通常要注意的几点:(1)最好是利用benchmark,以取得可信数据;(2)避免I/O缓存因素,缓存对性能影响较大;(3)确保测量环境的稳定性,特别是比较前后版本时。Autotest中也包含性能测试自动化,一次升级导致的性能问题和修复如下图六所示,2.6.14-rc2-mm1版本与2.6.14-rc1-mm1版本相比执行时间由101增加到了111,性能降低了10%,直到2.6.16-rc1-mm4版本才修复。

图六 Autotest性能测试执行结果图

8. 可测性。Linux kernel在可测性方面也有较好支持。

(1)内核模块化。启动内核模块支持后,内核的某些部分就可以在需要的时候才装载到内存中,使内核变得更小,运行更快。模块化的内核部分可以在系统运行期间装载或者卸载。

(2)内核hacking 选项,编译一个测试用的内核,有助有内核特定功能的调试,也需要注意有部分选项会导致性能降低。

(3)Magic SysRq键,神奇的键盘快捷键,可以利用快捷键直接向内核传递特定的指定。

(4)多种信息收集方式,内核的bug不一定是导致系统崩溃,一些严重的错误通过日志等表现出来,信息收集就变得犹为重要了。可以通过Syslog, console, dmesg等方式dump和显示日志,除此之外,还可以通过串口和网络控制台远程收集信息。

(5)kexec快速重启系统,使用 kexec可以直接重新启动到另一个内核,不再必须通过固件和引导装载程序阶段,能为测试节省大量时间。

(6)使用biselect追查问题,当发现一个bug却不知道是由哪个补丁引入时,使用二分查找,代码管理系统git以及补丁管理工具quilt都支持二分查找。

9. 覆盖率。关于Linux kernel的测试覆盖率,没有看到最新的统计,不过以前数据多是在20%左右的分支覆盖率。

虽然说自动化测试越来越重要,但是将内核部分测试自动化却是非常困难的,自动化只涵盖了一少部分功能,性能的自动化看起来做得更多些。质量更多还是由开发人员以及社区测试来保证。另外一方面看,Linux kernel本身的架构应该也有较高的容错性和可扩展性,以支持如此大量又频繁的修改。

说明

写本文时,我还并没有实际参与过Linux kernel的开发或测试,只是对其有兴趣,于是在网上查阅了大量文档,有些信息可能已经过时或者与实际情况不符,仅供参考。

引用说明

网上链接

Crackerjack Project http://ossipedia.ipa.go.jp/crackerjack/index.html

Linux命名争议 http://en.wikipedia.org/wiki/GNU/Linux_naming_controversy

Linux Test Project,测试Linux kernel以及相关功能 http://ltp.sourceforge.net/

Linux测试工具 http://ltp.sourceforge.net/tooltable.php

Linux 维基百科 http://en.wikipedia.org/wiki/Linux

Linux Kernel维基百科 http://en.wikipedia.org/wiki/Linux_kernel

开发流程 http://linuxwireless.org/en/developers/process

Autotest项目http://autotest.github.com/

Greg Kroah Hartman on the Linux Kernel视频http://www.youtube.com/watch?v=L2SED6sewRw

http://stackoverflow.com/questions/3177338/how-is-linux-kernel-tested

kernel官网 http://www.kernel.org

Linux kernel邮件列表FQA http://ftp.osuosl.org/pub/linux/docs/lkml/

Pdf电子书

《Best Practices in Linux Kernel Testing》

《Linux Kernel Tester’s Guide》version 0.3,中文版《Linux 内核测试指南》

《How to Participate in the Linux Community》2008.8

《Linux Kernel Development:How Fast it is Going, Who is Doing It, What They are Doing, and Who is Sponsoring It》2012.3

2014-02-19 15:39:28 shixha 阅读数 966

Firstly, we need the suitable Glibc version

My target Linux with kernel-2.6.9, and my host is Kernel-2.6.32, so I can use Glibc version 2.12.1

Infomation from the project Readme :

When working with Linux kernels, the GNU C Library version from

version 2.4 on is intended primarily for use with Linux kernel version

2.6.0 and later. 
 

 


 

wget http://ftp.gnu.org/gnu/libc/glibc-2.12.1.tar.bz2

tar -xjf glibc-2.12.1.tar.bz2

mkdir glibc-2.12.1-build

cd glibc-2.12.1-build/

../glibc-2.12.1/configure --prefix=/usr/local/glibc-2.12.1 --build=x86_64-redhat-linux-gnu --host=x86_64-redhat-linux-gnu --enable-kernel=2.6.0 --enable-static-nss

You need add the --enable-static-nss option otherwise you may get the following Warnning when compile your program with this glibc:

warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

Error1:

 

/home/kimi/soft/glibc-2.12.1-build/libc.a(nsswitch.o):(.data+0x8): undefined reference to `_nss_files_getaliasent_r'

/home/kimi/soft/glibc-2.12.1-build/libc.a(nsswitch.o):(.data+0x18): undefined reference to `_nss_files_endaliasent'
/home/kimi/soft/glibc-2.12.1-build/libc.a(nsswitch.o):(.data+0x28): undefined reference to `_nss_files_setaliasent'
/home/kimi/soft/glibc-2.12.1-build/libc.a(nsswitch.o):(.data+0x38): undefined reference to `_nss_files_getaliasbyname_r'

....

It's a common bug since glic-2.3 version,a patchcan fix it 

patch -p1 < ../patchfile

 

make -j4

make install

 

 

Secondly, Use -static Option

gcc hello.c -o hello -L/usr/local/glibc-2.12.1/lib  -static -nostdlib  /usr/local/glibc-2.12.1/lib/crt1.o /usr/local/glibc-2.12.1/lib/crti.o -lm -lc -lgcc -lgcc_eh  -lc  /usr/local/glibc-2.12.1/lib/crtn.o

 

Reference

http://stackoverflow.com/questions/8961622/how-to-port-c-c-applications-to-legacy-linux-kernel-versions

http://blog.csdn.net/force_eagle/article/details/6758045

 

Linux kernel测试初探

阅读数 1304

Linux kernel-CPU 调频

阅读数 2440

Linux Kernel 4.0.2

阅读数 1100

没有更多推荐了,返回首页