当前位置: 编程技术>综合
本页文章导读:
▪Android中G-Sensor流程 1.使G-sensor正常工作需要做的事:
G-sensor driver文件包括:
driver/i2c/chips/lis331dl.c
driver/i2c/chips/sensorioctl.h
include/linux/lis331dl.h
并在/kernel/arch/arm/mach-s3c6410/mach-ur6410.c文件中i2c chanel1的结构变量i2c_de.........
▪java 实现节点链表 public class LinkList<T> {
public class Node{ //定义节点
private T data;
private Node next;
public Node(){
}
public Node(T data,Node next){
this.data=data;
thi.........
▪Linux bonding的初始状态问题以及解决 问题:启动一个bonding网卡,往其里面添加两个根本就没有插着网线的网卡,拉起该bonding后,ifconfig发现其有RUNNING标志,然后将其一个slave插上网线再拔掉,ifconfig就没有RUNNING标志了。分析:.........
[1]Android中G-Sensor流程
来源: 互联网 发布时间: 2013-11-10
1.使G-sensor正常工作需要做的事:
G-sensor driver文件包括:
driver/i2c/chips/lis331dl.c
driver/i2c/chips/sensorioctl.h
include/linux/lis331dl.h
并在/kernel/arch/arm/mach-s3c6410/mach-ur6410.c文件中i2c chanel1的结构变量i2c_devs1[] __initdata中需要添加G-sensor的设备信息,
以使driver成功加载。
同时在该文件中添加一个结构变量
//JayLin add for Gsensor
struct lis331dl_platform_data lisGsensor_platform_data={
.name="lis331dl",
.pin_clk=0,
.pin_data=0,
.open_drain=1,
.interrupt=IRQ_EINT(3),
};
该结构变量在i2c_devs1[] __initdata中被引用。
/kernel/arch/arm/mach-s3c6410/mach-ur6410.c 中需要包含lis331dl.h。
在rootfs/system/etc/init.board.sh的最后一行加上mknod /dev/sensorioctl c 51 201&创建节点供ioctl使用。
编译后的sensor.so放在/rootfs/system/lib/hw下。
sensor.so和driver之间通过ioctl实现对G-sensor的状态控制。ioctl的命令编号定义在头文件sensorioctl.h中,分别放在
kernel/include/linux下
和
androidsourcecode/hardware/libhardware/include/hardware下
供driver和sensor.so使用。
G-sensor driver工作的大致流程:
系统开机后,先加载i2c总线驱动,然后加载设备驱动。
在设备驱动中的init函数中通过调用i2c_add_driver(&lis331dl_i2c_driver)注册i2c_driver;此函数将driver注册到i2c_bus_type的总线上,此总线的匹配规则是利用i2c_client的名称和
i2c_driver中id_table中的名称作匹配。
其中i2c_client是注册板载信息是系统自动创建的,注册板载信息的过程就是在/kernel/arch/arm/mach-s3c6410 /mach-ur6410.c文件中i2c chanel1的结构变量i2c_devs1[] __initdata中需要添加G-sensor的设备信息。
当匹配成功时,i2c_driver中的probe()函数开始执行。
Probe()函数主要完成以下功能:
1.从i2c_client结构中得到初始化信息
2.创建G-sensor的工作队列
2.注册input_device设备
3.读取Chip ID
4.设置寄存器,使能G-sensor
5.设置并启动中断
当G-sensor上报数据的时候会触发中断,然后在中断处理函数中提交一个报值的任务到队列中并禁止中断。
在工作队列中读数G-sensor的数据并上报到input子系统中,最后使能中断。
2.android上层应用apk到G-sensor driver的大致流程:
Android对于Sensor的API定义在 hardware/libhardware/include/hardware/sensor.h中, 要求在sensor.so提供以下8个API函数
[控制方面]
int (*open_data_source)(struct sensors_control_device_t *dev);
int (*activate)(struct sensors_control_device_t *dev, int handle, int enabled);
int (*set_delay)(struct sensors_control_device_t *dev, int32_t ms);
int (*wake)(struct sensors_control_device_t *dev);
[数据方面]
int (*data_open)(struct sensors_data_device_t *dev, int fd);
int (*data_close)(struct sensors_data_device_t *dev);
int (*poll)(struct sensors_data_device_t *dev, sensors_data_t* data);
[模块方面]
int (*get_sensors_list)(struct sensors_module_t* module, struct sensor_t const** list);
在Java层Sensor的状态控制由SensorService来负责,它的java代码和JNI代码分别位于:
frameworks/base/services/java/com/android/server/SensorService.java
frameworks/base/services/jni/com_android_server_SensorService.cpp
在Java层Sensor的数据控制由SensorManager来负责,它的java代码和JNI代码分别位于:
frameworks/base/core/java/android/hardware/SensorManager.java
frameworks/base/core/jni/android_hardware_SensorManager.cpp
android framework中与sensor通信的是sensorService.java和sensorManager.java。
sensorService.java的具体通信是通过JNI调用sensorService.cpp中的方法实现的。
sensorManager.java的具体通信是通过JNI调用sensorManager.cpp中的方法实现的。
sensorService.cpp和sensorManger.cpp通过hardware.c与sensor.so通信。其中sensorService.cpp实现对sensor的状态控制,sensorManger.cpp实现对sensor的数据控制。
sensor.so通过ioctl控制sensor driver的状态,通过打开sensor driver对应的设备文件读取G-sensor采集的数据。
android SDK提供了4个类来于sensor通信,分别为 sensor,sensorEvent,sensorEventListener,sensorManager.其中 sensorEventListener用来在sensorManager中注册需要监听的sensor类型。
sensorManager.java提供registrater(),unregistrater()接口供sensorEventListener使用。
sensorManager.java不断轮询从sensor.so中取数据。取到数据后送给负责监听此类型sensor的 sensorEventListener.java。sensorEventListener.java通过在sensorManager.java中注册可以监听特定类型的sensor传来的数据。
系统启动时执行systemProcess,会启动sensorService.java,在sensorService.java的构造函数中调用JNI方法_sensor_control_init()。
sensorService.cpp中相应的方法android_int()会被执行。该函数会调用hardware.c中的方法hw_get_module()此函数又通过调用load()函数在system/lib/hw下查找sensor.so
查找时会根据harware.c中定义好的sensor.*.so的扩展名的顺序查找,找到第一个匹配的时候即停止,并将该sensor.so中定义好的一个全局变量HAL_MODULE_INFO_SYM带回。该变量包含的一个
重要信息是它的一个成员结构变量中包含的一个函数指针open,该指针所指函数会对一个device结构变量赋值,从而带出sensorService.cpp 和sensorManager.cpp与sensor通信所需要的全部信息。
device结构变量有两种变体分别供sensorService.cpp和sensorManaer.cpp使用。其中主要是一些函数指针指向与sensor通信的函数。
sensorService.cpp和sensorManager.cpp在得到HAL_MODULE_INFO_SYM结构后都会调用 sensors.h的inline函数open()通过HAL_MODULE_INFO_SYM的open函数指针将所需的device信息取回。
系统在启动activityManager.java时,它会启动sensorManager.java,它也会调用hardware.c中的方法hw_get_module()带回HAL_MODULE_INFO_SYM。
3.关于Rotate的实现:
系统启动windowManger.java时,它会启动phoneWindowManager.java,该类有一个内部类myOrientationListener扩展自windowOrientationListener.java。
windowOrientationListener.java是一个辅助类,当device的方向发生变化时,供windowManger.java调用,用来接收数据。
windowOrientationListener.java 内部在sensorManger.java中进行了注册,它回监听G-sensor传来的数据,即x,y,z方向的加速度,收到数据后经过转换处理,若满足Roate条件则调用
IwindowManager接口的实现类windowManagerService.java中的setRotation()方法实现转屏。
SensorManager通过polling的方式从设备得到Sensor数据, Sensor数据的结构定义在sensor.h里,
其中SensorManager只处理了 vector.v, vector.status, time三个域, 分发给已注册的对这些消息的监听者
比如第一项 vector.v包含x,y,z三个方向的信息值,就是由 WindowOrientataionLister注册的,
当 SensorManager获取到这三个值之后,会传递给 WindowOrientataionLister,后者代码位于:
frameworkd/base/core/java/android/view/WindowOrientationListener.java
WindowOrientataionLister接收到这三个值之后,会计算出设备对应的orientation,并且执行 onOrientationChanged函数进一步上传
WindowOrientataionLister是个纯虚类,如果在APK里需要控制方向,可以重载一个实例,
而Android的系统实例对应在 PhoneWindowManager.java里,名字为MyOrientationListener
frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
如果需要旋转, MyOrientationListener则会调用以下代码进行窗口旋转:
mWindowManager.setRotation(rotation, false, mFancyRotationAnimation);
问题总结:
1.将lis302 G-sensor driver从spi总线移植到lis331 i2c总线时遇到的一些问题:
a).lis331用的中断管脚与lis302不同,通过硬件原理图可知lis331用的是GPN3.故需要在driver的probe中设置 writel((readl(S3C64XX_GPNCON) & ~(0xc0)) | (0x80), S3C64XX_GPNCON);
b).通过硬件原理图可知lis331的时钟线和数据线用的是i2c chanel1。故需要在/kernel/arch/arm/mach-s3c6410/mach-ur6410.c文件中i2c chanel1即结构变量i2c_devs1[] __initdata中
添加G-sensor的设备信息,以使driver成功加载。
c).lis331 driver是中断驱动的,每次G-sensor搜集到新数据都会产生中断,driver要在中断中通过i2cbus将数据从G-sensor中取回。由于i2cbus的读写操作是可能休眠的,而中断中不允许调用可能休眠的函数,故通过linux提供的延迟机制work_queue来解决。
问题b)的原理:
i2c驱动包括总线驱动和设备驱动
总线驱动只是提供对一条特定总线的读写机制,本身并不会去做通信。通过i2c总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其细节的与硬件设备通讯。
一个总线驱动通常需要2个模块:struct i2c_adapter和struct i2c_algorithm 定义在include/linux/i2c.h中
struct i2c_algorithm是为了i2c总线驱动和具体的i2c总线能够对话。很多i2c总线驱动定义和使用它们自己的algorithm.对于一些i2c总线驱动来说,很多algorithm已经写好了。
drivers/i2c/buses中包含所有的i2c总线驱动,drivers/i2c/algos中包含了所有的algorithm.
设备驱动通过总线驱动中的读写函数同具体的i2c设备通信,一个设备驱动用两个模块来描述:struct i2c_driver 和struct i2c_client.
i2c_client代表着位
G-sensor driver文件包括:
driver/i2c/chips/lis331dl.c
driver/i2c/chips/sensorioctl.h
include/linux/lis331dl.h
并在/kernel/arch/arm/mach-s3c6410/mach-ur6410.c文件中i2c chanel1的结构变量i2c_devs1[] __initdata中需要添加G-sensor的设备信息,
以使driver成功加载。
同时在该文件中添加一个结构变量
//JayLin add for Gsensor
struct lis331dl_platform_data lisGsensor_platform_data={
.name="lis331dl",
.pin_clk=0,
.pin_data=0,
.open_drain=1,
.interrupt=IRQ_EINT(3),
};
该结构变量在i2c_devs1[] __initdata中被引用。
/kernel/arch/arm/mach-s3c6410/mach-ur6410.c 中需要包含lis331dl.h。
在rootfs/system/etc/init.board.sh的最后一行加上mknod /dev/sensorioctl c 51 201&创建节点供ioctl使用。
编译后的sensor.so放在/rootfs/system/lib/hw下。
sensor.so和driver之间通过ioctl实现对G-sensor的状态控制。ioctl的命令编号定义在头文件sensorioctl.h中,分别放在
kernel/include/linux下
和
androidsourcecode/hardware/libhardware/include/hardware下
供driver和sensor.so使用。
G-sensor driver工作的大致流程:
系统开机后,先加载i2c总线驱动,然后加载设备驱动。
在设备驱动中的init函数中通过调用i2c_add_driver(&lis331dl_i2c_driver)注册i2c_driver;此函数将driver注册到i2c_bus_type的总线上,此总线的匹配规则是利用i2c_client的名称和
i2c_driver中id_table中的名称作匹配。
其中i2c_client是注册板载信息是系统自动创建的,注册板载信息的过程就是在/kernel/arch/arm/mach-s3c6410 /mach-ur6410.c文件中i2c chanel1的结构变量i2c_devs1[] __initdata中需要添加G-sensor的设备信息。
当匹配成功时,i2c_driver中的probe()函数开始执行。
Probe()函数主要完成以下功能:
1.从i2c_client结构中得到初始化信息
2.创建G-sensor的工作队列
2.注册input_device设备
3.读取Chip ID
4.设置寄存器,使能G-sensor
5.设置并启动中断
当G-sensor上报数据的时候会触发中断,然后在中断处理函数中提交一个报值的任务到队列中并禁止中断。
在工作队列中读数G-sensor的数据并上报到input子系统中,最后使能中断。
2.android上层应用apk到G-sensor driver的大致流程:
Android对于Sensor的API定义在 hardware/libhardware/include/hardware/sensor.h中, 要求在sensor.so提供以下8个API函数
[控制方面]
int (*open_data_source)(struct sensors_control_device_t *dev);
int (*activate)(struct sensors_control_device_t *dev, int handle, int enabled);
int (*set_delay)(struct sensors_control_device_t *dev, int32_t ms);
int (*wake)(struct sensors_control_device_t *dev);
[数据方面]
int (*data_open)(struct sensors_data_device_t *dev, int fd);
int (*data_close)(struct sensors_data_device_t *dev);
int (*poll)(struct sensors_data_device_t *dev, sensors_data_t* data);
[模块方面]
int (*get_sensors_list)(struct sensors_module_t* module, struct sensor_t const** list);
在Java层Sensor的状态控制由SensorService来负责,它的java代码和JNI代码分别位于:
frameworks/base/services/java/com/android/server/SensorService.java
frameworks/base/services/jni/com_android_server_SensorService.cpp
在Java层Sensor的数据控制由SensorManager来负责,它的java代码和JNI代码分别位于:
frameworks/base/core/java/android/hardware/SensorManager.java
frameworks/base/core/jni/android_hardware_SensorManager.cpp
android framework中与sensor通信的是sensorService.java和sensorManager.java。
sensorService.java的具体通信是通过JNI调用sensorService.cpp中的方法实现的。
sensorManager.java的具体通信是通过JNI调用sensorManager.cpp中的方法实现的。
sensorService.cpp和sensorManger.cpp通过hardware.c与sensor.so通信。其中sensorService.cpp实现对sensor的状态控制,sensorManger.cpp实现对sensor的数据控制。
sensor.so通过ioctl控制sensor driver的状态,通过打开sensor driver对应的设备文件读取G-sensor采集的数据。
android SDK提供了4个类来于sensor通信,分别为 sensor,sensorEvent,sensorEventListener,sensorManager.其中 sensorEventListener用来在sensorManager中注册需要监听的sensor类型。
sensorManager.java提供registrater(),unregistrater()接口供sensorEventListener使用。
sensorManager.java不断轮询从sensor.so中取数据。取到数据后送给负责监听此类型sensor的 sensorEventListener.java。sensorEventListener.java通过在sensorManager.java中注册可以监听特定类型的sensor传来的数据。
系统启动时执行systemProcess,会启动sensorService.java,在sensorService.java的构造函数中调用JNI方法_sensor_control_init()。
sensorService.cpp中相应的方法android_int()会被执行。该函数会调用hardware.c中的方法hw_get_module()此函数又通过调用load()函数在system/lib/hw下查找sensor.so
查找时会根据harware.c中定义好的sensor.*.so的扩展名的顺序查找,找到第一个匹配的时候即停止,并将该sensor.so中定义好的一个全局变量HAL_MODULE_INFO_SYM带回。该变量包含的一个
重要信息是它的一个成员结构变量中包含的一个函数指针open,该指针所指函数会对一个device结构变量赋值,从而带出sensorService.cpp 和sensorManager.cpp与sensor通信所需要的全部信息。
device结构变量有两种变体分别供sensorService.cpp和sensorManaer.cpp使用。其中主要是一些函数指针指向与sensor通信的函数。
sensorService.cpp和sensorManager.cpp在得到HAL_MODULE_INFO_SYM结构后都会调用 sensors.h的inline函数open()通过HAL_MODULE_INFO_SYM的open函数指针将所需的device信息取回。
系统在启动activityManager.java时,它会启动sensorManager.java,它也会调用hardware.c中的方法hw_get_module()带回HAL_MODULE_INFO_SYM。
3.关于Rotate的实现:
系统启动windowManger.java时,它会启动phoneWindowManager.java,该类有一个内部类myOrientationListener扩展自windowOrientationListener.java。
windowOrientationListener.java是一个辅助类,当device的方向发生变化时,供windowManger.java调用,用来接收数据。
windowOrientationListener.java 内部在sensorManger.java中进行了注册,它回监听G-sensor传来的数据,即x,y,z方向的加速度,收到数据后经过转换处理,若满足Roate条件则调用
IwindowManager接口的实现类windowManagerService.java中的setRotation()方法实现转屏。
SensorManager通过polling的方式从设备得到Sensor数据, Sensor数据的结构定义在sensor.h里,
其中SensorManager只处理了 vector.v, vector.status, time三个域, 分发给已注册的对这些消息的监听者
比如第一项 vector.v包含x,y,z三个方向的信息值,就是由 WindowOrientataionLister注册的,
当 SensorManager获取到这三个值之后,会传递给 WindowOrientataionLister,后者代码位于:
frameworkd/base/core/java/android/view/WindowOrientationListener.java
WindowOrientataionLister接收到这三个值之后,会计算出设备对应的orientation,并且执行 onOrientationChanged函数进一步上传
WindowOrientataionLister是个纯虚类,如果在APK里需要控制方向,可以重载一个实例,
而Android的系统实例对应在 PhoneWindowManager.java里,名字为MyOrientationListener
frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
如果需要旋转, MyOrientationListener则会调用以下代码进行窗口旋转:
mWindowManager.setRotation(rotation, false, mFancyRotationAnimation);
问题总结:
1.将lis302 G-sensor driver从spi总线移植到lis331 i2c总线时遇到的一些问题:
a).lis331用的中断管脚与lis302不同,通过硬件原理图可知lis331用的是GPN3.故需要在driver的probe中设置 writel((readl(S3C64XX_GPNCON) & ~(0xc0)) | (0x80), S3C64XX_GPNCON);
b).通过硬件原理图可知lis331的时钟线和数据线用的是i2c chanel1。故需要在/kernel/arch/arm/mach-s3c6410/mach-ur6410.c文件中i2c chanel1即结构变量i2c_devs1[] __initdata中
添加G-sensor的设备信息,以使driver成功加载。
c).lis331 driver是中断驱动的,每次G-sensor搜集到新数据都会产生中断,driver要在中断中通过i2cbus将数据从G-sensor中取回。由于i2cbus的读写操作是可能休眠的,而中断中不允许调用可能休眠的函数,故通过linux提供的延迟机制work_queue来解决。
问题b)的原理:
i2c驱动包括总线驱动和设备驱动
总线驱动只是提供对一条特定总线的读写机制,本身并不会去做通信。通过i2c总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其细节的与硬件设备通讯。
一个总线驱动通常需要2个模块:struct i2c_adapter和struct i2c_algorithm 定义在include/linux/i2c.h中
struct i2c_algorithm是为了i2c总线驱动和具体的i2c总线能够对话。很多i2c总线驱动定义和使用它们自己的algorithm.对于一些i2c总线驱动来说,很多algorithm已经写好了。
drivers/i2c/buses中包含所有的i2c总线驱动,drivers/i2c/algos中包含了所有的algorithm.
设备驱动通过总线驱动中的读写函数同具体的i2c设备通信,一个设备驱动用两个模块来描述:struct i2c_driver 和struct i2c_client.
i2c_client代表着位
[2]java 实现节点链表
来源: 互联网 发布时间: 2013-11-10
public class LinkList<T> { public class Node{ //定义节点 private T data; private Node next; public Node(){ } public Node(T data,Node next){ this.data=data; this.next=next; } } private Node header;//头节点 private Node tail;//尾节点 private int size;//链表大小 //构造函数初始化数据 public LinkList(){ header=null; tail=null; } public LinkList(T element){ header=new Node(element,null); tail=header; size++; } //链表长度 public int length(){ return size; } //返回指定位置index的节点 public T get(int index){ return getNodeByIndex(index).data; } private Node getNodeByIndex(int index) { if(index<0||index>size-1){ throw new IndexOutOfBoundsException("线性表索引越界"); } Node current=header; for(int i=0;i<size&¤t!=null;i++,current=current.next){ if(i==index){ return current; } } return null; } //返回element在链表的位置,-1表示不存在 public int locate(T element){ Node current=header; for(int i=0;i<size&¤t!=null;i++,current=current.next){ if(current.data==element){ return i; } } return -1; } //在index位置插入节点element public void insert(T element,int index){ if(index<0||index>size){ throw new IndexOutOfBoundsException("线性表索引越界"); } if(header==null){ add(element); }else{ if(index==0){ addAtHeader(element); }else{ Node prev=getNodeByIndex(index-1); prev.next=new Node(element,prev.next); size++; } } } //采用尾插法为链表添加新节点 private void add(T element) { // TODO Auto-generated method stub if(header==null){ header=new Node(element,null); tail=header; }else{ Node newNode=new Node(element,null); tail.next=newNode; tail=newNode; } size++; } //采用头插法为链表添加新节点 private void addAtHeader(T element){ header=new Node(element,header); if(tail==null){ tail=header; } size++; } //删除index位置的节点 public T delete(int index){ if(index<0||index>size-1){ throw new IndexOutOfBoundsException("线性表索引越界"); } Node del=null; if(index==0){ del=header; header=header.next; }else{ Node prev=getNodeByIndex(index-1); del=prev.next; prev.next=del.next; del.next=null; } size--; return del.data; } //从链表后面删除一个节点 public T remove(){ return delete(size-1); } //是否为空 public boolean empty(){ return size==0; } //清空链表 public void clear(){ header=null; tail=null; size=0; } public String toString(){ if(empty()){ return "[]"; }else{ StringBuilder sb=new StringBuilder("["); for(Node current=header;current!=null;current=current.next){ sb.append(current.data.toString()+", "); } int len=sb.length(); return sb.delete(len-2, len).append("]").toString(); } } public static void main(String[] args) { LinkList<String> list=new LinkList<String>(); list.insert("aaa", 0); list.add("bbb"); list.add("ccc"); System.out.println(list.toString()); list.insert("ddd", 1); System.out.println(list.toString()); list.delete(2); System.out.println(list.toString()); System.out.println("ccc在链表中的位置:"+list.locate("ccc")); System.out.println("链表中索引2处的元素:"+list.get(2)); } }
作者:caiwenfeng_for_23 发表于2013-1-12 13:46:07 原文链接
阅读:34 评论:0 查看评论
[3]Linux bonding的初始状态问题以及解决
来源: 互联网 发布时间: 2013-11-10
问题:启动一个bonding网卡,往其里面添加两个根本就没有插着网线的网卡,拉起该bonding后,ifconfig发现其有RUNNING标志,然后将其一个slave插上网线再拔掉,ifconfig就没有RUNNING标志了。
分析:这个问题实际上无伤大雅,只是在第一次欺骗一下OS而已,然而却会影响到keepalived的track_interfaces配置,进而影响基于VRRP的热备切换,导致拥有没有插着网线的机器的热备组中的若干台机器一旦重启,其热备状态就会混乱。没有插线的机器无论如何也不能是MASTER,可是keepalived看到了bonding网卡的RUNNING标志,误认为其已经可以使用,进而可能成为MASTER状态。
解析与修正:如果仅仅为了短平快的解决当下问题,那么最简单不过的就是将keepalived中基于RUNNING的判断换成基于LOWER_UP的判断,LOWER_UP就是标识网卡有没有插线的(但不绝对,还可能受别的因素影响,但是大多数情况-显然并非全部情况下可以这么认为),事实证明这是完全可以的,问题解决了。但是却违反了track_interfaces的初衷,因此这种改法不好!
在彻底修正这个问题之前还是需要了解网卡的各种状态以及层次。总体来讲,网卡state分为管理state和操作state。
管理state:这个状态是自上而下配置的,表示管理员的意愿
操作state:这个state是网卡自身现状,表示网卡目前是否已经准备好并且有能力为用户服务。
现在看看Linux系统中网卡的各种state:
IFF_LOWER_UP-线缆已经接好且上电
IFF_RUNNING-操作state是UP(那么什么时候操作state会是DOWN呢?1.在管理state为DOWN的时候,即管理员用命令down了网卡;2.网卡没有插线
理解了这些状态之后,即使没有keepalived,状态也不会,这就不是热备切换的问题了,keepalived并没有错,即便是修正了keepalived,那么bonding还是会影响到其它使用它的程序的...正是bonding驱动的bug导致了状态判断错误。
bond_open的返回值是0,因此bonding网卡默认就是START状态的,因为在物理网卡enslave进bonding网卡的时候需要bonding网卡是IF_UP状态的。可以在bond_enslave的最后看到一个频繁调用的函数:bond_set_carrier。该函数判断bonding网卡的所有slave的状态,如果全部为DOWN,则将bonding本身也设置成DOWN。这应该是一个周期调用的函数,调用周期取决于bonding的miimon参数,另外在几个关键点也会调用bond_set_carrier,比如在新的slave被bongding的时候,即调用bond_enslave的时候。基本上bond_set_carrier的逻辑是这样的:
没有任何问题。在周期性检测中,会设置所有slave的状态:
到底哪里出了问题??为何一块物理网卡明明是没有插线的,却没有调到bond_set_carrier最后面的netif_carrier_off,原因很显然了,因为netif_carrier_ok判断没有通过,为何没有通过,原因在于bonding自创建之日,其state根本就没有涉及__LINK_STATE_NOCARRIER这个bit的初始化!什么?这等错误竟然在内核里面出现!改了它便是,在bond_open的最后,return 0之前,加上一句:
即可修正这个错误。另外要修改的是bond_set_carrier函数:
关于这个修改,我只是为了保险起见,实质上并无必要,到底有没有必要进行这个修改,我并没有实测过,话说只要初始状态正确,后面的只是按照既有的逻辑来过,不可能有任何问题,话虽如此,可我还是不信任kernel社区的这帮人,要不然怎么会忘记初始化carrier状态呢?
插曲:这个问题是我项目中遇到的,因为要赶工期,领导决定先把这个问题放一下,抓紧主要问题解决,我因此也和领导有了一次冲突,因为我觉得做一个产品,真的就需要完美无缺,不能有遗留问题等到用户真的需要的时候再解决,宁可延期也要完美,这是我的逻辑,至于说我为何没有把这事情分发下去,是因为到了产品发布的最后关头,正如领导说的,谁熟悉什么谁做什么,我自认为在项目组对网络,对kernel比较熟悉,因此这个问题只有我能快速解决,如果说我因为解决这个问题而耽误了项目进度,那是我的错,因此我选择在工作时间外加班解决它,这是我回到家里搞定的,也只能这样才能保证产品的按期发布。关键问题是,到底应该以产品发布的时间点为准呢还是应该以产品的完美程度为准。我选择后者,你们呢?
分析:这个问题实际上无伤大雅,只是在第一次欺骗一下OS而已,然而却会影响到keepalived的track_interfaces配置,进而影响基于VRRP的热备切换,导致拥有没有插着网线的机器的热备组中的若干台机器一旦重启,其热备状态就会混乱。没有插线的机器无论如何也不能是MASTER,可是keepalived看到了bonding网卡的RUNNING标志,误认为其已经可以使用,进而可能成为MASTER状态。
解析与修正:如果仅仅为了短平快的解决当下问题,那么最简单不过的就是将keepalived中基于RUNNING的判断换成基于LOWER_UP的判断,LOWER_UP就是标识网卡有没有插线的(但不绝对,还可能受别的因素影响,但是大多数情况-显然并非全部情况下可以这么认为),事实证明这是完全可以的,问题解决了。但是却违反了track_interfaces的初衷,因此这种改法不好!
在彻底修正这个问题之前还是需要了解网卡的各种状态以及层次。总体来讲,网卡state分为管理state和操作state。
管理state:这个状态是自上而下配置的,表示管理员的意愿
操作state:这个state是网卡自身现状,表示网卡目前是否已经准备好并且有能力为用户服务。
现在看看Linux系统中网卡的各种state:
IFF_LOWER_UP-线缆已经接好且上电
IFF_RUNNING-操作state是UP(那么什么时候操作state会是DOWN呢?1.在管理state为DOWN的时候,即管理员用命令down了网卡;2.网卡没有插线
理解了这些状态之后,即使没有keepalived,状态也不会,这就不是热备切换的问题了,keepalived并没有错,即便是修正了keepalived,那么bonding还是会影响到其它使用它的程序的...正是bonding驱动的bug导致了状态判断错误。
bond_open的返回值是0,因此bonding网卡默认就是START状态的,因为在物理网卡enslave进bonding网卡的时候需要bonding网卡是IF_UP状态的。可以在bond_enslave的最后看到一个频繁调用的函数:bond_set_carrier。该函数判断bonding网卡的所有slave的状态,如果全部为DOWN,则将bonding本身也设置成DOWN。这应该是一个周期调用的函数,调用周期取决于bonding的miimon参数,另外在几个关键点也会调用bond_set_carrier,比如在新的slave被bongding的时候,即调用bond_enslave的时候。基本上bond_set_carrier的逻辑是这样的:
static int bond_set_carrier(struct bonding *bond) { struct slave *slave; int i; if (bond->slave_cnt == 0) goto down; if (bond->params.mode == BOND_MODE_8023AD) return bond_3ad_set_carrier(bond); //遍历所有的slave,只要有一个UP,那么bonding就UP bond_for_each_slave(bond, slave, i) { if (slave->link == BOND_LINK_UP) { if (!netif_carrier_ok(bond->dev)) { netif_carrier_on(bond->dev); return 1; } return 0; } } down: if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); return 1; } return 0; }
没有任何问题。在周期性检测中,会设置所有slave的状态:
bond_for_each_slave(bond, slave, i) { slave->new_link = BOND_LINK_NOCHANGE; link_state = bond_check_dev_link(bond, slave->dev, 0); switch (slave->link) { case BOND_LINK_UP: if (link_state) continue; //将状态设置为FAIL,此后再调用bond_set_carrier时可能会off掉bonding网卡 slave->link = BOND_LINK_FAIL; ...
到底哪里出了问题??为何一块物理网卡明明是没有插线的,却没有调到bond_set_carrier最后面的netif_carrier_off,原因很显然了,因为netif_carrier_ok判断没有通过,为何没有通过,原因在于bonding自创建之日,其state根本就没有涉及__LINK_STATE_NOCARRIER这个bit的初始化!什么?这等错误竟然在内核里面出现!改了它便是,在bond_open的最后,return 0之前,加上一句:
netif_carrier_on(bond->dev);
即可修正这个错误。另外要修改的是bond_set_carrier函数:
if (bond->slave_cnt == 0) goto down; 如果slave_cnt为0,那么就要调用netif_carrier_on将bonding拉起来,以便于后面往其加入新的slave,那么上述语句改为: if (bond->slave_cnt == 0) { netif_carrier_on(bond->dev) goto down; }
关于这个修改,我只是为了保险起见,实质上并无必要,到底有没有必要进行这个修改,我并没有实测过,话说只要初始状态正确,后面的只是按照既有的逻辑来过,不可能有任何问题,话虽如此,可我还是不信任kernel社区的这帮人,要不然怎么会忘记初始化carrier状态呢?
插曲:这个问题是我项目中遇到的,因为要赶工期,领导决定先把这个问题放一下,抓紧主要问题解决,我因此也和领导有了一次冲突,因为我觉得做一个产品,真的就需要完美无缺,不能有遗留问题等到用户真的需要的时候再解决,宁可延期也要完美,这是我的逻辑,至于说我为何没有把这事情分发下去,是因为到了产品发布的最后关头,正如领导说的,谁熟悉什么谁做什么,我自认为在项目组对网络,对kernel比较熟悉,因此这个问题只有我能快速解决,如果说我因为解决这个问题而耽误了项目进度,那是我的错,因此我选择在工作时间外加班解决它,这是我回到家里搞定的,也只能这样才能保证产品的按期发布。关键问题是,到底应该以产品发布的时间点为准呢还是应该以产品的完美程度为准。我选择后者,你们呢?
作者:dog250 发表于2013-1-12 13:35:22 原文链接
阅读:41 评论:0 查看评论
最新技术文章: