Linux bonding源码分析

Linux bonding源码分析
Linux bonding源码分析

1. 目的

本文档结合相关内核代码和对Linux 2.6.9内核中Bonding模块的三种主要工作模式的工作原理和流程。在配置Bond模块时,除了资料[2],本文档也有一定的参考价值。

2. 内容

本文档包含下列内容:

* Bonding模块工作流程综述。(第3节)

* Bonding链路状态监控机制(mii模式、arp模式)描述。(第4节)

* Bonding模块三种主要工作模式:balance-rr、active- backup和broadcast相关代码分析。(第5节)

* Bonding模块关键数据结构和函数的代码分析。(第5节)

如果想了解bonding模块的原理和工作流程,请阅读3、4节,如果想进一步分析bonding 模块的代码,请阅读5节。

3. Bonding模块工作流程综述

Bonding模块本质上是一个虚拟的网卡驱动(network device driver),只不过并没有真实的物理网卡与之对应,而是由这个虚拟网卡去“管辖”一系列的真实的物理网卡,所以它的代码结构和一般网卡驱动的代码结构非常类似,这是共性;除此之外,它还有自己的一些特性功能,例如特别的链路状态监控机制,绑定/解除绑定等。

3.1物理网卡的活动状态和链路状态

在bonding模块中为每一个被绑定的物理网卡定义了两种活动状态和四种链路状态:注意,这里的链路状态和实际网卡真实的链路状态(是否故障、是否有网线连接)没有直接的关系,虽然bonding模块通过MII或者ARP侦测到实际网卡故障时也会改变自定义的链路状态值(例如从BOND_LINK_UP切换到BOND_LINK_FAIL随后切换到 BOND_LINK_DOWN状态),但是概念上应该把这两类链路状态区分开。在本文档随后的内容中,除非特别指出,“链路状态”都指bonding模块自定义的链路状态。

活动状态:

* BOND_STATE_ACTIVE:处于该状态的网卡是潜在的发送数据包的候选者

* BOND_STATE_BACKUP:处于该状态的网卡在选择发送数据的网卡时被排除

链路状态:

* BOND_LINK_UP:上线状态(处于该状态的网卡是是潜在的发送数据包的候选者) * BOND_LINK_DOWN:故障状态

* BOND_LINK_FAIL:网卡出现故障,向状态BOND_LINK_DOWN 切换中

* BOND_LINK_BACK:网卡恢复,向状态BOND_LINK_UP切换中

一个网卡必须活动状态为BOND_STATE_ACTIVE并且链路状态为 BOND_LINK_UP,才有可能作为发送数据包的候选者,注意,这里所说的数据包并不包含ARP请求,在使用ARP链路状态监控时,一个处于BOND_LINK_BACK状态的网卡也可能发送ARP请求。

bonding模块的所有工作模式可以分为两类:多主型工作模式和主备型工作模式,balance-rr 和broadcast属于多主型工作模式而active-backup属于主备型工作模式。(balance-xor、自适应传输负载均衡模式(balance-tlb)和自适应负载均衡模式(balance-alb)也属于多主型工作模式,IEEE 802.3ad动态链路聚合模式(802.3ad)属于主备型工作模式,在本文档中不加以讨论)

在多主型工作模式中,如果物理网卡不出现故障,所有的物理网卡都处于BOND_STATE_ACTIVE和BOND_LINK_UP的状态下,参与数据的收发,此时:如果工作在balance-rr 模式中轮流向各个网卡发送数据,curr_active_slave字段(见5.1.3)指向前次发送数据(不包含ARP请求)的物理网卡,该指针每次发送过数据后都会切换到下一个物理网卡;在broadcast模式中向所有网卡发送数据,curr_active_slave字段除非网卡有故障发生不会切换。

在主备型工作模式中,如果物理网卡不出现故障,只有一块网卡(活动网卡)处于BOND_STATE_ACTIVE和BOND_LINK_UP的状态下,负责数据的收发,而其他网卡(后备网卡)处于BOND_STATE_BACKUP 和BOND_LINK_DOWN状态下,此时curr_active_slave字段指向当前的活动网卡。

如果工作在active-backup模式下,可以指定一个物理网卡作为主网卡(primitive interface),如果主网卡可用,就把主网卡作为当前活动网卡,否则在其他的可用网卡中选取一块网卡作为当前活动网卡,而一旦主网卡从故障中恢复,不管当前活动网卡是否故障都切换到主网卡。在balance-tlb和balance-alb模式中也可以指定主网卡,但是其意义和active-backup模式中并不相同。

3.2 数据收发流程

如果一个物理网卡被虚拟网卡绑定,则代表该网卡的数据结构struct net_device中下列字段将发生变化:

* flags字段(unsigned short)将包含IFF_SLAVE标志。

* master字段(struct net_device *)将指向虚拟网卡。

在主备型工作模式下,所有的非活动物理网卡的flags字段还将设置IFF_NOARP标志位表示对ARP请求不做出回应。

而代表虚拟网卡的struct net_device数据结构的flags字段将包含IFF_MASTER标志。

所有被绑定的物理网卡都将被设置相同的MAC地址和MTU值(和虚拟网卡也相同),这些

值将和第一块被绑定的物理网卡保持一致(“第一块网卡”并不是一个强制条件,这是由bonding模块的启动流程造成的,我们也可以手工设置虚拟网卡的MAC地址和MTU值,这个设定同时也将用于所有被绑定的物理网卡)。另外,所有被绑定的物理网卡没有IP地址,所以不参与发送IP数据包的路由选择。

在下面的三节中,只描述数据发送和接收过程中和bonding相关的一些特殊处理,关于Linux内核的一般数据包收发流程请参考资料[3][4],本文档不再赘述。

3.2.1 接收数据

无论在何种模式下,只要物理网卡的实际链路状态正常,任何被绑定的物理网卡都可以接收数据(虽然没有IP地址,但是仍然有MAC地址),即使是处于BOND_STATE_BACKUP和BOND_LINK_DOWN状态时,这是由于BOND_STATE_BACKUP和BOND_LINK_DOWN是bonding模块自己定义的管理物理网卡所用的状态,和内核的TCP/IP栈没有任何关系,bonding模块最多在主备模式下给备用物理网卡设置IFF_NOARP标志,使它对ARP数据包不做出回应,仅此而已。

收取数据包时,物理网卡驱动的中断处理函数把数据包放入接收队列中,随后软中断NET_RX_SOFTIRQ的处理函数net_rx_action被调用,该函数将调用接收数据包的物理网卡网卡的poll函数。

无论一个物理网卡是否支持NAPI,函数netif_receive_skb都将在某个阶段被调用。(如果物理网卡不支持NAPI,内核使用函数process_backlog代替真实的poll调用,而process_backlog调用netif_receive_skb)。

在netif_receive_skb函数中将调用函数skb_bond,该函数本质上作如下操作:

if(dev->master) skb->dev = dev->master;

即把数据包skb的物理网卡字段替换为虚拟网卡,使得该数据包看起来像是从虚拟网卡接收的一样,随后的处理和其他数据包没有任何差别,不再赘述。

3.2.2 发送数据

发送数据包时,内核根据路由选择某一个虚拟网卡作为发送接口(注意被绑定的物理网卡没有IP地址),最后调用该虚拟网卡的数据包传输接口net_device-> hard_start_xmit,注意此时该数据包中的dev字段指向虚拟网卡。net_device-> hard_start_xmit接口根据不同的工作模式指向不同的传输函数,但是无论是何种工作模式,最后bond_dev_queue_xmit函数都将被调用(以一个选定的物理网卡作为参数调用)。

bond_dev_queue_xmit函数将作如下操作:

skb->dev = slave_dev;

即替换skb的dev字段为选定的物理网卡。

随后,bond_dev_queue_xmit将调用标准的内核接口dev_queue_xmit把数据包放入选定物理网卡的发送队列中。

3.3 ARP请求和回应

既然被绑定物理网卡没有IP地址,那么如果接收到ARP请求之后,根据何IP地址决定是否产生应答?如果产生应答,在应答中,源IP地址应该是什么?

答案是:被绑定物理网卡接收到ARP请求后,由于函数arp_rcv在netif_receive_skb 之后被调用,而skb->dev经过netif_receive_skb的处理将指向虚拟网卡,所以是否产生应答由该物理网卡所属的虚拟网卡的IP地址决定(当然前提是物理网卡没有设置IFF_NOARP 标志),并且最终的ARP回应将包含虚拟网卡的IP地址(细节请参考arp_rcv和arp_process 函数)。

4 链路状态监控

链路状态监控有如下两个作用:

* 根据被绑定物理网卡的实际链路状态(是否故障、网线是否连接)更新bonding模块自定义的物理网卡链路状态和活动状态。

* 在主备模式下,根据自定义的物理网卡链路状态切换活动状态和当前的活动网卡。

Bonging模块支持两种模式的链路状态监控:通过MII ioctl调用直接进行或是通过发送ARP请求间接进行。前者通过调用物理网卡驱动程序的MII ioctl接口直接获得物理网卡是否故障的信息,后者通过向预定义的一组IP地址发送ARP请求,并观察被监控物理网卡是否有收到数据包(可能是ARP回答或者是一般数据包)间接进行。

