qos rule list

/ # /var/acl list dump

shiva_acl_list_dump:

/ # /var/acl status get
true
/ # 
/ # 
/ # /var/acl
acl status get 
acl status set <enable|disable>
acl list create <list_id> <priority>
acl list destroy <list_id>
acl list bind <list_id> <in|eg> <port|other> <objindex>
acl list unbind <list_id> <in|eg> <port|other> <objindex>
acl rule add <list_id> <rule_id> <rule_nr>
acl rule del <list_id> <rule_id> <rule_nr>
acl rule query <list_id> <rule_id>
acl app set <mark:0-7|others means no remark> <queue:0-3|others means no requeue> <L4_dstport>
acl multi set <portid>
acl list dump 
acl rule dump 
acl list reset 
acl qosrule clear 
acl rule clear 
/ # echo 8 > /proc/sys/kernel/printk
/ # /var/acl list dump

shiva_acl_list_dump:

/ # /var/acl list dump

shiva_acl_list_dump:

[id]:31  [pri]:31  [size]:02  [addr]:30  [pts_map]:0x1f
[id]:01  [pri]:01  [size]:01  [addr]:00  [pts_map]:0x01
/ # /var/acl rule query 31 0 1
acl rule query <list_id> <rule_id>
/ # /var/acl rule query 31 0  

[rule_type]:ip4
[mac_up]:0x7  [mac_up_mask]:0x7
[remark_queue]:yes  [queue]:3
[match_counter]:9/ # 
/ # 
/ # 
/ # /var/acl rule query 31 1

[rule_type]:ip4
[mac_up]:0x6  [mac_up_mask]:0x7
[remark_queue]:yes  [queue]:3
/ # /var/acl rule query 1 0 

[rule_type]:ip4
[ip_l4_dport_op]:mask  [dport]:0x2382  [dport_mask]:0xffff
[remark_queue]:yes  [queue]:1
[match_counter]:0/ #  

ieee80211_ioctl_setkey: ******** SETKEY : error: 0 , key_type=3, macaddr=ff:ff:ff:ff:ff:ff, key_len=16


/ # 
/ # 
/ # /var/qos
qos ptmode get <port> <da|up|dscp|port>
qos ptmode set <port> <da|up|dscp|port> <enable|disable>
qos ptmodepri set <port> <da|up|dscp|port> <pri>
qos ptmodepri get <port> <da|up|dscp|port>
qos ptschmode set <port> <sp|wrr|mix|mixplus> <q0,q1,q3,q4>
qos ptschmode get <port>
qos dscpq set <dscp> <queueid:0-3>
qos dscpq get <dscp>
qos upq set <up> <queueid:0-3>
qos upq get <up>
qos defpri set <port> <pri:0-7>
qos defpri get <port>
qos macq set <mac_addr> <portmap> <static|dynamic> <queue:0-3>
qos macq get <mac>
qos ptmode show 
qos dscpq show 
qos upq show 
qos macq show 
qos defpri show 
qos iptv enable <port> <queue>
qos iptv disable <port>
/ # /var/qos ptmode show
Port            Port Mode               Status          PRI
0               DA              Disable         0  
0               UP              Disable         1  
0               DSCP            Disable         2  
0               PORT            Enable          3  
1               DA              Disable         0  
1               UP              Disable         1  
1               DSCP            Disable         2  
1               PORT            Enable          3  
2               DA              Disable         0  
2               UP              Disable         1  
2               DSCP            Disable         2  
2               PORT            Enable          3  
3               DA              Disable         0  
3               UP              Disable         1  
3               DSCP            Disable         2  
3               PORT            Enable          3  
4               DA              Disable         0  
4               UP              Disable         1  
4               DSCP            Disable         2  
4               PORT            Enable          3  
5               DA              Disable         0  
5               UP              Disable         1  
5               DSCP            Disable         2  
5               PORT            Enable          3  
6               DA              Disable         0  
6               UP              Disable         1  
6               DSCP            Disable         2  
6               PORT            Enable          3  
/ # 
/ # 
/ # /var/qos upq show
VLAN PRI                QUEUE
0                   0
1                   0
2                   1
3                   1
4                   2
5                   2
6                   3
7                   3
/ # /var/qos defpri show
Port            PRI
0               0 
1               0 
2               4 
3               0 
4               0 
5               0 
6               0 
/ # 

sockaddr结构体 socket


    sockaddr结构体
