Look into it ~

present
随笔 - 32, 文章 - 0, 评论 - 3, 引用 - 0
数据加载中……

2008年11月12日

android软件

    只有注册用户登录后才能阅读该文。阅读全文

posted @ 2008-11-20 16:24 LukeW 阅读(33) | 评论 (0)编辑 收藏

linux设备模型

Linux 2.6内核的一个重要特色是提供了统一的内核设备模型。随着技术的不断进步,系统的拓扑结构越来越复杂,对智能电源管理、热插拔以及plug and play的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,2.6内核开发了全新的设备模型。
1. Sysfs文件系统
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。其顶层目录主要有:
Block目录:包含所有的块设备
Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构
Bus目录:包含系统中所有的总线类型
Drivers目录:包括内核中所有已注册的设备驱动程序
Class目录:系统中的设备类型(如网卡设备,声卡设备等)
2. 内核对象机制关键数据结构
2.1 kobject内核对象
Kobject 是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux 2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。
Kobject结构定义为:
struct kobject {
char * k_name;    // 指向设备名称的指针
char name[KOBJ_NAME_LEN];   // 设备名称
struct kref kref;    // 对象引用计数
struct list_head entry;   // 挂接到所在kset中去的单元
struct kobject * parent; // 指向父对象的指针
struct kset * kset;    // 所属kset的指针
struct kobj_type * ktype;   // 指向其对象类型描述符的指针
struct dentry * dentry; // sysfs文件系统中与该对象对应的文件节点路径指针
};

其中的kref域表示该对象引用的计数,内核通过kref实现对象引用计数管理,内核提供两个函数kobject_get()、kobject_put()分别用于增加和减少引用计数,当引用计数为0时,所有该对象使用的资源将被释放。
Ktype 域是一个指向kobj_type结构的指针,表示该对象的类型。Kobj_type数据结构包含三个域:一个release方法用于释放kobject占 用的资源;一个sysfs_ops指针指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两个函数store()和 show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态 传入的属性值。
2.2 kset内核对象集合
Kobject通常通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合,在内核中用kset数据结构表示,定义为:
struct kset {
struct subsystem * subsys;   // 所在的subsystem的指针
struct kobj_type * ktype;   // 指向该kset对象类型描述符的指针
struct list_head list;      // 用于连接该kset中所有kobject的链表头
struct kobject kobj;    // 嵌入的kobject
struct kset_hotplug_ops * hotplug_ops; // 指向热插拔操作表的指针
};

包 含在kset中的所有kobject被组织成一个双向循环链表,list域正是该链表的头。Ktype域指向一个kobj_type结构,被该 kset中的所有kobject共享,表示这些对象的类型。Kset数据结构还内嵌了一个kobject对象(由kobj域表示),所有属于这个kset 的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的 kobject对象的引用计数。
2.3 subsystem内核对象子系统
Subsystem是一系列kset的集合,描述系统中某一 类设备子系统,如block_subsys表示所有的块设备,对应于sysfs文件系统中的block目录。类似的,devices_subsys对应于 sysfs中的devices目录,描述系统中所有的设备。Subsystem由struct subsystem数据结构描述,定义为:
struct subsystem {
struct kset kset;       // 内嵌的kset对象
struct rw_semaphore rwsem; // 互斥访问信号量
};

每 个kset必须属于某个subsystem,通过设置kset结构中的subsys域指向指定的subsystem可以将一个kset加入到该 subsystem。所有挂接到同一subsystem的kset共享同一个rwsem信号量,用于同步访问kset中的链表。

3. 内核对象机制主要相关函数
针对内核对象不同层次的数据结构,linux 2.6内核定义了一系列操作函数,定义于lib/kobject.c文件中。
3.1 kobject相关函数
void kobject_init(struct kobject * kobj);// kobject初始化函数。设置kobject引用计数为1,entry域指向自身,其所属kset引用计数加1

int kobject_set_name(struct kobject *kobj, const char *format, );// 设置指定kobject的名称。

void kobject_cleanup(struct kobject * kobj);
void kobject_release(struct kref *kref);// kobject清除函数。当其引用计数为0时,释放对象占用的资源。

struct kobject *kobject_get(struct kobject *kobj);// 将kobj 对象的引用计数加1,同时返回该对象的指针。

void kobject_put(struct kobject * kobj);// 将kobj对象的引用计数减1,如果引用计数降为0,则调用kobject_release()释放该kobject对象。

int kobject_add(struct kobject * kobj);// 将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数。

int kobject_register(struct kobject * kobj);// kobject注册函数。通过调用kobject_init()初始化kobj,再调用kobject_add()完成该内核对象的注册。

void kobject_del(struct kobject * kobj);// 从Linux设备层次(hierarchy)中删除kobj对象。

void kobject_unregister(struct kobject * kobj);// kobject注销函数。与kobject_register()相反,它首先调用kobject_del从设备层次中删除该对象,再调用kobject_put()减少该对象的引用计数,如果引用计数降为0,则释放该kobject对象。

3.2 kset相关函数
与kobject 相似,kset_init()完成指定kset的初始化,kset_get()和kset_put()分别增加和减少kset对象的引用计数。 Kset_add()和kset_del()函数分别实现将指定keset对象加入设备层次和从其中删除;kset_register()函数完成 kset的注册而kset_unregister()函数则完成kset的注销。
3.3 subsystem相关函数
subsystem有一组完成类似的函数,分别是:
void subsystem_init(struct subsystem *subsys);
int subsystem_register(struct subsystem *subsys);
void subsystem_unregister(struct subsystem *subsys);
struct subsystem *subsys_get(struct subsystem *subsys)
void subsys_put(struct subsystem *subsys);

4. 设备模型组件
在上述内核对象机制的基础上,Linux的设备模型建立在几个关键组件的基础上,下面我们详细阐述这些组件。
4.1 devices
系统中的任一设备在设备模型中都由一个device对象描述,其对应的数据结构struct device定义为:
struct device {
struct list_head g_list;
struct list_head node;
    
struct list_head bus_list;
    
struct list_head driver_list;
    
struct list_head children;
    
struct device *parent;
    
struct kobject kobj;
    
char bus_id[BUS_ID_SIZE];
    
struct bus_type *bus;
    
struct device_driver *driver;
    
void *driver_data;
    
/* Several fields omitted */
};

g_list 将该device对象挂接到全局设备链表中,所有的device对象都包含在devices_subsys中,并组织成层次结构。Node域将该对象挂接 到其兄弟对象的链表中,而bus_list则用于将连接到相同总线上的设备组织成链表,driver_list则将同一驱动程序管理的所有设备组织为链 表。此外,children域指向该device对象子对象链表头,parent域则指向父对象。Device对象还内嵌一个kobject对象,用于引 用计数管理并通过它实现设备层次结构。Driver域指向管理该设备的驱动程序对象,而driver_data则是提供给驱动程序的数据。Bus域描述设 备所连接的总线类型。
内核提供了相应的函数用于操作device对象。其中Device_register()函数将一个新的device对象插 入设备模型,并自动在/sys/devices下创建一个对应的目录。Device_unregister()完成相反的操作,注销设备对象。 Get_device()和put_device()分别增加与减少设备对象的引用计数。通常device结构不单独使用,而是包含在更大的结构中作为一 个子结构使用,比如描述PCI设备的struct pci_dev,其中的dev域就是一个device对象。
4.2 drivers
系统中的每个驱动程序由一个device_driver对象描述,对应的数据结构定义为:
struct device_driver {
    
char *name;   // 设备驱动程序的名称
    struct bus_type *bus; // 该驱动所管理的设备挂接的总线类型
    struct kobject kobj;    // 内嵌kobject对象
    struct list_head devices;  // 该驱动所管理的设备链表头
    int (*probe)(struct device *dev); // 指向设备探测函数,用于探测设备是否可以被该驱动程序管理
int (*remove)(struct device *dev); // 用于删除设备的函数
/*
 some fields omitted*/
};

与device 结构类似,device_driver对象依靠内嵌的kobject对象实现引用计数管理和层次结构组织。内核提供类似的函数用于操作 device_driver对象,如get_driver()增加引用计数,driver_register()用于向设备模型插入新的driver对 象,同时在sysfs文件系统中创建对应的目录。Device_driver()结构还包括几个函数,用于处理热拔插、即插即用和电源管理事件。
4.3   buses
系统中总线由struct bus_type描述,定义为:
struct bus_type {
char   * name; // 总线类型的名称
struct subsystem subsys; // 与该总线相关的subsystem
struct kset drivers; // 所有与该总线相关的驱动程序集合
struct kset devices; // 所有挂接在该总线上的设备集合
struct bus_attribute * bus_attrs; // 总线属性
struct device_attribute * dev_attrs; // 设备属性
struct driver_attribute * drv_attrs;   // 驱动程序属性
int (*match)(struct device * dev, struct device_driver * drv);
int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
int (*suspend)(struct device * dev, u32 state);
int (*resume)(struct device * dev);
};

