shell变量

2 10 月, 2010 by edsionte 无评论 »

正如大多数的计算机语言那样,shell变量只能以字母和下划线为变量名的起始,其后可跟任意长度的字母、数字和下划线。shell变量的赋值(或者说定义)方式可参考如下例子:

edsionte@edsionte-desktop:~$ fruit=apple

其中fruit就是进行赋值的变量,紧接着即为赋值符=,然后是新值(变量值)。注意,整个赋值语句中不能包含空格。此时你可能会有“如果我的新值中含有空格,那我如何赋值”的疑问,那么不用担心,你可以这样赋值:

edsionte@edsionte-desktop:~$ fruit=“north apple”

对了,就是对整个新值加上双引号。一个变量赋值完毕后,若要取出变量中的值,那么只需在此变量前加上$符号,比如:

edsionte@edsionte-desktop:~$ echo $fruit
north apple
edsionte@edsionte-desktop:~$ echo fruit
fruit

你看到了,通过在变量名前加$,可以取出此变量中的值;相反,以上面例子来说的话,如果只echo fruit,那么它只会显示这个变量名本身而不是显示变量的值。

了解了变量的基本的赋值规则,我们再来看下面的赋值语句:

edsionte@edsionte-desktop:~$ myfruit=$fruit
edsionte@edsionte-desktop:~$ echo $myfruit
north apple

将变量A作为另一个变量B的新值时,只需对变量A加$引用其值就可以了,不需再加引号(但加上也无妨)。

当多个变量赋值给一个变量时,可参考如下例子:

edsionte@edsionte-desktop:~$ mumfruit=pear
edsionte@edsionte-desktop:~$ dadfruit=banana
edsionte@edsionte-desktop:~$ familyfruit="$myfruit,$dadfruit,$mumfruit"
edsionte@edsionte-desktop:~$ echo $familyfruit
north apple,banana,pear

也就是说,多个变量需要加上引号。

OK,关于变量的基本要点就是这些,get it?

什么是中断?

1 10 月, 2010 by edsionte 8 comments »

Last Update:2011/11/03

我们经常听到中断这个词,到底什么是中断?在这之前我先讲给大家一个故事。

从前有两位班主任A和B,A老师带一班,B老师带二班。这两位老师平时都很忙,平时除了为学生们备课改作业,当某个同学提出问题时,还要为他们解答疑问。A老师生怕遗漏每一位同学提出的问题,每隔一段时间就放下手头的工作,不断轮流寻问每一位同学:“你有问题吗”。也许被寻问的这位同学恰好有问题要咨询老师,可这毕竟是少数;而当A老师继续批改作业的时候又出现了一些同学提出问题,可是这个时候还没到A老师轮流寻问同学的时间。就这样,A老师的宝贵时间经常被浪费。

B老师和A老师有所不同,他认为完全没有必要这样死板的寻问每一位同学是否有问题。他对全班同学说:“谁有问题就主动来找我“。即便他可能正在改作业,但是完全可以暂时放下手头那些重要的工作,先为这位迷惑的同学解决问题。因此,B老师既可以改作业,又可以在学生主动提出问题的时候为那个学生解决问题。显然B老师的工作效率比A老师提高了很多。

故事看懂了,那么恭喜你,你也懂了什么是中断。

上面的两位老师分别代表系统中对设备进行管理的两种典型的方式。A老师的那种工作方式属于早期的程序查询控制方式(或称为轮询),内核定期对设备的状态进行查询;而B老师则属于中断控制方式,I/O设备需要服务时,可主动向内核发出中断请求并打断CPU当前正在执行的任务。前者内核为主动;而后者变内核主动为被动,由设备主动向内核发出中断请求。

从物理角度来看,中断请求是由外部硬件设备产生的一种电信号,外部设备首先将这种电信号发给中断控制器,接着中断控制器将此电信号发送给CPU。CPU检查到该中断信号后再通知内核,然后由内核完成后续的一些列处理工作。显然,内核不需要定期去检查设备,从而提高了CPU利用率。