sockaddr的缺陷:sa_data把目标地址和端口信息混在一起了
struct sockaddr {  
     unsigned short sa_family;
   char sa_data[14];                  
   }; 
sa_family是通信类型,最常用的值是 "AF_INET"
sa_data14字节,包含套接字中的目标地址和端口信息

  
   sockaddr_in 结构体
sockaddr_in结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
struct sockaddr_in { 
   short int sin_family;
   unsigned short int sin_port; 
     struct in_addr sin_addr;
struct in_addr { 
    unsigned long s_addr;
           }
                
     unsigned char sin_zero[8];
}   

   sin_port和sin_addr都必须是NBO
一般可视化的数字都是HBO(本机字节顺序)


    sin_zero 初始值应该使用函数 bzero() 来全部置零。
   一般采用下面语句
struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
  

   sockaddr_in结构体变量的基本配置
struct sockaddr_in ina;

bzero(&ina,sizeof(ina));

ina.sin_family=AF_INET;

ina.sin_port=htons(23);
ina.sin_addr.s_addr = inet_addr("132.241.5.10");  


    sockaddr 和 sockaddr_in的相互关系
一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数
  •     sockaddr_in用于socket定义和赋值
  •     sockaddr用于函数参数

   最典型的源、目的节点socket定义
对于源、目的地址和源、目的地址端口,需要建立两个socket变量
cliaddr绑定源地址和源端口
servaddr用于connect和sendto的设定目的地址和目的端口
struct sockaddr_in servaddr,cliaddr;

create_socket(char *server_addr_string,unsigned int server_port)
{
源socket赋值
       bzero(&cliaddr,sizeof(cliaddr));
       cliaddr.sin_family = AF_INET;
       通常TCP/UDP 协议源地址和端口都是随机的
       cliaddr.sin_addr.s_addr = htons(INADDR_ANY);
       cliaddr.sin_port = htons(0);

目的socket赋值
       bzero(&servaddr,sizeof(servaddr));
       servaddr.sin_family = AF_INET;
       inet_aton(server_addr_string,&servaddr.sin_addr);
       servaddr.sin_port = htons(server_port);
}


    网络字节顺序 (Network Byte Order)      NBO
结构体的sin_port和sin_addr都必须是NBO

   本机字节顺序 (Host Byte Order)    HBO
一般可视化的数字都是HBO

    NBO,HBO二者转换
inet_addr()    将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
inet_aton()    将字符串点数格式地址转化成NBO
inet_ntoa ()     将NBO地址转化成字符串点数格式
htons()    "Host to Network Short"
htonl()    "Host to Network Long"
ntohs()    "Network to Host Short"
ntohl()    "Network to Host Long"
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型

    三种给socket赋值地址的方法
inet_aton(server_addr_string,&myaddr.sin_addr);
myaddr.sin_addr.s_addr = inet_addr("132.241.5.10");
INADDR_ANY转不转NBO随便
myaddr.sin_addr.s_addr = htons(INADDR_ANY);  
myaddr.sin_addr.s_addr = INADDR_ANY;


    两种给socket 赋值端口的方法
#define MYPORT 3490 
myaddr.sin_port = htons(MYPORT);
0(随机端口)转不转NBO随便
myaddr.sin_port = htons(0);
myaddr.sin_port = 0;  


    htons/l和ntohs/l等数字转换都不能用于地址转换,因为地址都是点数格式,所以地址只能采用数字/字符串转换如inet_aton,inet_ntoa;
唯一可以用于地址转换的htons是针对INADDR_ANY
 cliaddr.sin_addr.s_addr = htons(INADDR_ANY)

   inet_addr()与inet_aton()的区别
  •     inet_addr()    是返回值型
struct sockaddr_in ina;

ina.sin_addr.s_addr = inet_addr("132.241.5.10"); 
  •     inet_aton()     是参数指针型
struct sockaddr_in ina;

inet_aton("132.241.5.10",&ina.sin_addr);


   inet_ntoa  将NBO地址转化成字符串点数格式
参数:结构体变量.sinaddr
返回值:字符串指针
a1 = inet_ntoa(ina.sin_addr);
printf("address 1: %s\n",a1); 
address 1: 132.241.5.10 


    inet_addr()的缺陷:必须对-1做检测处理