每 个bus_type对象都内嵌一个subsystem对象,bus_subsys对象管理系统中所有总线类型的subsystem对象。每个 bus_type对象都对应/sys/bus目录下的一个子目录,如PCI总线类型对应于/sys/bus/pci。在每个这样的目录下都存在两个子目 录:devices和drivers(分别对应于bus_type结构中的devices和drivers域)。其中devices子目录描述连接在该总 线上的所有设备,而drivers目录则描述与该总线关联的所有驱动程序。与device_driver对象类似,bus_type结构还包含几个函数 (match()、hotplug()等)处理相应的热插拔、即插即拔和电源管理事件。
4.4 classes
系统中的设备类由 struct class描述,表示某一类设备。所有的class对象都属于class_subsys子系统,对应于sysfs文件系统中的/sys/class目录。 每个class对象包括一个class_device链表,每个class_device对象表示一个逻辑设备,并通过struct class_device中的dev域(一个指向struct device的指针)关联一个物理设备。这样,一个逻辑设备总是对应于一个物理设备,但是一个物理设备却可能对应于多个逻辑设备。此外,class结构中 还包括用于处理热插拔、即插即拔和电源管理事件的函数,这与device对象和driver对象相似。

posted @ 2008-11-12 23:14 LukeW 阅读(177) | 评论 (0)编辑 收藏

位运算

C中的位运算
能够运用到任何整形的数据类型上(包括char, int), 无论有没有short, long, unsigned这样的限定词.


位运算的应用
// 交换指针变量x,y所指向的存储位置处存放的值
// 优势是不需要第三个位置来临时存储另一个值
// 但是这个方法并没有明显的性能优势,只是一个智力上的消遣
void inplace_swap(int *x, int *y)
{
 
*= *^ *y;
 
*= *^ *y;
 
*= *^ *y;
}

位运算常见用法:
实现掩码运算



-----------------------------------
Java中的位运算


posted @ 2008-11-12 13:53 LukeW 阅读(122) | 评论 (0)编辑 收藏

大端小端 -- 各系统及机器的信息表示

因为现行的计算机都是以八位一个字节为存储单位,那么一个16位的整数,也就是C语言中的short,在内存中可能有两种存储顺序big-

endian和litte-endian.考虑一个short整数0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可

能有如下两种情况:
大端字节(Big-endian):

short变量地址
       0x1000                  0x1001
___________________________________
|                 |
|         0x31    |       0x32
|________________ | ________________
高位字节在低位字节的前面,也就是高位在内存地址低的一端.可以这样记住(大端->高位->在前->正常的逻辑顺序)
 
小端字节(little-endian):

short变量地址
       0x1000                  0x1001
_____________________________________
|                 |
|         0x32    |       0x31
|________________ | __________________
低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位->在前->与正常逻辑顺序相反)
 
可以做个实验
在windows上下如下程序
#include <stdio.h>
#include 
<assert.h>
 
void main( void )
{
        
short test;
        FILE
* fp;
        
        test 
= 0x3132;  //(31ASIIC码的’1’,32ASIIC码的’2’)
        if ((fp = fopen ("c:""test.txt""wb")) == NULL)
              assert(
0);
        fwrite(
&test, sizeof(short), 1, fp);
        fclose(fp);
}

    然后在C盘下打开test.txt文件,可以看见内容是21,而test等于0x3132,可以明显的看出来x86的字节顺序是低位在前.如果我们
把这段同样的代码放到(big-endian)的机器上执行,那么打出来的文件就是12.这在本机中使用是没有问题的.但当你把这个文件从一
个big- endian机器复制到一个little-endian机器上时就出现问题了.

    如上述例子,我们在big-endian的机器上创建了这个test文件,把其复制到little-endian的机器上再用fread读到一个 short里