这两种链路状态监控模式不能并存。参数arp_interval和miimon表示以毫秒为单位的检测间隔,在加载bonding模块时如果指定了非0的arp_interval参数并且miimon参数等于0,则使用ARP链路状态监控;如果指定了非0 miimon参数则使用MII链路状态监控(强制arp_interval = 0从而忽略arp_interval参数)。如果arp_interval和miimon都等于0则使用参数值为100的MII链路状态监控(强制miimon等于100 ms)。

如果使用ARP链路状态监控,还*必须*指定 arp_ip_target参数,该参数设定ARP监控时发送ARP请求的目标IP列表,各个IP之间使用逗号分隔。

如果使用MII链路状态监控,还*可以*指定参数updelay和downdelay作为从BOND_LINK_DOWN到BOND_LINK_UP或者从BOND_LINK_UP 到BOND_LINK_DOWN切换的时间间隔,这两个参数默认是0值。(在ARP链路状态监控中这两个参数没有用)

4.1 MII链路状态监控

MII链路状态监控可以用下列流程图表示

初始时,如果虚拟网卡工作在多主型工作模式下,则所有物理网卡的链路状态为BOND_LINK_UP,并且活动状态处于BOND_STATE_ACTIVE,IFF_NOARP标志都没有设置;否则所有物理网卡的链路状态为 BOND_LINK_UP,但是只有当前活动网卡的活动状态处于BOND_STATE_ACTIVE并且没有设置IFF_NOARP 标志,而其余网卡的活动状态为BOND_STATE_BACKUP并且IFF_NOARP标志被设置。

MII检测机制每miimon毫秒检测一遍所有被绑定物理网卡的状态。

①在某时刻,如果通过MII调用侦测到某一个物理网卡发生故障,则该物理网卡的链路状态立即被设置为BOND_LINK_FAILED。

②如果在downdelay毫秒内物理网卡恢复正常,则重新把网卡的链路状态设置为BOND_LINK_UP。

③如果在downdelay毫秒内物理网卡始终没有恢复正常,则该物理网卡的链路状态被设置为BOND_LINK_DOWN。如果虚拟网卡工作于主备型工作模式下,则活动状态被设置为BOND_STATE_BACKUP同时设置物理网卡的IFF_NOARP标志,并且如果出故障的是当前活动网卡,则通过一个重选择过程选择新的活动网卡(一般是第一块可用物理网卡)。

④如果一个链路状态为BOND_LINK_DOWN的物理网卡在MII检测过程中恢复正常,则立即把网卡的链路状态设置为BOND_LINK_BACK。

⑤如果在updelay毫秒内物理网卡又发生故障,就把链路状态重新设置为BOND_LINK_DOWN。

⑥如果在updelay毫秒内物理网卡始终保持可用状态,就把链路状态重新设置为BOND_LINK_UP。如果虚拟网卡工作于主备型工作模式下,则同时设置活动状态为BOND_STATE_ACTIVE并且清除物理网卡的IFF_NOARP标志,并且如果是主网卡恢复到BOND_STATE_ACTIVE状态,则会把当前活动网卡切换到主网卡。

4.2 ARP链路状态监控

4.2.1 active-backup工作模式下的ARP链路状态监控

该模式下的ARP链路状态监控可以分为两个阶段:

如果当前活动网卡(curr_active_slave不为NULL)存在,则以间隔 arp_interval毫秒从当前活动网卡向arp_targets表示的各个IP地址发送ARP请求,如果当前活动网卡在过去的2*arp_interval毫秒内没有数据包发送*和*接收并且已经作为活动网卡至少2*arp_interval毫秒,则把当前活动网卡的链路状态设置为BOND_LINK_DOWN并且试图在链路状态为BOND_LINK_UP或者BOND_LINK_BACK的网卡中选取一个网卡作为当前活动网卡。如果有这样一个网卡存在,则原来的活动网卡的活动状态被设置为BOND_STATE_BACKUP,并且IFF_NOARP标志被设置,新的活动网卡链路状态被设置为BOND_STATE_UP, 活动状态被设置

为BOND_STATE_ACTIVE,IFF_NOARP标志被清除。

如果上述过程没有选出新的活动网卡(正常情况下active-backup 模式下除当前活动网卡外所有网卡的链路状态都是BOND_LINK_DOWN,所以可能没有链路状态为BOND_LINK_UP或者BOND_LINK_BACK的后备网卡),则开始一个下述的选取活动网卡的过程:

从第一块可用(即IFF_UP标志被设置,netif_running(dev)和netif_carrier_ok(dev)都返回非0值)物理网卡开始,向arp_targets表示的各个IP地址发送ARP请求,然后观察所有的物理网卡,如果有物理网卡在arp_interval毫秒内有数据发送*和*接收,就把它设置为当前活动网卡,结束这个选取过程。否则换下一个可用物理网卡,重复这个过程。

注意即使物理网卡被设置IFF_NOARP标志,仍旧可以收到ARP应答数据包。

4.2.2 其他工作模式下的ARP链路状态监控

虚拟网卡每arp_interval遍历一遍所有被绑定物理网卡,如果在网卡在过去的2 * arp_interval毫秒内没有任何数据的发送*或者*接收,就把网卡的链路状态设置为BOND_LINK_DOWN,活动状态设置为BOND_STATE_BACKUP,如果在过去的arp_interval毫秒有数据包发送*和*接收,则把网卡的链路状态设置为BOND_LINK_UP,活动状态设置为BOND_STATE_ACTIVE。

在遍历过程中,对每一个可用的物理网卡(IFF_UP标志被设置,netif_running(dev)和netif_carrier_ok(dev)都返回非0值),都试图从该网卡向arp_targets表示的各个IP地址发送ARP请求,保证其他的被绑定的物理网卡可以收到ARP应答包。

5. 代码分析

5.1 关键数据结构

5.1.1 struct bond_params

文件:driver/net/bonding/Bonding.h

该结构是全局结构(每系统一个),对应于加载bonding模块时传入的各个参数

主要成员:

名称类型含义

mode int Bonding模块工作模式

BOND_MODE_ROUNDROBIN balance-rr模式

BOND_MODE_ACTIVEBACKUP active-backup模式

BOND_MODE_XOR balance-xor模式

BOND_MODE_BROADCAST broadcast模式

BOND_MODE_8023AD IEEE 802.3ad动态链路聚合模式

BOND_MODE_TLB 自适应传输负载均衡模式

BOND_MODE_ALB 自适应负载均衡模式

miimon int 使用MII链路状态监控时的时间间隔(ms)

arp_interval int 使用arp链路状态监控时的时间间隔(ms)

use_carrier int 使用MII链路状态监控时是否使用更新的carrier调用

updelay int 使用MII链路状态监控时从BACK状态切换到UP状态的时延(ms)

downdelay int 使用MII链路状态监控时从FAIL状态切换到DOWN状态的时延(ms)

primary char[] 用来在active-backup、balance-tlb和balance-alb模式中指定主网卡arp_targets u32[] 在ARP链路状态监控中将向这些IP地址发送ARP请求。

5.1.2 struct slave

文件:driver/net/bonding/Bonding.h

每一个被管辖的物理网卡对应一个该数据结构的实例

主要成员:

名称类型含义

dev struct net_device* 指向被绑定的物理网卡

next,prev struct slave * 所有的slave数据结构通过这两个指针双向链接到一起形成*循环*链表

delay s16 用于保存MII链路状态监控和ARP链路状态监控的时延值。

jiffies u32 用于active-backup模式下的ARP状态监控

link s8 表示对应网卡的链路状态,取下列四个值之一:

BOND_LINK_UP 上线状态

BOND_LINK_DOWN 故障状态

BOND_LINK_FAIL 网卡出现故障,状态BOND_LINK_DOWN切换中

BOND_LINK_BACK 网卡恢复,状态BOND_LINK_UP切换中

state s8 表示对应网卡活动状态,取下列两个值之一:

BOND_STATE_ACTIVE 活动状态

BOND_STATE_BACKUP 后备状态

original_flags u32 保存被绑定物理网卡原来的flags

perm_hwaddr u8[] 保存被绑定物理网卡原来的MAC地址

ad_info struct

ad_slave_info

记录IEEE 802.3ad动态链路聚合模式下的“每网卡”相关状态信息

tlb_info struct tlb_slave_info 记录自适应传输负载均衡模式下的“每网卡”相关状态信息

link_failure_count u32 网卡从BOND_LINK_FAIL切换到BOND_LINK_DOWN的次数

speed u16 记录网卡的传输速度(10M/100M/1000G)

duplex u8 网卡工作模式(全双工?)

5.1.3 struct bonding

文件:driver/net/bonding/Bonding.h

每一个虚拟网卡对应一个该数据结构的实例。

主要成员:

名称类型含义

dev struct net_device* 指向虚拟网卡(例如bond0、bond1等等)

first_slave struct slave * 指向被绑定的第一个物理网卡对应的slave结构。

curr_active_slave struct slave * 指向当前活动的网卡对应的slave结构,在不同的工作模式下有不同的含义。

current_arp_slave struct slave * 用于ARP状态监控(只用于bond_activebackup_arp_mon)