通过以上的描述,你应该对中断有了一个大致的了解,不过想要更具体的了解中断必须搞清楚以下的知识点。

1.中断的分类

早期以及一般情况下,我们所说的中断即指由外设所产生的中断。随着计算机的迅速发展,中断不再仅仅局限于外部设备,CPU本身也会产生中断,不过我们将这种中断称为异常。

对于x86体系结构而言,中断可以分为两大类:同步中断和异步中断。同步中断即我们上面所说的异常,它是由 CPU 在执行非法命令时所产生的。之所以称为同步,是因为这种中断请求信号与代码指令同步执行,也就是说只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间。而异步中断即由外部设备产生的中断,这种中断可以随时发生,习惯上,我们将异步中断仍然称为中断。

中断可分为可屏蔽中断(Maskable interrupt)和不可屏蔽中断(Nomaskable interrupt)。异常可分为故障(fault)、陷阱(trap)、终止(abort)三类。

可屏蔽中断主要是针对外部设备所产生的中断信号,不可屏蔽中断一般是指计算机内部硬件产生的中断。由于异常是CPU发出的中断信号,与中断控制器(下文有解释)无关,因此异常不能被屏蔽。那么,异常和不可屏蔽中断有些相似点:它们均与外部设备无关,并且均不能被屏蔽。

2.中断控制器

中断控制器可分为可编程中断控制器(Programmable Interrupt Controller,PIC)和高级可编程中断控制器(Advanced Programmable Interrupt Controller,APIC)。前者仅可用于单处理器(Uni-processor)平台,后者则可用于多处理器(Mutiliple Processor)平台。

传统的PIC都是通过两片级联的8259A来管理和控制15个由外部设备所产生的中断源。由下图可看到,每片8259A芯片最多可管理8个中断源。但由于两片8259A芯片的级联,即从片的INT输出端与主片的2号(第三条线)中断线相连接,所以总共可以管理15个中断源。

外设和中断控制器上每根相连的线被称为中断线(也称为IRQ线)。对这些中断线进行编号就形成了中断号。IRQn线所对应的中断号即为n。当外部设备产生中断时,就通过中断线向系统发出中断请求(Interrupt ReQuirement)。由于中断控制器只能控制15个中断源,而如今的外设又日益剧增,因此就出现了对各外设共享一条中断线的情况,即中断共享(后文会有详细解释)。

上面已经说过,外部设备的中断请求可以被屏蔽,但是具体体现到硬件上是如何做到的?在每个8259A芯片上都有一个8位的中断屏蔽寄存器,每一位对应一条中断线。在对应为上置1则可屏蔽此条中断线;相反置0则可启动此条中断线。

需要说明的是,现在大多数计算机都使用的是APIC,可以通过查看/proc/interrupts文件获取中断控制器的名称。之所以将传统的8259A作为PIC的举例来学习,最大的原因还是因为它非常的经典,就如同古董——8086处理器一样。通过查看/proc/interrupts文件,可以发现APIC的中断号并不止15个(但即便是这样,还需要共享中断)。

3.中断向量

x86体系结构支持256种中断,即256个中断源。将这些中断源按照0到255的顺序对每种中断进行编号,这个编号叫做中断向量,通常使用8位无符号整数来存储这个向量。中断号和中断向量存在一对一的映射关系。

中断号和中断向量是两个不同的概念。当I/O设备把中断信号发送给中断控制器时,与之关联的是一个中断号;而当中断控制器将该中断信号传递给CPU时,与之关联的是一个中断向量。也就是说,中断号是以中断控制器的角度而言的;而中断向量则是以CPU的角度而言的。中断号和中断向量存在一对一的映射关系。