因为inet_addr()的结果是整型,而发生错误时返回-1。
而 ina.sin_addr.s_addr是unsigned long型
-1在long short显示成111111111,和IP地址255.255.255.255相符合!会被误认为广播地址!

sockaddr结构体 socket

    sockaddr结构体
sockaddr的缺陷:sa_data把目标地址和端口信息混在一起了
struct sockaddr {  
     unsigned short sa_family;
   char sa_data[14];                  
   }; 
sa_family是通信类型,最常用的值是 "AF_INET"
sa_data14字节,包含套接字中的目标地址和端口信息

  
   sockaddr_in 结构体
sockaddr_in结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
struct sockaddr_in { 
   short int sin_family;
   unsigned short int sin_port; 
     struct in_addr sin_addr;
struct in_addr { 
    unsigned long s_addr;
           }
                
     unsigned char sin_zero[8];
}   

   sin_port和sin_addr都必须是NBO
一般可视化的数字都是HBO(本机字节顺序)


    sin_zero 初始值应该使用函数 bzero() 来全部置零。
   一般采用下面语句
struct sockaddr_in cliaddr;
bzero(&cliaddr,sizeof(cliaddr));
  

   sockaddr_in结构体变量的基本配置
struct sockaddr_in ina;

bzero(&ina,sizeof(ina));

ina.sin_family=AF_INET;

ina.sin_port=htons(23);
ina.sin_addr.s_addr = inet_addr("132.241.5.10");  


    sockaddr 和 sockaddr_in的相互关系
一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数
  •     sockaddr_in用于socket定义和赋值
  •     sockaddr用于函数参数

   最典型的源、目的节点socket定义
对于源、目的地址和源、目的地址端口,需要建立两个socket变量
cliaddr绑定源地址和源端口
servaddr用于connect和sendto的设定目的地址和目的端口
struct sockaddr_in servaddr,cliaddr;

create_socket(char *server_addr_string,unsigned int server_port)
{
源socket赋值
       bzero(&cliaddr,sizeof(cliaddr));
       cliaddr.sin_family = AF_INET;
       通常TCP/UDP 协议源地址和端口都是随机的
       cliaddr.sin_addr.s_addr = htons(INADDR_ANY);
       cliaddr.sin_port = htons(0);

目的socket赋值
       bzero(&servaddr,sizeof(servaddr));
       servaddr.sin_family = AF_INET;
       inet_aton(server_addr_string,&servaddr.sin_addr);
       servaddr.sin_port = htons(server_port);
}


    网络字节顺序 (Network Byte Order)      NBO
结构体的sin_port和sin_addr都必须是NBO

   本机字节顺序 (Host Byte Order)    HBO
一般可视化的数字都是HBO

    NBO,HBO二者转换
inet_addr()    将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
inet_aton()    将字符串点数格式地址转化成NBO
inet_ntoa ()     将NBO地址转化成字符串点数格式
htons()    "Host to Network Short"
htonl()    "Host to Network Long"
ntohs()    "Network to Host Short"
ntohl()    "Network to Host Long"
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型

    三种给socket赋值地址的方法
inet_aton(server_addr_string,&myaddr.sin_addr);
myaddr.sin_addr.s_addr = inet_addr("132.241.5.10");
INADDR_ANY转不转NBO随便
myaddr.sin_addr.s_addr = htons(INADDR_ANY);  
myaddr.sin_addr.s_addr = INADDR_ANY;


    两种给socket 赋值端口的方法
#define MYPORT 3490 
myaddr.sin_port = htons(MYPORT);
0(随机端口)转不转NBO随便
myaddr.sin_port = htons(0);
myaddr.sin_port = 0;  


    htons/l和ntohs/l等数字转换都不能用于地址转换,因为地址都是点数格式,所以地址只能采用数字/字符串转换如inet_aton,inet_ntoa;
唯一可以用于地址转换的htons是针对INADDR_ANY
 cliaddr.sin_addr.s_addr = htons(INADDR_ANY)

   inet_addr()与inet_aton()的区别
  •     inet_addr()    是返回值型
struct sockaddr_in ina;

ina.sin_addr.s_addr = inet_addr("132.241.5.10"); 
  •     inet_aton()     是参数指针型
struct sockaddr_in ina;

inet_aton("132.241.5.10",&ina.sin_addr);


   inet_ntoa  将NBO地址转化成字符串点数格式