primary_slave struct slave * 如果使用BOND_MODE_ACTIVEBACKUP、BOND_MODE_TLB或者BOND_MODE_ALB模式,则指向主物理网卡对应的slave结构(primary_slave)

slave_cnt s32 虚拟网卡所管辖的物理网络接口的个数

lock rwlock_t 每一个虚拟网卡管辖一系列物理网卡,每一个物理网卡对应一个slave结构,所有的slave被放在一个链表中,这个读写锁用来保护该链表。

curr_slave_lock rwlock_t 用来保护curr_active_slave的读写锁。

mii_timer struct timer_list 用于MII链路状态监控的定时器

arp_timer struct timer_list 用于ARP链路状态监控的定时器

kill_timers s8 如果该标志置位,所有的计时器超时后就不再重新设置,从而可以被安全删除

bond_list struct list_head 通过该结构,所有的bonding数据结构被连接为双向链表,链表头是全局变量bond_dev_list。

5.2 关键函数

本节描述了bonding模块关键函数的操作流程,这些函数是基本的原子模块,其他没有被列举的函数仅仅是对这些函数的简单包装。

5.2.1 模块初始化/释放

5.2.1.1 初始化

* bonding_init

原型:

static int __init bonding_init(void)

bonding_init作为bonging模块的初始化函数,在模块被加载时被调用。它主要做如下工作:

1. 调用函数bond_check_params解析传入模块的参数并检查其合法性,结果放入数据结构params中。其中params是一个类型为bond_params的全局变量。

2. 如果内核支持proc文件系统,调用bond_create_proc_dir在/proc/net下创建目录/proc/net/bonding。

3. 如果传入参数指定了bond设备的个数(通过参数max_bonds),则通过下列步骤创建max_bonds个bond设备(从bond0到bondN)

1. 调用alloc_netdev和dev_alloc_name创建网络设备,指定每一个设备的私有数据是一个bonding结构(dev->priv)。

2. 为每一个新创建的虚拟网络设备调用bond_init函数。

3. 调用register_netdevice注册这个新创建的网络设备。

4. 调用register_netdevice_notifier,注册函数bond_netdev_notifier为网络事件处理函数。

* bond_init

原型:

static int __init bond_init(struct net_device *bond_dev, struct bond_params *params)

该函数对每一个新创建的虚拟网络设备调用一次。它主要做下列工作。

1. 取出虚拟网络设备bond_dev的私有数据块,用bond指向它(struct bonding *bond)。

2. 初始化两个读写锁bond->lock和bond->curr_slave_lock。

3. 把bond->first_slave、bond->curr_active_slave、bond->current_arp_slave、bond->primary_slave全部置为NULL。bond->dev指向属主网络设备bond_dev。

4. 设置bond_dev的一系列通用接口函数,例如open、close、get_stats、do_ioctl、set_multicast_list、change_mtu和set_mac_address。

5. 根据不同的工作模式,设置bond_dev的通用接口函数hard_start_xmit 指向不同的目的函数。例如如果工作模式是BOND_MODE_ROUNDROBIN,则 hard_start_xmit指向函数bond_xmit_roundrobin。

6. 设置bond_dev->tx_queue_len为0,表示发送队列大小没有限制。

7. 设置bond_dev->flags为IFF_MASTER|IFF_MULTICAST表示该设备支持Muticase并且是一个流量均衡组中的master(被它管辖的其他物理网卡将被设置IFF_SLAVE标志)。

8. 其他和VLAN相关的标志设置和初始化。

9. 如果内核支持proc文件系统,调用bond_create_proc_entry在目录/proc/net/bonding下创建对应的proc文件。

10. 调用list_add_tail把该bonding数据结构添加到bond_dev_list中。

5.2.1.2 释放

* bonding_exit

原型:

static void __exit bonding_exit(void)

该函数在模块被卸载的时候被调用,它主要做如下工作:

1. 调用unregister_netdevice_notifier注销网络事件处理函数。

2. 调用bond_free_all注销所有形如bondN的虚拟网络接口。bond_free_all遍历bond_dev_list链表,并且对其中的每一个类型为 struct bonding*的数据结构bond做如下操作:

1. 调用unregister_netdevice注销bond->dev

2. 调用bond_deinit(bond->dev)

3. 调用bond_destroy_proc_dir删除/proc/net/bonding目录

* bond_deinit

原型:

static inline void bond_deinit(struct net_device *bond_dev) 该函数对每一个虚拟网卡的实例调用一次,它主要做如下操作:

1. 调用list_del把虚拟网卡对应的bonding数据结构从bond_dev_list链表中摘除。

2. 调用bond_remove_proc_entry删除/proc/net/bonding目录中的对应文件。

5.2.2 物理网卡的绑定/解除绑定

在下面的讨论中,假定ifenslave使用新的ABI接口,即:

* 在绑定物理网卡时,如果虚拟的网卡还没有MAC地址,则ifenslave通过IOCTL把该虚拟网卡的MAC地址设置为该物理网卡的MAC地址(保证bond_enslave被调用时虚拟网卡

已经有了MAC地址)。

* 如果被绑定网卡处于UP状态,则ifenslave首先把它设置为DOWN状态(保证bond_enslave被调用时被绑定物理网卡处于DOWN状态)。

如果使用旧版本的ABI接口,则虚拟的网卡的MAC地址由bonding模块在bond_enslave 函数中自行设置,并且被绑定网卡在bond_enslave被调用时可能处于UP状态,需要由bond_enslave函数自行处理。

5.2.2.1 绑定

* bond_enslave

原型:

static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)

该函数在试图把一个物理网卡绑定到一个虚拟的网卡时被调用,其中bond_dev表示虚拟的网卡,slave_dev表示真实的物理网卡。该函数主要做如下操作:

1. 取出bond_dev的私有数据,用bond指向它(struct bonding *)。

2. 一系列的合法性检查,包括:

1. bond_dev的flags是否已经设置了IFF_UP(虚拟的网卡必须已经处于UP状态)

2. slave_dev的flags是否没有设置IFF_SLAVE(防止同一个物理网卡被绑定两次)

3. bond_dev的flags如果设置了NETIF_F_VLAN_CHALLENGED,则bond->vlan_list 不能为空。

4. slave_dev->flags是否没有设置IFF_UP(物理网卡应该处于DOWN状态)

5. slave_dev->set_mac_address不能为NULL(物理网卡应该支持设置MAC地址)

3. 调用kmalloc分配一个新的slave结构。

4. 把slave_dev->flags保存在slave->original_flags中。

5. 把slave_dev原有的MAC地址保存在slave-> perm_hwaddr中。

6. 设置slave_dev新的MAC地址为虚拟网卡的MAC地址。

7. 调用netdev_set_master设置slave_dev,该函数主要作如下操作:

1. 设置slave_dev->flags的IFF_SLAVE标志。

2. 设置slave_dev->master指向虚拟网卡bond_dev。

8. 设置slave->dev指向slave_dev。

9. 如果bond_dev工作在模式BOND_MODE_TLB或者BOND_MODE_ALB,对slave调用

bond_alb_init_slave函数。

10. 维护和Multicast以及VLAN相关的一系列数据结构。

11. 调用bond_attach_slave把slave加入bond的链表(通过维护bond-> first_slave 和slave结构的next,prev指针)

12. 把slave的delay和link_failure_count都清零。

13. 监测slave_dev的链路状态:

1. 如果使用MII链路,并且bond_check_dev_link返回BMSR_LSTATUS(表示链路正常),或者不使用MII链路监控,则根据updelay是否为0把slave->link设置为BOND_LINK_BACK或者BOND_LINK_UP。

2. 如果使用MII链路,并且bond_check_dev_link返回非BMSR_LSTATUS值,则设置slave->link为BOND_LINK_DOWN。

14. 调用bond_update_speed_duplex更新slave_dev的链路速率,如果失败则设置slave_dev的链路速率为100M全双工。

15. 如果虚拟网卡工作在BOND_MODE_ACTIVEBACKUP、BOND_MODE_TLB或者BOND_MODE_ALB模式下,并且slave_dev是用户指定的主网卡,则设置bond->primary_slave 为slave_dev。

16. 设置bond->curr_active_slave和slave的活动状态,维护VLAN和Multicast相关数据结构:

1. 如果虚拟网卡工作在BOND_MODE_ACTIVEBACKUP:如果bond->curr_active_slave没有被设置或者bond->curr_active_slave不响应ARP(设置了IFF_NOARP标志),并且slave_dev不处于BOND_LINK_DOWN状态,则设置slave_dev为活动网卡(设置BOND_STATE_ACTIVE标志,清除IFF_NOARP标志),否则设置slave_dev为后备网卡(设置BOND_STATE_BACKUP标志,设置IFF_NOARP标志)。

2. 如果虚拟网卡工作在BOND_MODE_ROUNDROBIN或者 BOND_MODE_BROADCAST:直接设置slave_dev的活动状态为BOND_STATE_ACTIVE(在BOND_MODE_ROUNDROBIN和BOND_MODE_BROADCAST 模式下,IFF_NOARP标志始终被清除),如果没有设置bond->curr_active_slave,则设置bond->curr_active_slave指向slave。

3. 其他工作模式:(还没有加以分析)

5.2.2.2 解除绑定