通常,Intel将编号为0~31的向量分配给异常和非屏蔽中断,这部分向量是固定的。因此在8259A默认情况下,中断号n所对应的中断向量为n+32。

4.中断服务例程

在响应一个具体的中断时,内核会执行一个函数,该函数被称为中断服务例程(Interrupt Service Routine,ISR)。每一个设备的驱动程序中都会定义相关的中断服务例程。从下面的代码可以看到,中断服务例程有两个参数,分别为int型和void指针型。并且返回值为irqreturn_t。

 //linux/include/linux/interrupt.h
  98typedef irqreturn_t (*irq_handler_t)(int, void *);
 //linux/include/linux/irqreturn.h
  10enum irqreturn {
  11        IRQ_NONE,
  12        IRQ_HANDLED,
  13        IRQ_WAKE_THREAD,
  14};
  15
  16typedef enum irqreturn irqreturn_t;

由于irqreturn_t是一个枚举类型,因此本质上为整形。并且IRQ_NONE,IRQ_HANDLED,IRQ_WAKE_THREAD的值一次为0,1,2。IRQ_NONE表示不处理所收到的中断请求;IRQ_HANDLED表示接收到了有效的中断请求,并且作出了正确的处理(这一点在后文中有详细讨论)。

需要注意的是,当一个中断服务例程正在执行的时候,该中断所在所有处理器上的都会被屏蔽,以免继续接受同一条中断线上的另一个新的中断。

以上就是关于中断的基本概念,了解了它们就容易进行后续的中断分析了。

热身

1 10 月, 2010 by edsionte 无评论 »

一般开机后,我做的第一件事就是开终端,然后进入到我的代码目录下,查看该子目录下的所有文件。比如我最近在学习设备驱动,那么我会依次进入到下面的目录,而且最终会ls一下:

~/code/driver/myglobalmem/

这么虽然做没有什么意义,可是我想大多数人跟我一样,会有此类“强迫症”,那么我们可以将上面所操作的命令写成如下shell脚本:

#! /bin/sh

cd ~/code/driver/myglobalmem
ls -l

这样每次打开终端后,以.sriptname运行这个脚本文件,就可以到达你想要进的目录下了。

makefile再学习

27 9 月, 2010 by edsionte 无评论 »

前几篇文章中,我们一起分析了字符设备驱动。假如我们已经编写好了驱动代码,那么接下来该如何做?

我们首先要进行make,编译成功后会生成一个globalmem.ko文件。接下来要将这个内核模块插入到内核当中,然后还要利用mknod命令生成一个设备文件节点。接下来我们再利用测试程序,对我们写好的驱动程序进行测试。

那么以上的工作都可以通过下面这个Makefile文件完成,直接在shell终端输入make就可编译这个内核模块,输入make clean就可以清除一些中间文件,输入make install就可以将编译好的内核模块插入到内核当中。更重要的是,这个Makefile文件具有很好的移植性。本文通过分析下面给出的Makefile文件,与大家一起更深入的学习Makefile文件的相关语法以及一些shell编程。

TARGET= globalmem

ifneq ($(KERNELRELEASE),)
 obj-m := $(TARGET).o
 $(TARGET)-objs := module.o global_fops.o
else
 KERNEL := $(shell uname -r)
 KDIR ?= /lib/modules/$(KERNEL)/build
 PWD := $(shell pwd)

.PHONY all
.PHONY clean
.PHONY install
.PHONY remove

all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean
install:
	@sudo ./$(TARGET).sh
remove:
	@sudo ./clean.sh
endif

这个Makefile文件(新Makefile)比这里的Makefile文件(旧Makefile)强大了很多。

1.条件语句

首先注意这个新的Makefile文件在逻辑结构上发生了很大的变化,采用了条件语句:ifneq-else-endif。这个条件语句是用来判断括号中逗号前后的两个变量是否不相等。ifneq之后为符合条件时所要执行的语句,相应的else之后为不符合条件时要执行的语句。上述Makefile文件中的ifneq ($(KERNELRELEASE),)是用来判断KERNELRELEASE变量是否为空,不为空则符合条件。