参数:结构体变量.sinaddr
返回值:字符串指针
a1 = inet_ntoa(ina.sin_addr);
printf("address 1: %s\n",a1); 
address 1: 132.241.5.10 


    inet_addr()的缺陷:必须对-1做检测处理
因为inet_addr()的结果是整型,而发生错误时返回-1。
而 ina.sin_addr.s_addr是unsigned long型
-1在long short显示成111111111,和IP地址255.255.255.255相符合!会被误认为广播地址!

PCI设备驱动 三

为了能看到实际的运行效果,我们选择8139too网卡作为示例,从该网卡的linux驱动程序中裁剪相关代码。
一个PCI设备的驱动程序必须要向内核中的PCI核心描述自己。同时,它也必须告诉PCI核心自己能够驱动哪些设备。下面,就介绍两个相关的重要数据结构。
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
kernel_ulong_t driver_data; /* Data private to the driver */
};

struct pci_driver {
struct list_head node;
char *name;
struct module *owner;
const struct pci_device_id *id_table; //驱动所能操纵的设备id列表。
int (*probe)(struct pci_dev *dev, const struct pci_device_id
*id); //插入新设备
void (*remove)(struct pci_dev *dev); //移除设备。
int (*suspend)(struct pci_dev *dev, pm_message_t state);
int (*resume)(struct pci_dev *dev);
int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);
void (*shutdown) (struct pci_dev *dev);
struct device_driver driver;
struct pci_dynids dynids;
};
pci_device_id唯一标识一个PCI设备。它的几个成员依次分别表示:厂商号,设备号,子厂商号,子设备号,类别,类别掩码(类可分为基类,子类),私有数据。每一个PCI设备的驱动程序都有一个pci_device_id的数组,用于告诉PCI核心自己能够驱动哪些设备。8139too的驱动程序定义它的pci_device_id数组如下:
static struct pci_device_id rtl8139_pci_tbl[];
该数组被初始化为8139系列的一组网卡,当PCI核心得到这个数组后,会拿数组中的每一项跟从PCI配置空间中读取到的数据进行比对,从而为该驱动程序找到正确的设备。而pci_driver代表一个pci驱动程序。成员id_talbe即是指向pci_device_id数组的指针。name是驱动程序的名字,probe完成探测工作,即拿pci_device_id数组与内核中的数据进行比对。remove完成驱动程序的移除工作。关键的成员就这几个。
驱动程序通过pci_module_init向内核注册自己(我们有时会看到pci_register_driver函数,其实它们是同一个,在内核代码中会看到,只是一个简单的#define):
pci_module_init(&pci_driver);
调用函数后,如果pci_device_id数组中标识的设备存在于系统中,并且该设备恰好还没有驱动程序,则该驱动程序会被安装。下面我们来看从8139too驱动代码中裁剪的pci设备初始化代码:
pci_driver.h:

/* pci_driver.h
* helinqiang@hotmail.com
* 2006-3-5
*/
#ifndef PCI_DRIVER_H
#define PCI_DRIVER_H

#include <linux/mod_devicetable.h> //for struct pci_device_id
#include <linux/module.h> //for MODULE_DEVICE_TABLE
#include <linux/pci.h> //for struct pci_driver

#define DRV_NAME "8139too"
#define DRV_VERSION "0.9.27"
#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION

typedef enum{
RTL8139 = 0,
RTL8129,
}board_t;

static struct pci_device_id rtl8139_pci_tbl[] = {
{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },

#ifdef CONFIG_SH_SECUREEDGE5410
/* Bogus 8139 silicon reports 8129 without external PROM :-( */
{0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },

Linux 设置环境变量

1.设置临时环境变量(重启后消失)
# export JAVA_HOME=\usr\java\jdk1.4 2.

2.设置永久环境变量
2.1所有用户(不安全)修改/etc/profile(对所有用户都是有效的)
# vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.4
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
vi /etc/sysconfig/i18n //这里比profile在开机前先执行也可以
修改 LANG="zh_CN.GB2312"   
     或者 LANG="zh_CN.GBK"   
     或者 LANG="zh_CN.18030"
LANG="zh_CN.GB18030"
LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"
SUPPORTED="zh_HK.UTF-8:zh_HK:zh:zh_CN.UTF-8:zh_CN:zh:zh_SG.UTF-8:zh_SG:zh:zh_TW.UTF-8:zh_TW:zh:en_US.UTF-8:en_US:en"
SYSFONT="lat0-sun16"
2.2单独用户?修改~/.bashrc文件。(每个用户目录下都有,ls -all)
# cd ~
# vi .bashrc
set JAVA_HOME=/usr/local/jdk
export JAVA_HOME
set PATH=$PATH:$JAVA_HOME/bin
export PATH
set CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export CLASSPATH
第一种适用于为单一用户设置 PATH,第二种是为全局设置 PATH。

第一种方法:
在用户主目录下有一个 .bashrc 文件,可以在此文件中加入 PATH 的设置如下:
export PATH=”$PATH:/your path1/:/your path2/…..”
注意:每一个 path 之间要用 “:“ 分隔。
注销重启 X 就可以了。


第二种方法:
在 /etc/profile中增加。
PATH="$PATH:/usr/local/arm/2.95.3/bin"
export PATH

如果要使得计时生效,使用 source 命令

source .bashrc

通过hello world介绍2.6内核模块编译的最基本原理

1、makefile的预备知识

本文旨在介绍编译模块的原理,不详细介绍makefile。

下面是一个简单的没有任何用途的makefile:

1 MAKE_TEST = make test 

3 all: 
4     @echo "make all
5     @echo "MAKE_TEST = $(MAKE_TEST)" 

7 test: 
8     @echo "make test
9     @echo "MAKE_TEST = $(MAKE_TEST)"

本Makefile首先定义了一个变量MAKE_TEST,然后有两个目标:all和test

下面是分别执行make和make test的运行结果:

wangjiankun@driver:~/make_study$ make 
make all 
MAKE_TEST = make test 
wangjiankun@driver:~/make_study$ make test 
make test 
MAKE_TEST = make test 
wangjiankun@driver:~/make_study$

make test的运行结果说明:虽然指定了目标test,但是目标test之前定义的变量仍然是起作用的,这是介绍这个例子的唯一目的,用来解释下面的变量KERNELRELEASE在目标modules之前定义。

2、hello world的makefile文件

ifneq ($(KERNELRELEASE),) 
2     obj-m := hello_world.o 

else 
5     KERNELDIR ?= /lib/modules/2.6.19/build 
6     PWD := $(shell pwd
default
8     $(MAKE) -C $(KERNELDIRM=$(PWDmodules 
endif

上面是hello world模块的Makefile文件,当在当前目录下执行make命令时,由于变量KERNELRELEASE没有定义,所以make走else分支;

?=操作符make手册是如下解释的:

This statement:

FOO ?= bar

is exactly equivalent to this

ifeq ($(origin FOO), undefined)

FOO = bar

endif

Note that a variable set to an empty value is still defined, so ‘?=’ will not set that variable.

也就是说,第5行只有变量KERNELDIR没有被定义时,才给它赋值,所以此时给KERNELDIR赋值为:/lib/modules/2.6.19/build;

第7行,make遇到了第一个目标:default,执行第8行的命令:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules。第8行中有如下3个介绍点:(1)、-C选项是进目录的意思;(2)、定义了变量M,对于make来说,M就是一个变量,除此之外没有任何特殊的含义,make更不会因为他是module的首字母而认为是在编译模块;(3)、第8行的编译目标是modules。这样make带着两个信息(一个是变量M及其值,另一个是目标是modules)进入由变量KERNELDIR定义的目录来编译,在此是进入目录:/lib/modules/2.6.19/build。如下所示:

wangjiankun@driver:/lib/modules/2.6.19$ pwd 
/lib/modules/2.6.19 
wangjiankun@driver:/lib/modules/2.6.19$ ls -l 
total 1313 
lrwxrwxrwx 1 root root     37 Dec 14 16:18 build -> /home/wangjiankun/kernel/linux-2.6.19 
drwxr-xr-x 9 root root   1024 Dec 14 16:18 kernel 
-rw-r--r-- 1 root root 296327 Dec 14 16:18 modules.alias 
-rw-r--r-- 1 root root     69 Dec 14 16:18 modules.ccwmap 
-rw-r--r-- 1 root root 299229 Dec 14 16:18 modules.dep 
-rw-r--r-- 1 root root    813 Dec 14 16:18 modules.ieee1394map 
-rw-r--r-- 1 root root    730 Dec 14 16:18 modules.inputmap 
-rw-r--r-- 1 root root  21916 Dec 14 16:18 modules.isapnpmap 
-rw-r--r-- 1 root root     74 Dec 14 16:18 modules.ofmap 
-rw-r--r-- 1 root root 226143 Dec 14 16:18 modules.pcimap 
-rw-r--r-- 1 root root   1135 Dec 14 16:18 modules.seriomap 
-rw-r--r-- 1 root root 135623 Dec 14 16:18 modules.symbols 
-rw-r--r-- 1 root root 342460 Dec 14 16:18 modules.usbmap 
lrwxrwxrwx 1 root root     37 Dec 14 16:18 source -> /home/wangjiankun/kernel/linux-2.6.19 
wangjiankun@driver:/lib/modules/2.6.19$

这个目录是在debian下编译内核时生成的,/lib/modules/2.6.19/build指向内核源码包。这样make就要运行内核源码包的主Makefile文件了。

3、linux 2.6内核主Makefile文件对编译模块的支持

由上面的介绍可知:make进入内核源码包带着两个信息:(1)定义了变量M(= 模块的当前目录路径)和(2)目标是modules来执行make命令。先看一下modules目标。

内核主Makefile定义了目标modules:

1202 module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD)) 
1203 PHONY += $(module-dirs) modules 
1204 $(module-dirs): crmodverdir $(objtree)/Module.symvers 
1205     $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@) 
1206 
1207 modules: $(module-dirs) 
1208     @echo '  Building modules, stage 2.'; 
1209     @echo "wangjiankun noted 1
1210     $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost

modules目标依赖于由变量module-dirs定义的目标,所以要先编译由变量module-dirs定义的目标,1202行定义了变量module-dirs( := $(addprefix _module_,$(KBUILD_EXTMOD)) ),内核主Makefile有如下定义:

63 # Use make M=dir to specify directory of external module to build 
64 # Old syntax make ... SUBDIRS=$PWD is still supported 
65 # Setting the environment variable KBUILD_EXTMOD take precedence 
66 ifdef SUBDIRS 
67   KBUILD_EXTMOD ?= $(SUBDIRS) 
68 endif 
69 ifdef M 
70   ifeq ("$(origin M)", "command line") 
71     KBUILD_EXTMOD := $(M) 
72   endif 
73 endif

由以上定义可知变量KBUILD_EXTMOD就是我们在模块的Makefile中定义的变量M的值,即模块的当前目录。1202行用到了make的一个函数addprefix,addprefix在make手册中如下解释:

$(addprefix prefix,names...) 
The argument names is regarded as a series of names, separated by whitespace
prefix is used as a unit. The value of prefix is prepended to the front of each 
individual name and the resulting larger names are concatenated with single 
spaces between them. For example, 
$(addprefix src/,foo bar) 
produces the result ‘src/foo src/bar’.

这样变量module-dirs就等于_module_模块的当前目录路径,在我的测试系统中的值为:_module_/home/wangjiankun/ldd3/ch2

1204行目标:$(module-dirs)依赖于crmodverdir和$(objtree)/Module.symvers

内核主Makefile文件定义了目标:crmodverdir如下所示,其实就是在模块的当前目录下建立一个文件夹.tmp_versions,并将其内部文件全部删除

1190 crmodverdir: 
1191     $(Q)mkdir -p $(MODVERDIR) 
1192     $(Q)rm -f $(MODVERDIR)/*

内核主Makefile文件定义了目标:$(objtree)/Module.symvers如下所示,其实就是检查内核源码根目录下是否存在文件Module.symvers,如果不存在打印出警告信息,但是模块仍然可以成功编译。

1195 $(objtree)/Module.symvers: 
1196     @test -e $(objtree)/Module.symvers || ( / 
1197     echo; / 
1198     echo "  WARNING: Symbol version dump $(objtree)/Module.symvers"; / 
1199     echo "           is missing; modules will have no dependencies and modversions."; / 
1200     echo )

1205行的$(patsubst _module_%,%,$@)就是变量module-dirs的值:_module_/home/wangjiankun/ldd3/ch2除去_module_的结果,即:/home/wangjiankun/ldd3/ch2。表达式中的$@意为:The file name of the target.

$(build)的值为:-f scripts/Makefile.build obj(没有找到在什么地方定义,待查,此处是在我的测试系统上,通过echo打印出来的),这样1205行的实际语句为:

@make -f scripts/Makefile.build obj=/home/wangjiankun/ldd3/ch2

scripts/Makefile.build文件比较复杂没有详细研究,但有一点是肯定的,make二次执行模块目录下的Makefile文件就是通过这个脚本直接或间接进行的(目前猜测,可能不是二次执行了模块的Makefile文件,而是将模块的Makefile文件包含到了内核的build系统的脚本或Makefile中使得obj-m := hello_world.o起作用而编译了模块)。通过这个脚本,make二次执行模块的Makefile文件,此时变量KERNELRELEASE已经定义,内核主Makefile有如下定义:

320 # Read KERNELRELEASE from include/config/kernel.release (if it exists) 
321 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)

include/config/kernel.release文件内容如下:

wangjiankun@driver:~/kernel/linux-2.6.19/include/config$ pwd 
/home/wangjiankun/kernel/linux-2.6.19/include/config 
wangjiankun@driver:~/kernel/linux-2.6.19/include/config$ cat kernel.release 
2.6.19 
wangjiankun@driver:~/kernel/linux-2.6.19/include/config$

也就是说,此时KERNELRELEASE不但定义了,而且值为:内核版本号,所以make走第2行的分支并通过隐含规则将hello_world.c编译成hello_world.o文件。

make回到1208行打印出:Building modules, stage 2.,并通过1210行生成模块。

eFuse技术

近日从Hexus网站传出消息,ATI宣布已经开发出名为eFuse的芯片控制技术,这种特殊的技术可以对芯片的特定部分提供更高的控制权,使芯片能够对自身进行监控、调节和修复,能够在系统需要或出现故障时进行自我动态调整。最终结果就是芯片的智能程度更高、同时也能提高产能! 

    eFuse技术的原理就是在设计之初,为每一块芯片上增加了大量的微电溶丝,它们与特定的随机软件结合时,eFuse可使芯片分配自身内部电路以应对不同计算任务,或者增加芯片的运算频率。这种微电溶丝被焊接在芯片上而无需增加成本,它的功能是控制各个电路的速度,从而可以管理电路性能与电力消耗。 

    eFuse还可以随意的彻底切断芯片内某些缓存或者是功能模块,当然不会影响到其他部分的正常运行。因此eFuse技术能够更加合理的利用瑕疵芯片、或者是动态关闭芯片功能从而大幅降低功耗(比如移动GPU)。 

GPU产能/智能更高!ATI开发eFuse技术 

    eFuse的这种功能有助于修复芯片的某些缺陷或围绕缺陷做善后工作。例如,当芯片电路运行过快或过慢时,微电溶丝可以改变电路的电压或者提高和减缓速度,以适应任务的需要。或者是干脆关闭这些有问题的模块,从而衍生出不同规格的产品。目前广泛应用在GPU上面的屏蔽管线技术仅仅是“屏蔽”,实际上被屏蔽的部分还是要消耗一部分电力的。如果芯片的作用被改变,用户需要芯片用较少电力,或者是让芯片发挥出较强的性能,此时eFuse技术可被用于对芯片重新编程。 

    实际上几年前IBM公司就提出了eFuse技术的概念,不过eFuse技术需要90nm或者更先进工艺的支持
什么是eFuse? 

  eFuse的诞生源于几年前IBM工程师的一个发现:与更旧的激光熔断技术相比,电子迁移(EM)特性可以用来生成小得多的熔丝结构。EM熔丝可以在芯片上编程,不论是在晶圆探测阶段还是在封装中。采用I/O电路的片上电压(通常为2.5V),一个持续200微秒的10毫安直流脉冲就足以编程单根熔丝。 

  不同于大多数FPGA使用的SRAM阵列,eFuse一次只有一根熔丝能够被编程,这是该方法的配置能力存在限制范围的原因。但当与日益成熟的内置自测试(BIST)引擎组合使用时,这些熔丝就变成了强大的工具,能减少测试和自修复的成本,而这正是复杂芯片设计所面临的重大挑战。  

  eFuse就好像在硅片上建立了无数个交通岗哨,控制信号的传输或停止,据悉这将把芯片中的电路运行效率提高上千倍。这种功能将会为电子领域带来一种“大规模市场效应。”FPGA提供商加州Xilinx公司CTO里奥.波尔森先生表示:“比如您购买了一个新的控制器,最开始的时候控制器的功能是空的,不过在把它带回家后,它重新识别了您家中的所有系统,电视、音响、DVD、并且自动对自身进行改造,来控制这些电器。”