* bond_release

原型

static int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)

该函数在试图解除一个物理网卡的绑定状态时被调用,其中bond_dev表示虚拟的网卡,slave_dev表示真实的物理网卡。该函数主要做如下操作:

1. 取出bond_dev的私有数据,用bond指向它(struct bonding *)。

2. 寻找对应的slave结构。

3. 一系列的合法性检查,包括:

1. slave_dev->flags是否设置了IFF_SLAVE。

2. slave结构是否存在。

3. slave_dev原来的MAC地址是否和bond_dev相同,如果相同给出警告(防止MAC冲突)

4. 如果虚拟网卡工作在BOND_MODE_8023AD模式,调用bond_3ad_unbind_slave

5. 调用bond_detach_slave把slave从bond的链表中摘除(通过维护bond-> first_slave和slave结构的next,prev指针)。

6. 如果slave_dev是虚拟网卡以前的主物理网卡,则设置bond->primary_slave为NULL。

7. 如果slave_dev是虚拟网卡以前的活动网卡,则设置bond->active_slave为NULL (通过调用bond_change_active_slave函数)。

8. 如果虚拟网卡工作在模式BOND_MODE_TLB或者BOND_MODE_ALB则调用bond_alb_deinit_slave。

9. 如果slave_dev是虚拟网卡以前的活动网卡,则调用bond_select_active_slave 寻找一个新的活动网卡。

10. 如果虚拟网卡再也没有管辖的物理网卡,清除虚拟网卡的MAC地址(如果新调用ifenslave绑定物理网卡,则重新设置这个MAC地址)。

11. 维护VLAN和Multicast相关的数据结构。

12. 调用netdev_set_master解除master和slave的绑定关系并且调用dev_close 关闭slave_dev。

13. 恢复slave_dev的MAC地址(根据slave->perm_hwaddr)和flags(根据slave->original_flags)。

14. 调用kfree释放slave结构。

5.2.3 网卡驱动通用接口(interface service routines)

既然bonding模块本质上是一个虚拟网卡的驱动模块,所以必须提供一组所有网卡驱动模块都遵守的通用接口函数。

5.2.3.1 open/close

* bond_open(net_device->open接口)

原型:

static int bond_open(struct net_device *bond_dev)

该函数在对应的虚拟网卡被打开时调用(即使用ifup/ifconfig工具启动网卡的时候),

主要做如下操作(只分析三种主要模式):

1. 设置bond->kill_timers为0。

2. 如果使用MII链路状态监控:

1. 初始化mii_timer。

2. 设置超时时间mii_timer->expires为当前jiffies+1(立即调用bond_mii_monitor函数)

3. 设置bond_mii_monitor为定时器的超时处理函数。

3. 如果使用ARP链路状态监控:

1. 初始化arp_timer。

2. 设置超时时间arp_timer->expires为当前jiffies+1(立即调用定时器的超时处理函数)

3. 如果工作在BOND_MODE_ACTIVEBACKUP,设置bond_activebackup_arp_mon为超时处理函数。

4. 如果工作在其他模式,设置bond_loadbalance_arp_mon为超时处理函数。

* bond_close(net_device->stop接口)

原型:

static int bond_close(struct net_device *bond_dev)

该函数在对应的虚拟网卡被关闭时调用(即使用ifdown/ifconfig工具关闭网卡的时候),主要做如下操作(只分析三种主要模式):

1. 调用bond_mc_list_destroy维护Multicast相关数据结构。

2. 设置bond->kill_timers为1,所有的计时器超时后就不再重新设置,从而可以被安全删除。

3. 删除所有的定时器,包括mii_timer和arp_timer。

4. 调用bond_release_all释放所有被绑定的物理网卡,本质上该函数只是遍历slave 链表并且对每一个元素调用bond_release。

5. 如果虚拟网卡工作在BOND_MODE_TLB或者BOND_MODE_ALB模式下,调用bond_alb_deinitialize。

5.2.3.2 ioctl接口

* bond_do_ioctl(net_device->do_ioctl 接口)

原型:

static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd)

该函数是虚拟网卡的IOCTRL接口,仅仅根据不同的IOCTRL命令调用其他函数执行相应的功能,所以不再列出操作流程而仅仅列举出这些被调用的函数和相应的功能:

* 链路状态设置和查询(bond_ethtool_ioctl或者if_mii)

* Bonding模块状态查询(bond_info_query)

* 被绑定的物理网卡状态查询(bond_slave_info_query)

* 物理网卡的绑定和解除绑定(bond_enslave/bond_release)

* 虚拟网卡的MAC地址设置(bond_sethwaddr)

* 切换当前活动的物理网卡(bond_ioctl_change_active)

5.2.3.3 统计值查询

* bond_get_stats(net_device-> get_stats 接口)

原型:

static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)

该函数枚举所有被管辖的物理网卡,并且对每一个物理网卡调用get_stats接口,然后把对应的统计值加起来并作为最终的返回值,这些统计值包括。

名称含义

rx_packets 接收包总数

rx_bytes 接收字节总数

rx_errors 接收过程中错误数据包数

rx_dropped 接受过程中丢弃包数

tx_packets 发送包总数

tx_bytes 发送字节总数

tx_errors 发送过程中错误数据包数

tx_dropped 发送过程中丢弃包数

multicast Multicast数据包总数

collisions MAC地址冲突次数

rx_length_errors 接收数据包长度错误总数

rx_over_errors ring buff溢出次数

rx_crc_errors 接收数据包CRC校验错误总数

rx_frame_errors 接收数据包frame对齐错误总数

rx_fifo_errors 接收队列溢出次数

rx_missed_errors 接收时丢失的包数(仅仅对某些媒体有效)

tx_aborted_errors 发送取消次数(例如发送超时)

tx_carrier_errors 链路错误总数

tx_fifo_errors 发送队列溢出次数

tx_heartbeat_errors 心跳信号丢失(仅仅对某些媒体有效)

tx_window_errors 接收窗口错误(不明,需要进一步确认)

* bond_set_multicast_list(net_device-> set_multicast_list 接口)

原型:

static void bond_set_multicast_list(struct net_device *bond_dev)

该函数设置和Multicast和混杂模式相关的一组数据结构,由于三种主要工作模式并不过多地涉及这个函数,所以本文档不给出详细的说明。

* bond_change_mtu(net_device-> change_mtu 接口)

原型:

static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)

该函数把被虚拟网卡的MTU和被它管辖的所有物理网卡的MTU设置为同一值,主要做如下操作:

1. 枚举所有被管辖的物理网卡,对每一个物理网卡调用change_mtu设置新的MTU值,如果物理网卡没有change_mtu接口函数,则直接设置slave->dev->mtu等于new_mtu。

2. 设置bond_dev->mtu的值等于new_mtu。

* bond_set_mac_address(net_device-> set_mac_address 接口)

原型:

static int bond_set_mac_address(struct net_device *bond_dev, void *addr)

该函数设置虚拟网卡的MAC地址和被管辖的物理网卡的MAC地址为同一值,主要做如下操作:

1. 枚举所有被管辖的物理网卡,对每一个物理网卡调用set_mac_address设置新的MAC地址,如果物理网卡没有set_mac_address函数,则错误返回。

2. 设置bond_dev->dev_addr的值等于新的MAC地址。

5.2.3.4 数据包传输(接收/发送)

Bonding模块仅仅负责把发送数据包按照特定的工作模式转给被管辖的物理网卡发送,而每一个物理网卡负责自己的数据包接收,即虚拟网卡不管理各个物理网卡的数据接收过程,

它能做的仅仅是设置它们的IFF_NOARP标志,使某一个物理网卡对ARP请求不做出回应。

在模块初始化时, bond_init函数根据工作模式把net_device-> hard_start_xmit接口设置为不同的函数,对于BOND_MODE_ROUNDROBIN、BOND_MODE_ACTIVEBACKUP和BOND_MODE_BROADCAST 模式,该接口分别被设置为下列三个函数之一

* bond_xmit_roundrobin(net_device-> hard_start_xmit 接口)

原型:

static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev)

该函数用来在BOND_MODE_ROUNDROBIN模式中发送数据包,主要做如下操作:

1. 合法性检查,包括:

1. 检查bond_dev ->flags中IFF_UP标志是否设置

2. netif_running(bond_dev)是否返回非0值

3. 对应的虚拟网卡是否至少有一个管辖的物理网卡

2. 从bond->curr_active_slave开始遍历slave链表,找到第一个链路状态为BOND_LINK_UP,活动状态为BOND_STATE_ACTIVE的物理网卡并且调用bond_dev_queue_xmit 向这个物理网卡发送数据,然后设置bond->curr_active_slave为slave链表中的下一个物理网卡。

3. 如果没有找到这样的网卡或者bond_dev_queue_xmit返回非0值,则调用dev_kfree_skb丢弃数据包。

* bond_xmit_activebackup(net_device-> hard_start_xmit 接口)

原型:

static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev)

该函数用来在BOND_MODE_ACTIVEBACKUP模式中发送数据包,主要做如下操作:

1. 如果是试图发送ARP请求,则把全局变量my_ip设置为ARP请求中的发送方IP地址(skb->data+以太网头长度+ARP头长度+6),这个全局变量在ARP链路状态监控中被使用。