类似的还有下面的条件语句,只不过条件判断的类型不同。

ifeq-else-endif:如果两个变量相等,则满足条件。

下面两种条件语句中,条件判断处为变量名,是用来判断此变量是否被定义过。

ifdef-else-endif:如果变量被定义,满足条件。

ifndef-else-endif:如果变量未被定义,满足条件。

不过,上述两个条件语句所判断的变量定义没有递归性,比如下面例子:

path=
cur_path=$(path)
ifdef cur_path
right=yes
else
right=no
endif

这个例子中最终执行的是right=yes。虽然path为空,但是cur_path=$(path)却被认为是定义了cur_path变量。正如上面所说定义没有递归行。

2.变量赋值

在Makefile文件中定义一个变量的格式为:变量名 赋值符 变量值

赋值符通常有以下四种类型:=,:=,?=,+=。对于赋值符=与我们平日里使用的等号差不多,但是这里我们需要清除一个概念,那就是递归展开变量。为了更清除的说明上面的概念,请看下面的例子:

first=$(second)
second=$(third)
third=yes
all:
       echo $(first)

很显然结果为yes。当执行make时,first首先展开为second,接着second又展开成为third,再后来引用third的值即yes。可以看到first是递归展开而得到最后的yes值的。这便是我们刚才所谓的递归展开变量。

而与上述变量赋值符号不同的是,:=赋值符号是立即展开变量的,同样的例子,只不过这次我们使用:=赋值符:

first:=$(second)
second:=$(third)
third:=yes
all:
       echo $(first)

此时first为空。这是因为在定义first变量时就立即展开了second,因为second此时未定义。即便此句之后为second变量赋了值,但first的值为空。

另外两个赋值符号比较容易理解。首先+=赋值符是在变量原有值的基础上再增加新的值,而不是覆盖原有变量值。而?=赋值符首先会判断变量实现是否已经被赋值,只有之前未被赋值的变量此刻才能被赋值。

OK,了解了赋值符号的含义,那么再次看上述的Makefile文件,就会清晰很多。

3.伪目标

正如上述所言,直接在shell终端输入make就会执行目标all后的命令,这并不是all目标具有什么默认的效果。只不过在Makefile文件中,第一个目标总被认为是最终目标。因此可以想象到,当你交换一下all和clean的位置,直接执行make时会自动执行clean后面的命令。并且不一定总对第一个目标起名为all,你可以使用你喜欢的目标名(也许all是一种无声的约定 e43 )。

通常在clean这样的目标后都没有依赖文件,因为我们的目的是想让make执行这些目标后的命令。但是当Makefile文件所在目录下有一个名为clean的文件时,此时make clean就会被认为是生成clean目标文件。而clean后是没有任何依赖文件的,所以每次make clean后clean目标文件都会被认为是最新,而不去执行下面的命令,这虽然符合语法规则,但并不能达到我们使用clean的目的。

因此我们必须将clean这种目标定义成伪目标。定义方法为:.PHONY:all。这样不管该目录下是否有同名的文件都会执行clean后的命令。现在你应该明被为什么MAKEFILE文件中有这么多以.PHONY开头的目标文件了吧。

4.为什么要用makefile

内核模块化简单实用,但是编译却成了问题:有时候我们只是改动了某个文件的一小部分就不得不编译整个内核,这是个很可怕的事情。但是GNU make引入后,这个问题就迎刃而解了:make只会编译已被改动代码的文件,而不是将所有文件都编译。但是make具体如何对源文件进行编译,怎么编译?这个时候就需要makefile文件了。在之前的文章当中,我们对Makefile文件下过“编译规则”这样的定义,下面通过分析上面的Makefile文件,我们具体感受一下这个“编译规则”。