面,我们得到的就不再是0x3132而是0x3231了,这样读到的数据就是错误的,所以在两个字节顺序不一样的机器上传输数据时需要特别
小心字节顺序,理解了字节顺序在可以帮助我们写出移植行更高的代码.

正因为有字节顺序的差别,所以在网络传输的时候定义了所有字节顺序相关的数据都使用big-endian,BSD的代码中定义了四个宏来处
理:
#define ntohs(n)     //网络字节顺序到主机字节顺序 n代表net, h代表host, s代表short
#define htons(n)     //主机字节顺序到网络字节顺序 n代表net, h代表host, s代表short
#define ntohl(n)      //网络字节顺序到主机字节顺序 n代表net, h代表host, s代表 long
#define htonl(n)      //主机字节顺序到网络字节顺序 n代表net, h代表host, s代表 long

举例说明下这其中一个宏的实现:
 #define sw16(x) "
    ((
short)( "
        (((short)(x) & (short)0x00ffU<< 8| "
        (((short)(x) & (short)0xff00U>> 8) ))

这里实现的是一个交换两个字节顺序.其他几个宏类似.

我们改写一下上面的程序
#include <stdio.h>
#include 
<assert.h>

#define sw16(x) "
    ((
short)( "
        (((short)(x) & (short)0x00ffU<< 8| "
        (((short)(x) & (short)0xff00U>> 8) ))

// 因为x86下面是低位在前,需要交换一下变成网络字节顺序
#define htons(x) sw16(x)
 
void main( void )
{
        
short test;
        FILE
* fp;
        
        test 
= htons(0x3132); //(31ASIIC码的’1’,32ASIIC码的’2’)
        if ((fp = fopen ("c:""test.txt""wb")) == NULL)
              assert(
0);
        fwrite(
&test, sizeof(short), 1, fp);
        fclose(fp);
}

 
    如果在高字节在前的机器上,由于与网络字节顺序一致,所以我们什么都不干就可以了,只需要把#define htons(x) sw16(x)宏替

换为 #define htons(x) (x).
    一开始我在理解这个问题时,总在想为什么其他数据不用交换字节顺序?比如说我们write一块buffer到文件,最后终于想明白了,

因为都是unsigned char类型一个字节一个字节的写进去,这个顺序是固定的,不存在字节顺序的问题.

【用函数判断系统是Big Endian还是Little Endian】


bool IsBig_Endian()
//如果字节序为big-endian,返回true;
//反之为   little-endian,返回false
{
    unsigned 
short test = 0x1122;
    
if(*( (unsigned char*&test ) == 0x11)
       
return TRUE;
else
    
return FALSE;

}
//IsBig_Endian()

【打印程序对象的字节表示】
// 可在不同平台与硬件架构的机器中测试运行这段代码,理解大端表示和小端表示的不同.
// 这段代码使用强制类型转换规避类型系统
#incluede <stdio.h>

// 假设每个字节都是非负整数
typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len)
{
 
for(int i = 0; i < len; i++)
  printf(
" %.2x", start[i]);
 printf(
"\n");
}

void show_int(int x)
{
 show_bytes((byte_pointer) 
&x, sizeof(int));
}

void show_float(float x)
{
 show_bytes((byte_pointer) 
&x, sizeof(float));
}

// 在使用相同编码(如ASCII编码)的系统中,字符串字节表示得到的结果一般是相同的.所以文本数据比二进制数据具有更强的平台无关性
void show_string(char *x)
{
 show_bytes((byte_pointer) x, strlen(x));
}

void show_pointer(void *x)
{
 show_bytes((byte_pointer) 
&x, sizeof(void *));
}

void test_show_bytes(int val)
{
 
int ival = val;
 
float fval = (float)ival;
 
int *pval = &ival;
 
 show_int(ival); 
// 各个机器因为大端表示和小端表示的不同,从而只是字节顺序不同
 show_float(fval); // 各个机器因为大端表示和小端表示的不同,从而只是字节顺序不同
 show_pointer(pval); // 指针值是与机器相关的(linux,sun使用4字节地址, 而alpha使用八字节地址)
}

---------------------------------------------
对于如数值12345在int型和float型时的编码表示

posted @ 2008-11-12 11:58 LukeW 阅读(647) | 评论 (0)编辑 收藏