2. 合法性检查,包括:

1. 检查bond_dev ->flags中IFF_UP标志是否设置

2. netif_running(bond_dev)是否返回非0值

3. 对应的虚拟网卡是否至少有一个管辖的物理网卡

3. 如果bond->curr_active_slave不为空,则调用bond_dev_queue_xmit向这个物理网卡发送数据。

4. 否则,调用dev_kfree_skb丢弃数据包。

* bond_xmit_broadcast(net_device-> hard_start_xmit接口)

原型:

static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)

该函数用来在BOND_MODE_BROADCAST模式中发送数据包,主要做如下操作:

1. 合法性检查,包括:

1. 检查bond_dev ->flags中IFF_UP标志是否设置

2. netif_running(bond_dev)是否返回非0值

3. 对应的虚拟网卡是否至少有一个管辖的物理网卡

2. 从bond->curr_active_slave开始遍历slave链表,找到所有状态为BOND_LINK_UP,活动状态为BOND_STATE_ACTIVE的物理网卡,包括bond->curr_active_slave,调用bond_dev_queue_xmit向这些物理网卡发送数据(其中需要通过skb_clone复制skb结构)。

3. 如果发送失败,调用dev_kfree_skb丢弃数据包

* bond_dev_queue_xmit

原型:

int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev)

该函数被bond_xmit_roundrobin,bond_xmit_activebackup 和bond_xmit_broadcast 调用,向实际的物理网卡发送数据包,主要做如下操作:

1. 设置skb->dev为slave_dev(在此之前skb->dev指向虚拟网卡,现在指向真实的物理网卡)

2. 维护和VLAN相关的数据结构。

3. 调用dev_queue_xmit发送数据包。

* dev_queue_xmit

原型:

该函数不是bonding模块的一部分而是内核的一个标准接口,为了清楚起见也把它列出来,请参考net/core/dev.c文件。

int dev_queue_xmit(struct sk_buff *skb)

1. 如果底层的物理网卡不支持Scatter/Gather IO,而skb包含了分片(注意不是IP 分片,而是和DMA相关的一个概念,见skbbuff.h),则调用__skb_linearize合并分片。

2. 如果底层的设备不支持计算校验和,则计算一系列校验和。

3. 如果底层的设备有发送队列(qdisc),则把数据包放入发送队列中,退出。

4. 如果底层的设备没有发送队列(例如loopback或者其他没有真实物理网卡对应的设备,bonding模块自然也算一个),则直接调用底层设备的hard_start_xmit发送数据包。

5. 如果发送失败,调用dev_kfree_skb丢弃数据包

5.2.4 链路状态监控

5.2.4.1 MII链路状态监控

* bond_mii_monitor

原型:

static void bond_mii_monitor(struct net_device *bond_dev)

如果使用MII链路状态监控,则该函数被周期调用以检测每一个被绑定物理网卡的链路状态, 主要做如下操作:

1. 计算局部变量delta_in_ticks = (bond->params.miimon * HZ) / 1000,即miimon 参数的jiffies表示。

2. 如果kill_timers被设置,直接退出。

3. 如果没有任何物理网卡被绑定,重新设置定时器,退出。

4. 根据bond_check_dev_link的结果,按照第5节描述的MII链路状态监控模型设置网卡的链路状态。

5. 如果原来物理网卡的链路状态为BOND_LINK_FAIL,而 bond_check_dev_link返回非BMSR_LSTATUS值,则除了把链路状态设置为BOND_LINK_DOWN之外,还做如下操作:

1. 如果虚拟网卡工作在模式BOND_MODE_8023AD,调用bond_3ad_handle_link_change

2. 如果虚拟网卡工作在模式BOND_MODE_TLB或者BOND_MODE_ALB模式下,调用bond_alb_handle_link_change。

3. 如果当前被检查的slave不是curr_active_slave,设置标志do_failover表明可能会发生slave切换。

6. 如果原来物理网卡的链路状态为BOND_LINK_BACK而bond_check_dev_link 返回BMSR_LSTATUS,则除了把链路状态设置为BOND_LINK_UP之外,还做如下操作:

1. 如果虚拟网卡工作在模式BOND_MODE_8023AD或者被监测网卡不是primary_slave,则设置物理网卡的活动状态为BOND_STATE_BACKUP

2. 如果虚拟网卡*不是*工作在模式BOND_MODE_ACTIVEBACKUP,则设置物理网卡的活动状态为BOND_STATE_ACTIVE

3. 如果虚拟网卡工作在模式BOND_MODE_8023AD,调用bond_3ad_handle_link_change

4. 如果虚拟网卡工作在模式BOND_MODE_TLB或者BOND_MODE_ALB模式下,调用bond_alb_handle_link_change。

5. 如果当前被检查的slave不是curr_active_slave,设置标志do_failover表明可能会发生slave切换。

7. 调用bond_update_speed_duplex更新物理网卡的速率。

8. 如果do_failover被设置,调用bond_select_active_slave。

9. 设置定时器的超时值为jiffies+delta_in_ticks。

* bond_check_dev_link

原型:

static int bond_check_dev_link(struct bonding *bond, struct net_device *slave_dev, int reporting)

该函数调用MII/ETHTOOL IOCTL或者使用netif_carrier_ok()检查链路是否正常工作(如果用户指定了use_carrier)参数,如果该函数返回BMSR_LSTATUS表明链路是正常的,否则表示链路故障(例如掉网线等等)。

5.2.4.2 ARP链路状态监控

* bond_loadbalance_arp_mon

原型:

Android Hotfix 新方案——Amigo 源码解读

Android Hotfix 新方案——Amigo 源码解读 首先我们先来看看如何使用这个库。 用法 在project 的build.gradle中 dependencies { classpath 'me.ele:amigo:0.0.3' } 在module 的build.gradle中 apply plugin: 'me.ele.amigo' 就这样轻松的集成了Amigo。 生效补丁包 补丁包生效有两种方式可以选择: ? 稍后生效补丁包 ? 如果不想立即生效而是用户第二次打开App 时才打入补丁包,则可以将新的Apk 放到/data/data/{your pkg}/files/amigo/demo.apk,第二次打开时就会自动生效。可以通过这个方法 ? File hotfixApk = Amigo.getHotfixApk(context); ?

获取到新的Apk。 同时,你也可以使用Amigo 提供的工具类将你的补丁包拷贝到指定的目录当中。 ? FileUtils.copyFile(yourApkFile, amigoApkFile); ? ? 立即生效补丁包 ? 如果想要补丁包立即生效,调用以下两个方法之一,App 会立即重启, 并且打入补丁包。 ? Amigo.work(context); ? Amigo.work(context, apkFile); ? 删除补丁包 如果需要删除掉已经下好的补丁包,可以通过这个方法 Amigo.clear(context); 提示:如果apk 发生了变化,Amigo 会自动清除之前的apk。 自定义界面 在热修复的过程中会有一些耗时的操作,这些操作会在一个新的进程中的Activity 中执行,所以你可以通过以下方式来自定义这个Activity。

Android源码下载方法详解

Android: Android源码下载方法详解 分类:Android平台 安卓源码下载地址:https://www.360docs.net/doc/a42924673.html,/source/downloading.html 相信很多下载过内核的人都对这个很熟悉 git clone git://https://www.360docs.net/doc/a42924673.html,/kernel/common.git kernel 但是这是在以前,现在如果这么执行的话,会显示如下内容 Initialized empty Git repository in /home/star/working/kernel/.git/ https://www.360docs.net/doc/a42924673.html,[0: 149.20.4.77]: errno=Connection refused fatal: unable to connect a socket (Connection refused) 通过浏览器输入https://www.360docs.net/doc/a42924673.html,/,发现该网站已经被重定向为 https://www.360docs.net/doc/a42924673.html,/source/downloading.html 可以在该页面的最后发现内核的下载方法。 下面我们介绍一下Android源码下载的步骤。 工作环境: 操作系统:Ubuntu 10.04 或Ubuntu10.10 git程序:1.7.0.4 或1.7.1 转载请注明出处:https://www.360docs.net/doc/a42924673.html,/pku_android 方法一: 1.1 初始化安装环境 参考网页https://www.360docs.net/doc/a42924673.html,/source/initializing.html 主要要做的就是安装jdk和安装一些软件包 $ sudo apt-get install git-core gnupg flex bison gperf build-essential \ zip curl zlib1g-dev libc6-dev libncurses5-dev x11proto-core-dev \ libx11-dev libreadline6-dev libgl1-mesa-dev tofrodos python-markdown \ libxml2-utils 如果已经安装了,就不许要这步了 1.2 无论下载内核和源码,都需要进行如下操作 参考网页https://www.360docs.net/doc/a42924673.html,/source/downloading.html $ mkdir ~/bin $ PATH=~/bin:$PATH $ curl https://https://www.360docs.net/doc/a42924673.html,/dl/googlesource/git-repo/repo > ~/bin/repo 如果出现: repo init error: could not verify the tag 'v1.12.7',

Android源代码结构分析