整个Makefile文件根据KERNELRELEASE的值来划分不同的编译规则(方式),这里的KERNELRELEASE只会在内核源码目录下显示当前内核的版本号。

一般情况下,我们编写的内核模块源文件所在的目录并非位于内核源码根目录(或其子目录)下。那么此时就不符合ifueq条件,即执行else语句下的编译规则。这种情况下,当我们输入make后,就会执行make -C $(KDIR) M=$(PWD) modules这条命令。注意这条命令后的modules,它表示将会编译所有在配置菜单中被选作模块编译的那些内容(也就是赋值给obj-m的那些目标)。接下来由于-C $(KDIR)参数的原因,make会转向内核源码根目录下去执行。根据M后的目录,编译我们写的内核模块源码,生成.o文件。接着联合一些中间文件生成.ko文件。这便是make生成整个内核目标文件的过程。这个过程可以在make之后在终端产生的一系列描述文字得到。

上面这种情况会将我们编写的内核模块源码编译成内核模块目标文件,接下来就是我们熟悉的内核模块插入了。不过当我们所写的内核模块文件处于内核源码目录下时,KERNELRELEASE就会非空(此时为版本号),那么此时就满足ifueq条件了。什么时候我们编写的内核模块源码会处在内核源码目录下?此时的内核编译是那种方式?

在前面的文章中我们假设已经写好了驱动代码,然后在Kconfig文件中为这个驱动编写配置选项。在配置菜单中有了此驱动的相关配置选项后,接下来用户可以选择是否会将此驱动源码一起编译进内核。那么此时Makefile文件的作用就是将此内核模块源码编译进内核(obj-m := $(TARGET).o)。不过注意,只是通知内核下次编译“带上我”,并没有实际编译。

现在应该明白整个Makefile文件的逻辑结构了吧?

Git快速入门(Q&A)

26 9 月, 2010 by edsionte 5 comments »

如果你对于git完全不知,那么不用着急,我们一起来从零开始学习。

1.难道git就是傻瓜的意思吗?

git是一个版本控制系统,也叫做傻瓜内容跟踪器。按照我目前的理解是:一个项目团队共同完一个项目,而且团队里的每个人都深处异地,那么如何将每个人负责的代码融合在一起?而且如何清晰的展现不同的人在不同的地方在不同的时间里对代码做过修改?这时候,git派上用场了。

事实上,Linux内核正是使用git进行管理的,而git的创始人也正是linux之父:Linus Torvalds。

2.我如何才能获得git?

想使用git,肯定必须安装git了。如果你在ubuntu下,使用下面的命令可以快速安装:sudo apt-get install git-core。安装完成后,可使用git –version查看git的版本号。

3.我在安装完git后该做些什么?

首先你得向git介绍你自己,告诉它你的姓名和邮箱吧。

git config --global user.name "edsionte"
git config --global user.email "edsionte@gmail.com"

4.我依然可以通过男人手册获得git的帮助吗?

当然可以,男人手册是强大的。比如我们可以通过下面的命令获得git中commit命令的相关帮助。

man git-commit

5.我如何开启一个新的管理项目?

比如你要管理的项目目录为mywork,该目录下有一个文件hello.c.进入到该目录,通过git init命令就可以创建一个版本库。hello.c的内容如下:

edsionte@edsionte-desktop:~/mywork$ cat hello.c
#include “stdio.h”

int main()
{
	printf("hello,git!\n");
	printf("I can use git!\n");
	return 0;
}

6.如何将我的文件加入到版本库?

比如我要将hello.c文件加入到我的版本库,那么我可以这样:

git add hello.c
git commit

第一条命令只是将hello.c文件增加到版本库的索引当中,而第二条命令才真正的将hello.c文件的内容提交到版本库中,并且当你执行第二条命令的时候会在终端出现新的界面,在这个界面中你可以输入与该文件相关的开发日志等,比如我们输入“The version is: 0.0.1”。注意,要在这个界面下进行相关操作必须以ctrl组合相应的字母。

事实上第二条命令我们可以这样处理:

git commit -m "The version is: 0.0.1"

即直接在命令中输入开发日志信息。

每当你改进原有文件的代码时,你都可以按照上面的指令将更新后的内容添加到版本库中。不过,当我们只是更新了原有文件的内容而不是新增文件或者目录时,就可以用下面的命令代替以上工作:

git commit -a

7.在我对某个(些)文件进行了修改之后,我如何查看新旧文件的差异?

这是一个好问题。不过在回答这个问题之前,请先回顾如何将一个文件更新到版本库:先修改文件,再add,再commit。OK,那么接下来的几个指令的使用时间就很清晰了。

在add之前,可以使用git diff命令来查看文件被更新前后的差异。

在coomit之前,可以使用git diff –cached命令来查看文件被更新前后的差异。

以上命令可以查看文件内容的具体差异。那么在coomit之前,如何查看那些文件被修改过?git status即可做到。

8.既然我记录了开发日志,那么我如何查看它?

git log

9.什么是分支?分支有何用?

以上我们的操作均是在主分支master中进行的。有时候我们需要创建一个个人分支,以避免对主分支的影响;有时候我们需要一个临时性的分支去完成一些实验性的工作;当出现以上情况时,我们就需要创建一个分支。

10.如何创建一个分支?对于这个分支我能做些什么?

如果我想创建temp分支,那么可以通过git branch temp来完成。 通过git branch命令可以查看所以分支,比如:

edsionte@edsionte-desktop:~/mywork$ git branch
* master
  slave
  temp

而且*标志表示当前你所在的分支。如果你想进入temp分支,那么通过git checkout temp命令完成。接下来你再temp分支中创建新文件或者修改原有文件都不会影响主分支。

11.到目前为止,我认为temp分支完全正确,那么如何将它合并到主分支?

在这之前你要确认一下temp分支和master分支中现在存在那些文件。在master分支中有一个hello.c文件。在temp分支中当然也存在hello.c文件,不过被修改成如下内容:

edsionte@edsionte-desktop:~/mywork$ cat hello.c
#include “stdio.h”

int main()
{
	printf("hello,git!\n");
	printf("I can use git!\n");
	printf("I love git!\n");
	return 0;
}

此外,在temp分支中还新建了hi.c文件。使用git commit -a命令向版本库中提交了hello.c文件后,还必须add和commit来提交新建的hi.c文件,完成后切换到master分支。接着使用git merge temp命令就可将temp分支合并到主分支了。查看hello.c文件,是不是已经更新了?而且还新增了hi.c文件。

edsionte@edsionte-desktop:~/mywork$ git merge temp
Merge made by recursive.
 hello.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

12.虽然你说的很轻松,可是我在试验的时候还是出现了诸如下面的错误,这是怎么回事?

edsionte@edsionte-desktop:~/mywork$ git merge temp
Auto-merging hello.c
CONFLICT (content): Merge conflict in hello.c
Automatic merge failed; fix conflicts and then commit the result.

如果出现这样的情况,我们可以看一下master分支下的hello.c文件:

edsionte@edsionte-desktop:~/mywork$ cat hello.c
#include “stdio.h”

int main()
{
	printf("hello,git!\n");
<<<<<<< HEAD ======= 	printf("I can use git!\n"); >>>>>>> temp
	return 0;
}

“====”符号是出现混淆内容的分割线。“<<<<<<< HEAD”与”====”之间的语句段为空,而”====”与“>>>>>>> temp”之间的语句段为printf(“I can use git!\n”);。出现这种情况是因为git不能辨别这两段语句的前后关系,这时候需要我们人为来调整分支文件的内容再合并。

13.接下来我还能做些什么?

以上的内容只是git的简单入门教程,更多的内容可以参考git男人手册

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key