目录 一、源代码结构 (2) 第一层次目录 (2) bionic目录 (3) bootloader目录 (5) build目录 (7) dalvik目录 (9) development目录 (9) external目录 (13) frameworks目录 (19) Hardware (20) Out (22) Kernel (22) packages目录 (22) prebuilt目录 (27) SDK (28) system目录 (28) Vendor (32)

一、源代码结构 第一层次目录 Google提供的Android包含了原始Android的目标机代码,主机编译工具、仿真环境,代码包经过解压缩后,第一级别的目录和文件如下所示: . |-- Makefile (全局的Makefile) |-- bionic (Bionic含义为仿生,这里面是一些基础的库的源代码) |-- bootloader (引导加载器),我们的是bootable, |-- build (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具) |-- dalvik (JAVA虚拟机) |-- development (程序开发所需要的模板和工具) |-- external (目标机器使用的一些库) |-- frameworks (应用程序的框架层) |-- hardware (与硬件相关的库) |-- kernel (Linux2.6的源代码) |-- packages (Android的各种应用程序) |-- prebuilt (Android在各种平台下编译的预置脚本) |-- recovery (与目标的恢复功能相关) `-- system (Android的底层的一些库)

Android USB 驱动分析

Android USB 驱动分析 一、USB驱动代码架构和使用 1、代码简介 USB驱动代码在/drivers/usb/gadget下,有三个文件:android.c, f_adb.c, f_mass_storage.c;g_android.ko 是由这三个文件编译而来,其中android.c 依赖于 f_adb.c 和 f_mass_storage.c(这两个文件之间无依赖关系)。 可在android.c中看到: static int __init android_bind_config(struct usb_configuration *c) { struct android_dev *dev = _android_dev; int ret; printk(KERN_DEBUG "android_bind_config\n"); ret = mass_storage_function_add(dev->cdev, c, dev->nluns); if (ret) return ret; return adb_function_add(dev->cdev, c); } 2、驱动使用 要使USB mass storage连接到主机: 打开/sys/devices/platform/usb_mass_storage/lun0/file文件,向 file文件写入一个存储 设备的路径,例如/dev/block/vold/179:0 (major:minor)路径; 这里的usb_mass_storage根据实际应用可以改的,由 platform_device_register函数的参数决 定。 例如: static struct platform_device fsg_platform_device = { .name = "usb_mass_storage", .id = -1, }; static void __init tegra_machine_init(void) { .... (void) platform_device_register(&fsg_platform_device); .... }

Android 串口编程原理和实现方式附源码

提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:。下面我分别对JNI、以及串口的一些知识点和实现的源码进行分析说明。这里主要是参考了开源项目android-serialport-api。 串口编程需要了解的基本知识点:对于串口编程,我们只需对串口进行一系列的设置,然后打开串口,这些操作我们可以参考串口调试助手的源码进行学习。在Java中如果要实现串口的读写功能只需操作文件设备类:即可,其他的事都由驱动来完成不用多管!当然,你想了解,那就得看驱动代码了。这里并不打算对驱动进行说明,只初略阐述应用层的实现方式。 (一)JNI: 关于JNI的文章网上有很多,不再多做解释,想详细了解的朋友可以查看云中漫步的技术文章,写得很好,分析也很全面,那么在这篇拙文中我强调3点: 1、如何将编译好的SO文件打包到APK中?(方法很简单,直接在工程目录下新建文件夹libs/armeabi,将SO文件Copy到此目录即可) 2、命名要注意的地方?(在编译好的SO文件中,将文件重命名为:lib即可。其中是编译好后生成的文件) 3、MakeFile文件的编写(不用多说,可以直接参考package/apps目录下用到JNI的相关项目写法) 这是关键的代码: [cpp]view plaincopy

(二):

文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。文件描述符的主要实际用途是创建一个包含该结构的或。这是API的描述,不太好理解,其实可简单的理解为:就是对一个文件进行读写。 (三)实现串口通信细节 1) 建工程:SerialDemo包名:org.winplus.serial,并在工程目录下新建jni和libs两个文件夹和一个org.winplus.serial.utils,如下图: 2) 新建一个类:SerialPortFinder,添加如下代码: [java]view plaincopy 1.package org.winplus.serial.utils; 2. 3.import java.io.File; 4.import java.io.; 5.import java.io.IOException; 6.import java.io.LineNumberReader; 7.import java.util.Iterator; 8.import java.util.Vector; 9. 10.import android.util.Log; 11. 12.public class SerialPortFinder { 13. 14.private static final String TAG = "SerialPort"; 15.

App工程结构搭建:几种常见Android代码架构分析

App工程结构搭建:几种常见Android代码架构分析 关于Android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角度,看到整齐的代码,优美的分层总是一种舒服的享受的。 从艺术的角度看,其实我们是在追求一种美。 本文先分析几个当今比较流行的android软件包,最后我们汲取其中觉得优秀的部分,搭建我们自己的通用android工程模板。 1. 微盘 微盘的架构比较简单,我把最基本,最主干的画了出来: 第一层:com.sina.VDisk:com.sina(公司域名)+app(应用程序名称) 。 第二层:各模块名称(主模块VDiskClient和实体模块entities)第三层:各模块下具体子包,实现类。 从图中我们能得出上述分析中一个最简单最经典的结构,一般在应用程序包下放一些全局的包或者类,如果有多个大的模块,可以分成多个包,其中包括一个主模块。 在主模块中定义基类,比如BaseActivity等,如果主模块下还有子模块,可以在主模块下建立子模块相应的包。说明一点,有的时候如果只有一个主模块,我们完全可以省略掉模

块这一层,就是BaseActivity.java及其子模块直接提至第二层。 在实体模块中,本应该定义且只定义相应的实体类,供全局调用(然而实际情况可能不是这样,后面会说到)。在微盘应用中,几乎所有的实体类是以xxx+info命名的,这种命名也是我赞成的一种命名,从语义上我觉得xxxModel.java这种命名更生动更真实,xxxModel给我一种太机械太死板的感觉,这点完全是个人观点,具体操作中以个人习惯为主。还有一点,在具体的xxxInfo,java中有很多实体类中是没有get/set的方法,而是直接使用public的字段名。这一点,我是推荐这种方式的,特别是在移动开发中,get/set方法很多时候是完全没有必要的,而且是有性能消耗的。当然如果需要对字段设置一定的控制,get/set方法也是可以酌情使用的。 2. 久忆日记 相比于微盘的工程结构,久忆日记的结构稍微复杂了一些。如下图: 1).第一层和前面微盘一样的. 2).第二层则没有模块分类,直接把需要的具体实现类都放在下面,主要日记的一些日记相关的Activity。 3).第二层的实体包命令为model包,里面不仅存放了实体类

最全的Android源码目录结构详解

最全的Android源码目录结构详解 Android 2.1 |-- Makefile |-- bionic (bionic C库) |-- bootable (启动引导相关代码) |-- build (存放系统编译规则及generic等基础开发包配置) |-- cts (Android兼容性测试套件标准) |-- dalvik (dalvik JAVA虚拟机) |-- development (应用程序开发相关) |-- external (android使用的一些开源的模组) |-- frameworks (核心框架——java及C++语言) |-- hardware (部分厂家开源的硬解适配层HAL代码) |-- out (编译完成后的代码输出与此目录) |-- packages (应用程序包) |-- prebuilt (x86和arm架构下预编译的一些资源) |-- sdk (sdk及模拟器) |-- system (底层文件系统库、应用及组件——C语言) `-- vendor (厂商定制代码) bionic 目录 |-- libc (C库) | |-- arch-arm (ARM架构,包含系统调用汇编实现) | |-- arch-x86 (x86架构,包含系统调用汇编实现) | |-- bionic (由C实现的功能,架构无关) | |-- docs (文档) | |-- include (头文件) | |-- inet (?inet相关,具体作用不明) | |-- kernel (Linux内核中的一些头文件) | |-- netbsd (?nesbsd系统相关,具体作用不明) | |-- private (?一些私有的头文件) | |-- stdio (stdio实现) | |-- stdlib (stdlib实现) | |-- string (string函数实现) | |-- tools (几个工具) | |-- tzcode (时区相关代码) | |-- unistd (unistd实现) | `-- zoneinfo (时区信息) |-- libdl (libdl实现,dl是动态链接,提供访问动态链接库的功能)|-- libm (libm数学库的实现,) | |-- alpha (apaha架构) | |-- amd64 (amd64架构) | |-- arm (arm架构) | |-- bsdsrc (?bsd的源码)

android源码分析精典

Android 2.1 源码结构分析 lee Android 2.1 |-- Makefile |-- bionic (bionic C库) |-- bootable (启动引导相关代码) |-- build (存放系统编译规则及generic等基础开发包配置)|-- cts (Android兼容性测试套件标准) |-- dalvik (dalvik JAVA虚拟机) |-- development (应用程序开发相关) |-- external (android使用的一些开源的模组) |-- frameworks (核心框架——java及C++语言) |-- hardware (主要保护硬解适配层HAL代码) |-- out (编译完成后的代码输出与此目录) |-- packages (应用程序包) |-- prebuilt (x86和arm架构下预编译的一些资源) |-- sdk (sdk及模拟器) |-- system (文件系统库、应用及组件——C语言) `-- vendor (厂商定制代码) bionic 目录 |-- libc (C库) | |-- arch-arm (ARM架构,包含系统调用汇编实现) | |-- arch-x86 (x86架构,包含系统调用汇编实现) | |-- bionic (由C实现的功能,架构无关) | |-- docs (文档) | |-- include (头文件) | |-- inet (?inet相关,具体作用不明) | |-- kernel (Linux内核中的一些头文件) | |-- netbsd (?nesbsd系统相关,具体作用不明) | |-- private (?一些私有的头文件) | |-- stdio (stdio实现) | |-- stdlib (stdlib实现) | |-- string (string函数实现) | |-- tools (几个工具) | |-- tzcode (时区相关代码) | |-- unistd (unistd实现) | `-- zoneinfo (时区信息) |-- libdl (libdl实现,dl是动态链接,提供访问动态链接库的功能) |-- libm (libm数学库的实现,) | |-- alpha (apaha架构) | |-- amd64 (amd64架构) | |-- arm (arm架构)

Android Galler2源码分析

图库Gallery2 Gallery2主要功能是实现本地存储器、MTP存储器和网络存储器中媒体(图像和视频)的浏览、显示和更多操作(删除、分享、选择和缩放等)。下面用一张简单的用例图描述了Gallery2的功能和职责。 Gallery 主要是4个页面的跳转: AlbumSetPage.Java(相册缩略图); AlbumPage.java(单个相册照片缩略图); PhotoPage.java(单张照片); SlideShowPage.java(幻灯片界面); 跳转过程: AlbumSetPage.Java→AlbumPage.java→PhotoPage.java SlideShowPage.java是单独的。 这些界面类父类为ActivityState.java;这些界面的切换由StateManager.java负责。 1 界面跳转过程: 在Galley2模块,我们先从程序的入口看起,在androidManifest.xml中注册Application标签(Android 系统会为每个程序运行时创建一个Application的类对象且仅创建一个,他的生命周期等于这个程序的生命周期,它是全局的单实例的,一般做一些全局的初始化操作),应用创建时就会被初始化,维护应用内部全局数据,主要看几个函数:initializeAsyncTask(), GalleryUtils.initialize(this),GalleryUtil是Gallery的工具类,获得了屏幕参数,WindowManager,Resource等

Gallery 从launcher进入Gallery,进入GalleryActivity.ava @Override protected void onCreate(Bundle savedInstanceState) { …... setContentView(https://www.360docs.net/doc/a42924673.html,yout.main); if (savedInstanceState != null) { getStateManager().restoreFromState(savedInstanceState); } else { initializeByIntent(); } } private void initializeByIntent() { Intent intent = getIntent(); String action = intent.getAction(); if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)){ startGetContent(intent); } else if(Intent.ACTION_PICK.equalsIgnoreCase(action)) { // We do NOT really support the PICKintent. Handle it as // the GET_CONTENT. However, we needto translate the type // in the intent here. Log.w(TAG, "action PICK is notsupported"); String type =Utils.ensureNotNull(intent.getType()); if(type.startsWith("vnd.android.cursor.dir/")) { if(type.endsWith("/image")) intent.setType("image/*"); if(type.endsWith("/video")) intent.setType("video/*"); } startGetContent(intent);

Android系统分析

Android操作系统分析 穆英华 基于Linux内核的Android操作系统尽管非常年轻,却已经占领了智能手机9%的市场,而且还在向其它移动平台及嵌入式领域迅速扩张着。下图为2010年一季度的智能手机操作系统占有率统计。在国内外制造商纷纷推出基于Android的产品、Android Market中的应用程序飞速增长的背景下,有必要对Android操作系统的情况进行调研,对比其与Midinux的优劣势、估计其机遇或威胁。本文将从以下几个方面分析Android:目标用户群、支持硬件架构、平台技术架构、应用程序数量、未来发展方向。 一.目标用户群 覆盖高、中、低端智能手机用户是Android的主要目标,尤其是市场上基于ARM11处理器的智能手机已经降到1000元人民币的价格,让低成本的硬件配置也可以运行Android系统,更使得Android从中高端扩展到低端成为可能。除智能手机外,Android已经渗透到了平板电脑、上网本,乃至汽车电子、机顶盒、电子相册、无绳电话等各移动平台和嵌入式产品领域。 对于Android的智能手机和平板电脑用户,Android Market中种类丰富和数量众多的应用程序是其选择Android的重要原因,没有众多可选择的第三方应用程序,操作系统就是再性能优异,也不会对用户有任何杀伤力。这一点类似于苹果的iOS,但总体来说,Android 的用户不会像苹果用户那样忠诚、他们中的很大一部分也不会像苹果用户那样为数码产品花费那么多金钱,可以说Android更大众一些。 二. 支持硬件架构 1.支持的硬件架构 目前Android已经移植到了ARM、X86、M IP S各体系架构中的很多处理器核、处理器芯片、以及更多的板级结构上,对PPC等体系架构的移植也早已开始。如高通、三星等芯片制造商推出的基于ARM C ortex-A8处理器核及P o w er V R显示核心的处理器芯片早已移植Android成功,并被联想的Le Ph one、魅族的M9等许多手机采用,多核的C ortex-A9也蓄势待发。随着

最全的Android源码目录结构详解

最全的Android源码目录结构详解 收藏 转自http: 67."html Android 2."1 |-- Makefile |-- bionic(bionic C库) |-- bootable(启动引导相关代码) |-- build(存放系统编译规则及generic等基础开发包配置)|-- cts(Android 兼容性测试套件标准) |-- dalvik |-- development |-- external |-- frameworks |-- hardware |-- out |-- packages |-- prebuilt |-- sdk |-- system

`-- vendor bionic目录 |-- libc ||-- arch-arm ||-- arch-x86 ||-- bionic ||-- docs ||-- include ||-- kernel ||-- private ||-- stdio ||-- stdlib ||-- string ||-- tools ||-- tzcode ||-- unistd |`-- zoneinfo |-- libdl |-- libm ||-- alpha ||-- amd64(dalvik JAVA虚拟机)

(应用xx相关) (android使用的一些开源的模组) (核心框架——java及C++语言) (部分厂家开源的硬解适配层HAL代码) (编译完成后的代码输出与此目录) (应用程序包) (x86和arm架构下预编译的一些资源) (sdk及模拟器) (底层文件系统库、应用及组件——C语言)(厂商定制代码)(Cxx) ARM架构,包含系统调用汇编实现) x86架构,包含系统调用汇编实现) (由C实现的功能,架构无关) (文档) (头文件) (Linux内核中的一些头文件) (?nesbsd系统相关,具体作用不明) (?一些私有的头文件) (stdio实现) (stdlib实现) (string函数实现)

Android 5.0 Camera系统源码分析(5):Camera预览3A流程

Android 5.0 Camera系统源码分析(5):Camera预览3A流程 1. 前言 本文分析的是Android Hal层的源码,硬件平台基于mt6735。之前几篇讲的预览流程中3A 相关的环节都忽略了,现在重新整理下。 3A指的是Auto Exposure,Auto Focus,Auto White Balance。这三个一起放上来代码实在太多了,这里将重点记录AF的代码。AF的部分工作是由ISP完成的,而ISP的大部分代码mtk都没有开放给我们,比如ISP是如何计算得到对焦位置信息的,但得到对焦位置之后怎么操作对焦马达的代码我们是看得到的,所以涉及到ISP的一些代码将被略过 2. 初始化3A 3A的初始化在DefaultCam1Device的onInit函数里面开始,之前在camera打开流程里面已经提到过 bool DefaultCam1Device:: onInit() { ...... // (1) Open 3A mpHal3a = NS3A::IHal3A::createInstance( NS3A::IHal3A::E_Camera_1, getOpenId(), LOG_TAG); ...... } 构造一个Hal3A对象,看下Hal3A::createInstance的实现 Hal3A* Hal3A:: createInstance(MINT32 i4SensorDevId, MINT32 i4SensorOpenIndex) { switch (i4SensorDevId) { case SENSOR_DEV_MAIN: Hal3ADev::getInstance()->init(i4SensorDevId, i4SensorOpenIndex); return Hal3ADev::getInstance();

最全的Android源码目录结构详解

最全的Android源码目录结构详解收藏 转自https://www.360docs.net/doc/a42924673.html,/a/android/2010/0622/67.html Android 2.1 |-- Makefile |-- bionic (bionic C库) |-- bootable (启动引导相关代码) |-- build (存放系统编译规则及generic等基础开发包配置) |-- cts (Android兼容性测试套件标准) |-- dalvik (dalvik JAVA虚拟机) |-- development (应用程序开发相关) |-- external (android使用的一些开源的模组) |-- frameworks (核心框架——java及C++语言) |-- hardware (部分厂家开源的硬解适配层HAL代码) |-- out (编译完成后的代码输出与此目录) |-- packages (应用程序包) |-- prebuilt (x86和arm架构下预编译的一些资源) |-- sdk (sdk及模拟器) |-- system (底层文件系统库、应用及组件——C语言) `-- vendor (厂商定制代码) bionic 目录 |-- libc (C库) | |-- arch-arm (ARM架构,包含系统调用汇编实现) | |-- arch-x86 (x86架构,包含系统调用汇编实现) | |-- bionic (由C实现的功能,架构无关) | |-- docs (文档) | |-- include (头文件) | |-- inet (?inet相关,具体作用不明) | |-- kernel (Linux内核中的一些头文件) | |-- netbsd (?nesbsd系统相关,具体作用不明) | |-- private (?一些私有的头文件) | |-- stdio (stdio实现) | |-- stdlib (stdlib实现) | |-- string (string函数实现) | |-- tools (几个工具) | |-- tzcode (时区相关代码) | |-- unistd (unistd实现) | `-- zoneinfo (时区信息) |-- libdl (libdl实现,dl是动态链接,提供访问动态链接库的功能)|-- libm (libm数学库的实现,) | |-- alpha (apaha架构) | |-- amd64 (amd64架构)

android天气预报源代码解析

通过google接口在Android中实现天气预报效果 Android可以通过google实现获取指定经纬度位置或者某一个城市的天气信息。如果是根据经纬度查询天气信息,需要对精度为进行转换,例如lat值为31.174165,需要过滤掉小数点,变为31174165传到接口中,维度也一样处理,处理后传 给https://www.360docs.net/doc/a42924673.html,/ig/api?weather=,,,31174165,121433841既可以获取数据。这里要注意一个问题,如果大家获取的经纬度序列很长,直接去掉小数点,有时候也无法获取天气信息,例如40.478224838152528,124.97828006744385,去掉小数点后,传到参数位置,无法获取值,需要大家将经纬度按下面方式转换一下,只取小数点后6位就可以了。int latI = (int) (lat * 1E6); int lonI = (int) (lon * 1E6); 下面的例子演示了根据输入城市,获取该城市的天气预报,Weather.java的61行,是根据经纬度获取天气信息。 工程结构:

Weather.java类 package com.AndroidWeather; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import android.app.Activity; import android.graphics.Bitmap; import android.os.Bundle;

android应用程序源码结构分析

1、src文件夹存放源码。 2、gen下有跟src中一样的包文件,内部有一个名为R.java类,它是自动生成的一个类;该目录不用我们开发人员维护,但又非常重要的目录。该目录用来存放由Android 开发工具所生成的目录。该目录下的所有文件都不是我们创建的,而是由系统自动生成的。这个R,javav文件是只读类型,用户一般式不需要修改的! R.java 文件中默认有attr 、drawable 、layout 、string 等四个静态内部类,每个静态内部类分别对应一种资源,layout 静态内部类对应layout 中的界面文件main.xml,其中每个静态内部类中的静态常量分别定义一条资源标识符,如“ publicstatic final int main =0x7f030000; ” 用一个十六进制的数来代表常量,当开发者在res文件夹下添加或删除任何一个文件或一个属性,R.java都会随之进行更新! 3、android 2.3.3/ 文件夹表明了开发环境的版本,内部存放Android 自身的jar 包。 4、assets/该目录用来存放应用中用到的类似于视频文件、MP3 一些媒体文件。 5、res/ 该目录为资源目录。该目录可以存放一些应用图标、界面文件、应用中用到的文字信息等。 res/目录下有三个dawable 文件夹,区别只是将图标按分辨率高低来放入不同的目录中,drawable-hdpi存放高分辨率的图标,drawable-mdpi存放中等分辨率的图标, drawable-ldpi存放低分辨率的图标。程序运行时可以根据手机分辨率的高低选取相应目录下的图标。 值得注意的是: 如果是老版本的,比如是1.5版本的,res下的drawable文件夹只有一个,如需修改需要手动添加, res/目录下layout/的文件main.xml是布局文件, main.xml < LinearLayout xmlns:android = "https://www.360docs.net/doc/a42924673.html,/apk/res/android" android:orientation = "vertical" android:layout_width = "fill_parent"

Android程序启动过程源码分析

Android应用程序启动过程源代码分析 分类:Android 2011-08-19 00:58 5447人阅读评论(40) 收藏举报 前文简要介绍了Android应用程序的Activity的启动过程。在Android系统中,应用程序是由Activity组成的,因此,应用程序的启动过程实际上就是应用程序中的默认Activity 的启动过程,本文将详细分析应用程序框架层的源代码,了解Android应用程序的启动过程。 在上一篇文章Android应用程序的Activity启动过程简要介绍和学习计划中,我们举例子说明了启动Android应用程序中的Activity的两种情景,其中,在手机屏幕中点击应用程序图标的情景就会引发Android应用程序中的默认Activity的启动,从而把应用程序启动起来。这种启动方式的特点是会启动一个新的进程来加载相应的Activity。这里,我们继续以这个例子为例来说明Android应用程序的启动过程,即MainActivity的启动过程。 MainActivity的启动过程如下图所示:

点击查看大图下面详细分析每一步是如何实现的。 Step 1. Launcher.startActivitySafely

在Android系统中,应用程序是由Launcher启动起来的,其实,Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来。 Launcher的源代码工程在packages/apps/Launcher2目录下,负责启动其它应用程序的源代码实现在src/com/android/launcher2/Launcher.java文件中: view plaincopy to clipboardprint? 1./** 2.* Default launcher application. 3.*/ 4.public final class Launcher extends Activity 5. implements View.OnClickListener, OnLongClickListener, LauncherMod el.Callbacks, AllAppsView.Watcher { 6. 7. ...... 8. 9. /** 10. * Launches the intent referred by the clicked shortcut. 11. * 12. * @param v The view representing the clicked shortcut. 13. */ 14. public void onClick(View v) { 15. Object tag = v.getTag(); 16. if (tag instanceof ShortcutInfo) { 17. // Open shortcut 18. final Intent intent = ((ShortcutInfo) tag).intent; 19. int[] pos = new int[2]; 20. v.getLocationOnScreen(pos); 21. intent.setSourceBounds(new Rect(pos[0], pos[1], 22. pos[0] + v.getWidth(), pos[1] + v.getHeight())); 23. startActivitySafely(intent, tag); 24. } else if (tag instanceof FolderInfo) { 25. ...... 26. } else if (v == mHandleView) {

Android锁屏与解屏相关代码分析

我觉得对于普通人来说,最常见的是Android解屏的界面,然后应该是Home 界面。今天就来分析一下解屏界面的相关代码(以索爱的解锁界面为例)。 首先看解屏的界面,我把解屏的界面分为两个部分,最上部是status Bar,下面是LockScreenSemc,在LockScreenSemc上有一个可供滑动解锁的界面,每个界面对应的类如图所示。

两个类所在包: com.Android.internal.policy.impl.LockScreenSemc.java com.Android.internal.widget.SlidingTabSemc.java SlidingTabSemc.java分析: 分析我们的Touch在SlidingTabSemc 上产生的效果,主要看onTouchEvent ()的处理。 ACTION_DOWN 时会调用disableButtonPress(),产生手指点击时的动画效果。 ACTION_MOVE 时会调用moveControl()产生图片跟随手指移动的效果。 ACTION_UP 时会判断移动的位置是否已经到了,可以触发Trigger的程度,如果到了,则调用dispatchTriggerEvent,通知在SlidingTabSemc上的Trigger Listener。由于SlidingTabSemc上的Trigger Listener是在LockScreenSemc里实现的,所以会代用到LockScreenSemc中。

LockScreenSemc.java分析: 代码中有一行:private SlidingTabSemc mSelector; 将SlidingTabSemc 作为它的一个属性值, 另外此类实现了SlidingTabSemc.OnTriggerListener。这个listener用来执行我们从左到右或者从右到左的滑动,在构造函数中有 mSelector.setOnTriggerListener(this);这样我们在SlidingTabSemc上的滑动会调用到public void onTrigger(View v, int whichHandle)方法中。 onTrigger的执行为两个分支,一个为从右滑向左,执行了mAudioManager.setRingerMode(),设置了声音的模式,并更新右边声音的图片。另一个为从左滑向右,执行的是mCallback.goToUnlockScreen();这个mCallback 是何方神圣呢?它是KeyguardScreenCallback类型,并且在LockScreenSemc初始化的时候传进来的,跟踪后我们发现,LockScreenSemc是类LockPatternKeyguardView的一个属性值(private View mLockScreen);KeyguardScreenCallback的实现在LockPatternKeyguardView中。 LockPatternKeyguardView中LockPatternKeyguardView的逻辑是,如果用户在设置->位置和安全->设置屏幕锁定中设置了解锁画面,则调用了updateScreen(Mode.UnlockScreen)进入相应的解锁画面,否则调用:getCallback().keyguardDone(true); 这个getCallback() 返回的是KeyguardViewMediator。 KeyguardViewMediator 中keyguardDone()方法,最终会给Handler发送KEYGUARD_DONE 的消息,Handler会执行handleKeyguardDone()方法,在方法中会调用handleHide(),这个函数会将我们的LockScreenSemc给隐藏。 总结:总体来说,上面提到的类基本上可以这么认为,SlidingTabSemc属于LockScreenSemc,LockScreenSemc属于LockPatternKeyguardView, LockPatternKeyguardView 属于KeyguardViewManager,KeyguardViewManager 来统管所有与锁解屏相关的操作,另外通过KeyguardViewMediator,来实现KeyguardViewManager和PhoneWindowManager之间的交互与通信,如图所示: 画面的更新实现

相关主题
相关文档
最新文档