从串口驱动到Linux驱动模型

大学的时候,帮朋友写的操做系统调研的做业,最近整理过去的文档时候偶然发现,遂做为博客发出来。linux



                   从串口驱动到Linux的tty子系统驱动模型简要分析程序员

                                                                                    基于ARM920T核心算法

                                                                           Samsung的S3C2440芯片shell

         本文经过对Linux下串口驱动的分析。由最上层的C库。到操做系统系统调用层的封装。再到tty子系统的核心。再到一系列线路规程。再到最底层的硬件操做。对Linux中的tty子系统进行简要的说明。从理论到实践。以便读者能对OS原理有更深刻的了解和更具体的掌握。数据库

         在具体分析以前。咱们必须对串口。驱动。和Linux操做系统有必定的了解。这一阶段咱们有三个问题须要解决:编程

1.什么是Linux操做系统。windows

2.什么是Linux设备驱动。缓存

3.关于串口的种种。安全

要了解这些概念。以下我介绍了一点这方面的知识。不过遗憾的是对一些概念有着不可避免的向前引用。这个过程当中我会尽可能忽略次要因素。以在本次调研中最主要目的为主线。若是读者您对这些概念已经有很深刻的理解。能够直接阅读后面的代码分析:bash

         1.什么是Linux操做系统 ?

Linux是一套无偿使用和自由传播的类Unix操做系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操做系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操做系统。

Linux操做系统诞生于1991 年10 月5 日(这是第一次正式向外公布时间)。Linux存在着许多不一样的Linux版本,但它们都使用了Linux内核。Linux具有惊人的可移植性。可安装在各类计算机硬件设备中,好比手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。

严格来说,Linux这个词自己只表示Linux内核,但实际上人们已经习惯了用Linux来形容整个基于Linux内核,而且使用GNU 工程各类工具和数据库的操做系统。

在这几个简要的段落中。有很多新的名词被引入了进来。下面我对几个重要的概念进行描述。

A.关于类UNIX系统

类Unix系统(英文:Unix-like)指各类传统的Unix系统(好比FreeBSD、OpenBSD、SUN公司的Solaris)以及各类与传统Unix相似的系统(例如Minix、Linux、QNX等)。它们虽然有的是自由软件,有的是商业软件,但都至关程度地继承了原始UNIX的特性,有许多类似处,而且都在必定程度上遵照POSIX规范。

这个在一些经典的操做系统教科书中已经做了说明。咱们仅需知道。它和咱们熟知的Windows系列操做系统同样。都是一种现代操做系统。对底层的计算机资源进行抽象。对上层用户提供调用接口。完成计算机应该完成的功能。

B.关于可移植性

可移植性指与软件从某一环境转移到另外一环境下的难易程度。为得到较高的可移植性,在设计过程当中常采用通用的程序设计语言和运行支撑环境。尽可能不用与系统的底层相关性强的语言。

可移植性是软件质量之一,良好的可移植性能够提升软件的生命周期。代码的可移植性主题是软件;可移植性是软件产品的一种能力属性,其行为表现为一种程度,而表现出来的程度与环境密切相关。

一个操做系统的可移植性每每表如今它能在运行在不一样的体系结构上。感性的理解就是能够支持的设备有不少。好比前文所说的,Linux能够运行在大型服务器上。各类平板电脑上。前段时间有黑客成功的把Linux移植到一个佳能照相机上。而且在这个照相机上运行了一些主流的软件。能够说。只要有足够能够利用的硬件资源。就能够把Linux移植到这个硬件平台上去。这个资源的最低要求每每很低。这能够与对硬件资源要求很高的Windows有一个鲜明的对比。举个例子就是。当Windows 10的升级提示从你计算机的右下角弹出时。你能够不假思索的点击‘立刻升级’吗?我想大多数人对这个问题的答案是否认的。为何?由于大多数状况下。升级以后就会变得更卡。延迟更大。一些无用而庞大的软件疯狂的占用你有限的计算机资源。而若是你选择的是Linux。你几乎能够任意的在计算机上安装软件。运行程序(若是你的内存不是过小。且硬盘交换分区足够的话)。Linux核心已经将有限的硬件资源发挥到了极致。开源软件良好的模块化设计在各个层次上充分利用了程序的局部性原理。(固然这是在损失了必定易用性的前提下的。)。很差意思我扯远了。这些不是本文的重点。。

因为笔者没有土豪到有不少计算机。因此选择了一款比较便宜的ARM9开发板做为开发平台。它的CPU是三星公司生产的S3C2440。核心是ARM920T。

C.关于Linux的基本思想

Linux的基本思想有两点:

第一.  一切都是文件。系统中的全部都归结为一个文件,包括命令、硬件和软件设备、操做系统、进程等等对于操做系统内核而言,都被视为拥有各自特性或类型的文件。至于说Linux是基于Unix的,很大程度上也是由于这二者的基本思想十分相近

第二.  每一个软件都有肯定的用途。。[3]

D.关于Linux的特色

彻底免费

Linux是一款免费的操做系统,用户能够经过网络或其余途径免费得到,并能够任意修改其源代码。这是其余的操做系统所作不到的。正是因为这一点,来自全世界的无数程序员参与了Linux的修改、编写工做,程序员能够根据本身的兴趣和灵感对其进行改变,这让Linux吸取了无数程序员的精华,不断壮大。

彻底兼容POSIX1.0标准

这使得能够在Linux下经过相应的模拟器运行常见的DOS、Windows的程序。这为用户从Windows转到Linux奠基了基础。许多用户在考虑使用Linux时,就想到之前在Windows下常见的程序是否能正常运行,这一点就消除了他们的疑虑。

多用户、多任务

Linux支持多用户,各个用户对于本身的文件设备有本身特殊的权利,保证了各用户之间互不影响。多任务则是如今电脑最主要的一个特色,Linux可使多个程序同时并独立地运行。

良好的界面

Linux同时具备字符界面和图形界面。在字符界面用户能够经过键盘输入相应的指令来进行操做。它同时也提供了相似Windows图形界面的X-Window系统,用户可使用鼠标对其进行操做。在X-Window环境中就和在Windows中类似,能够说是一个Linux版的Windows。

支持多种平台

Linux能够运行在多种硬件平台上,如具备x8六、680x0、SPARC、Alpha等处理器的平台。此外Linux仍是一种嵌入式操做系统,能够运行在掌上电脑、机顶盒或游戏机上。2001年1月份发布的Linux 2.4版内核已经可以彻底支持Intel 64位芯片架构。同时Linux也支持多处理器技术。多个处理器同时工做,使系统性能大大提升。

文件类型

普通文件(regular file):就是通常存取的文件,由ls-al显示出来的属性中,第一个属性为 [-],例如 [-rwxrwxrwx]。另外,依照文件的内容,又大体能够分为:

一、纯文本文件(ASCII):这是Unix系统中最多的一种文件类型,之因此称为纯文本文件,是由于内容能够直接读到的数据,例如数字、字母等等。设置文件几乎都属于这种文件类型。举例来讲,使用命令“cat ~/.bashrc”就能够看到该文件的内容(cat是将文件内容读出来)。

二、二进制文件(binary):系统其实仅认识且能够执行二进制文件(binary file)。Linux中的可执行文件(脚本,文本方式的批处理文件不算)就是这种格式的。举例来讲,命令cat就是一个二进制文件。

三、数据格式的文件(data):有些程序在运行过程当中,会读取某些特定格式的文件,那些特定格式的文件能够称为数据文件(data file)。举例来讲,Linux在用户登入时,都会将登陆数据记录在 /var/log/wtmp文件内,该文件是一个数据文件,它能经过last命令读出来。但使用cat时,会读出乱码。由于它是属于一种特殊格式的文件。

四、目录文件(directory):就是目录,第一个属性为[d],例如 [drwxrwxrwx]。

链接文件(link):相似Windows下面的快捷方式。第一个属性为 [l],例如 [lrwxrwxrwx]。

五、设备与设备文件(device):与系统外设及存储等相关的一些文件,一般都集中在 /dev目录。一般又分为两种:

块设备文件:就是存储数据以供系统存取的接口设备,简单而言就是硬盘。例如一号硬盘的代码是 /dev/hda1等文件。第一个属性为 [b]。

字符设备文件:即串行端口的接口设备,例如键盘、鼠标等等。第一个属性为 [c]。

六、套接字(sockets):这类文件一般用在网络数据链接。能够启动一个程序来监听客户端的要求,客户端就能够经过套接字来进行数据通讯。第一个属性为 [s],最常在 /var/run目录中看到这种文件类型。

七、管道(FIFO,pipe):FIFO也是一种特殊的文件类型,它主要的目的是,解决多个程序同时存取一个文件所形成的错误。FIFO是first-in-first-out(先进先出)的缩写。第一个属性为 [p]

文件结构

/:根目录,全部的目录、文件、设备都在/之下,/就是Linux文件系统的组织者,也是最上级的领导者。

/bin:bin 就是二进制(binary)英文缩写。在通常的系统当中,均可以在这个目录下找到linux经常使用的命令。系统所须要的那些命令位于此目录。

/boot:Linux的内核及引导系统程序所须要的文件目录,好比 vmlinuz initrd.img 文件都位于这个目录中。在通常状况下,GRUB或LILO系统引导管理器也位于这个目录。

/cdrom:这个目录在刚刚安装系统的时候是空的。能够将光驱文件系统挂在这个目录下。例如:mount /dev/cdrom /cdrom

/dev:dev 是设备(device)的英文缩写。这个目录对全部的用户都十分重要。由于在这个目录中包含了全部linux系统中使用的外部设备。可是这里并非放的外部设备的驱动程序。这一点和经常使用的windows,dos操做系统不同。它其实是一个访问这些外部设备的端口。能够很是方便地去访问这些外部设备,和访问一个文件,一个目录没有任何区别。

/etc:etc这个目录是linux系统中最重要的目录之一。在这个目录下存放了系统管理时要用到的各类配置文件和子目录。要用到的网络配置文件,文件系统,x系统配置文件,设备配置信息,设置用户信息等都在这个目录下。

/home:若是创建一个用户,用户名是"xx",那么在/home目录下就有一个对应的/home/xx路径,用来存放用户的主目录。

/lib:lib是库(library)英文缩写。这个目录是用来存放系统动态链接共享库的。几乎全部的应用程序都会用到这个目录下的共享库。所以,千万不要轻易对这个目录进行什么操做,一旦发生问题,系统就不能工做了。

/lost+found:在ext2或ext3文件系统中,当系统意外崩溃或机器意外关机,而产生一些文件碎片放在这里。当系统启动的过程当中fsck工具会检查这里,并修复已经损坏的文件系统。有时系统发生问题,有不少的文件被移到这个目录中,可能会用手工的方式来修复,或移到文件到原来的位置上。

/mnt:这个目录通常是用于存放挂载储存设备的挂载目录的,好比有cdrom等目录。能够参看/etc/fstab的定义。

/media:有些linux的发行版使用这个目录来挂载那些usb接口的移动硬盘(包括U盘)、CD/DVD驱动器等等。

/opt:这里主要存放那些可选的程序。

/proc:能够在这个目录下获取系统信息。这些信息是在内存中,由系统本身产生的。

/root:Linux超级权限用户root的家目录。

/sbin:这个目录是用来存放系统管理员的系统管理程序。大可能是涉及系统管理的命令的存放,是超级权限用户root的可执行命令存放地,普通用户无权限执行这个目录下的命令,这个目录和/usr/sbin; /usr/X11R6/sbin或/usr/local/sbin目录是类似的,凡是目录sbin中包含的都是root权限才能执行的。

/selinux :对SElinux的一些配置文件目录,SElinux可让linux更加安全。

/srv 服务启动后,所需访问的数据目录,举个例子来讲,www服务启动读取的网页数据就能够放在/srv/www中

/tmp:临时文件目录,用来存放不一样程序执行时产生的临时文件。有时用户运行程序的时候,会产生临时文件。/tmp就用来存放临时文件的。/var/tmp目录和这个目录类似。

/usr

这是linux系统中占用硬盘空间最大的目录。用户的不少应用程序和文件都存放在这个目录下。在这个目录下,能够找到那些不适合放在/bin或/etc目录下的额外的工具

/usr/local:这里主要存放那些手动安装的软件,即不是经过“新立得”或apt-get安装的软件。它和/usr目录具备相相似的目录结构。让软件包管理器来管理/usr目录,而把自定义的脚本(scripts)放到/usr/local目录下面、。

/usr/share :系统共用的东西存放地,好比/usr/share/fonts 是字体目录,/usr/share/doc和/usr/share/man帮助文件。

/var:这个目录的内容是常常变更的,看名字就知道,能够理解为vary的缩写,/var下有/var/log 这是用来存放系统日志的目录。/var/ www目录是定义Apache服务器站点存放目录;/var/lib 用来存放一些库文件,好比MySQL的,以及MySQL数据库的的存放地。

         如上。相信读者已经对Linux操做系统有了一个概观。对于一些具体命令。笔者决定须要用到的时候再作说明。如今咱们来看看第二个概念:

         2.什么是Linux设备驱动

设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工做方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA通讯,进行物理内存向虚拟内存的映射等,最终让通讯设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据。

Linux设备驱动是对底层硬件资源的抽象。对上层的操做系统其余服务提供一个良好的接口。让其余服务能够把一个特定的硬件。或是一种机制当作一个文件使用。使用通用的系统调用进行调用。

3.关于串口的种种。

         众所周知。咱们如今的计算机上面有不少接口。如USB。网口。并口等。串口总线是其中的一个。串行接口简称串口,也称串行通讯接口或串行通信接口(一般指COM接口),是采用串行通讯方式的扩展接口。串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特色是通讯线路简单,只要一对传输线就能够实现双向通讯(能够直接利用电话线做为传输线),从而大大下降了成本,特别适用于远距离通讯,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通信方式称为串行通信。串行通信的特色是:数据位的传送,按位顺序进行,最少只需一根传输线便可完成;成本低但传送速度慢。串行通信的距离能够从几米到几公里;根据信息的传送方向,串行通信能够进一步分为单工、半双工和全双工三种。

串口通讯的两种最基本的方式:同步串行通讯方式和异步串行通讯方式。

同步串行是指SPI(SerialPeripheral interface)的缩写,顾名思义就是串行外围设备接口。SPI总线系统是一种同步串行外设接口,它可使MCU与各类外围设备以串行方式进行通讯以交换信息,TRM450是SPI接口。

异步串行是指UART(UniversalAsynchronous Receiver/Transmitter),通用异步接收/发送。UART是一个并行输入成为串行输出的芯片,一般集成在主板上。UART包含TTL电平的串口和RS232电平的串口。 TTL电平是3.3V的,而RS232是负逻辑电平,它定义+5~+12V为低电平,而-12~-5V为高电平,MDS27十、MDS SD四、EL805等是RS232接口,EL806有TTL接口。

串行接口按电气标准及协议来分包括RS-232-C、RS-42二、RS485等。

RS-232

也称标准串口,最经常使用的一种串行通信接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通信的标准。它的全名是“数据终端设备(DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准”。传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座(DB25),后来使用简化为9芯D型插座(DB9),如今应用中25芯插头座已不多采用。

RS-232采起不平衡传输方式,即所谓单端通信。因为其发送电平与接收电平的差仅为2V至3V左右,因此其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通信而设计的,其驱动器负载为3~7kΩ。因此RS-232适合本地设备之间的通讯。

RS-422

标准全称是“平衡电压数字接口电路的电气特性”,它定义了接口电路的特性。典型的RS-422是四线接口。实际上还有一根信号地线,共5根线。其DB9链接器引脚定义。因为接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故容许在相同传输线上链接多个接收节点,最多可接10个节点。即一个主设备(Master),其他为从设备(Slave),从设备之间不能通讯,因此RS-422支持点对多的双向通讯。接收器输入阻抗为4k,故发端最大负载能力是10×4k+100Ω(终接电阻)。RS-422四线接口因为采用单独的发送和接收通道,所以没必要控制数据方向,各装置之间任何须须的信号交换都可以按软件方式(XON/XOFF握手)或硬件方式(一对单独的双绞线)实现。

RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率如下,才可能达到最大传输距离。只有在很短的距离下才能得到最高速率传输。通常100米长的双绞线上所能得到的最大传输速率仅为1Mb/s。

RS-485

是从RS-422基础上发展而来的,因此RS-485许多电气规定与RS-422相仿。如都采用平衡传输方式、都须要在传输线上接终接电阻等。RS-485能够采用二线与四线方式,二线制可实现真正的多点双向通讯,而采用四线链接时,与RS-422同样只能实现点对多的通讯,即只能有一个主(Master)设备,其他为从设备,但它比RS-422有改进,不管四线仍是二线链接方式总线上可多接到32个设备。

RS-485与RS-422的不一样还在于其共模输出电压是不一样的,RS-485是-7V至+12V之间,而RS-422在-7V至+7V之间,RS-485接收器最小输入阻抗为12kΩ、RS-422是4kΩ;因为RS-485知足全部RS-422的规范,因此RS-485的驱动器能够在RS-422网络中应用。

RS-485与RS-422同样,其最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率如下,才可能使用规定最长的电缆长度。只有在很短的距离下才能得到最高速率传输。通常100米长双绞线最大传输速率仅为1Mb/s。

笔者采用的RS-232串口通讯协议。下面对其通讯接线方法作简要说明。目前较为经常使用的串口有9针串口(DB9)和25针串口(DB25),通讯距离较近时(<12m),能够用电缆线直接链接标准RS232端口(RS422,RS485较远),若距离较远,需附加调制解调器(MODEM)或其余相关设备。最为简单且经常使用的是三线制接法,即地、接收数据和发送数据三脚相连,这是最为基本的接法,且直接用RS232相连。

RS232C串口通讯接线方法(三线制)

首先,串口传输数据只要有接收数据针脚和发送针脚就能实现:同一个串口的接收脚和发送脚直接用线相连,两个串口相连或一个串口和多个串口相连

同一个串口的接收脚和发送脚直接用线相连对9针串口和25针串口,均是2与3直接相连

两个不一样串口(不管是同一台计算机的两个串口或分别是不一样计算机的串口)

串口连机线

DB9-DB9

2-3,3-2,5-5

DB25-DB25

2-3,3-2,7-7

DB9-DB25

2-3,3-2,5-7

DB9-DB25串口转接线

2-3,3-2,5-7

上面是对微机标准串行口而言的,还有许多非标准设备,不作说明。

         好了。到此为止咱们已经解决了一开始的三个问题。让咱们进入实际的代码。实际的硬件来进行分析。

         在一个硬件平台上。硬件是可用的。咱们必需要烧写适当的软体到平台的RAM中。这样CPU才能跳转到最早的指令。而后慢慢加载各类资源。才能完成系统的自举。

         通常咱们采用BootLoader进行硬件的初始化。并引导至操做系统核心。

         笔者采用的BootLoader是u-Boot-1.1.16。Uboot是一个众所周知的开源软件。读者仅需了解它起到了BootLoader的做用便可。这里很少作解释。仅对串口的链接和程序的下载做简要说明。






如图所示。将UBOOT目录下的u-boot.bin下载到开发平台上。在Windows打开设备管理器。选择端口。从而找到正确的com口号。在此以前确保开发板的串口与笔记本的USB口链接。(由于如今笔记本都没有并口了。因此只能采用USB转串口线。搭配开发板上的电平转换芯片来完成串口链接目的。)





而后咱们再使用一个工具。即SecureCRT。找到对应的com号。完成快速连接。波特率选择115200。取消流控。





若是一切顺利。在笔记本上就能够看到串口的相似下面的输出。这就是传说中的串口控制台。。





这个串口的指令功能是由Uboot自己完成的。并非linux下的串口驱动。

引入此图旨在让读者感性的认识到串口控制台的功能是什么。

下面正式开始对串口打开。发送。接收函数的分析。这里向前引用一个函数。就是linux内核中几种2440芯片通用的串口发送函数s3c24xx_serial_start_tx。函数声明为static voids3c24xx_serial_start_tx(struct uart_port *port):函数定义在./linux/driver/tty/serial/samsung.c中。

好了。咱们从这个目录结构开始。说明大概的tty子系统驱动模型。

首先。最前面的linux是内核代码的根目录。如图所示。





至此。咱们面临一个问题。linux内核是什么。

Linux内核是什么?

Linux是一种开源电脑操做系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操做系统。

Linux最先是由芬兰黑客Linus Torvalds为尝试在英特尔x86架构上提供自由免费的类Unix操做系统而开发的。该计划开始于1991年,在计划的早期有一些Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。

Linux是一个一体化内核(monolithickernel)系统。“内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。一个内核不是一套完整的操做系统。一套基于Linux内核的完整操做系统叫做Linux操做系统,或是GNU/Linux。设备驱动程序能够彻底访问硬件。Linux内的设备驱动程序能够方便地以模块化(modularize)的形式设置,并在系统运行期间可直接装载或卸载。

操做系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体,它们互相依赖,不可分割。计算机的硬件,含有外围设备、处理器、内存、硬盘和其余的电子设备组成计算机的发动机。可是没有软件来操做和控制它,自身是不能工做的。完成这个控制工做的软件就称为操做系统,在Linux的术语中被称为“内核”,也能够称为“核心”。Linux内核的主要模块(或组件)分如下几个部分:存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通讯,以及系统的初始化(引导)、系统调用等。

系统调用接口

SCI 层提供了某些机制执行从用户空间到内核的函数调用。正如前面讨论的同样,这个接口依赖于体系结构,甚至在相同的处理器家族内也是如此。SCI 其实是一个很是有用的函数调用多路复用和多路分解服务。在 ./linux/kernel 中您能够找到 SCI 的实现,并在 ./linux/arch 中找到依赖于体系结构的部分。

进程管理

进程管理的重点是进程的执行。在内核中,这些进程称为线程,表明了单独的处理器虚拟化(线程代码、数据、堆栈和 CPU寄存器)。在用户空间,一般使用进程这个术语,不过 Linux 实现并无区分这两个概念(进程和线程)。内核经过 SCI 提供了一个应用程序编程接口(API)来建立一个新进程(fork、exec 或 Portable Operating System Interface [POSⅨ] 函数),中止进程(kill、exit),并在它们之间进行通讯和同步(signal 或者 POSⅨ机制)。

进程管理还包括处理活动进程之间共享 CPU的需求。内核实现了一种新型的调度算法,无论有多少个线程在竞争 CPU,这种算法均可以在固定时间内进行操做。这种算法就称为 O⑴调度程序,这个名字就表示它调度多个线程所使用的时间和调度一个线程所使用的时间是相同的。O⑴调度程序也能够支持多处理器(称为对称多处理器或 SMP)。您能够在 ./linux/kernel 中找到进程管理的源代码,在 ./linux/arch 中能够找到依赖于体系结构的源代码。

内存管理

内核所管理的另一个重要资源是内存。为了提升效率,若是由硬管理虚拟内存,内存是按照所谓的内存页方式进行管理的(对于大部分体系结构来讲都是 4KB)。Linux 包括了管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。

不过内存管理要管理的可不止 4KB缓冲区。Linux 提供了对 4KB缓冲区的抽象,例如 slab 分配器。这种内存管理模式使用 4KB缓冲区为基数,而后从中分配结构,并跟踪内存页使用状况,好比哪些内存页是满的,哪些页面没有彻底使用,哪些页面为空。这样就容许该模式根据系统须要来动态调整内存使用。

为了支持多个用户使用内存,有时会出现可用内存被消耗光的状况。因为这个缘由,页面能够移出内存并放入磁盘中。这个过程称为交换,由于页面会被从内存交换到硬盘上。内存管理的源代码能够在 ./linux/mm 中找到。

虚拟文件系统

虚拟文件系统(VFS)是 Linux 内核中很是有用的一个方面,由于它为文件系统提供了一个通用的接口抽象。VFS 在 SCI 和内核所支持的文件系统之间提供了一个交换层。

VFS 在用户和文件系统之间提供了一个交换层

在 VFS 上面,是对诸如 open、close、read 和 write 之类的函数的一个通用 API 抽象。在 VFS 下面是文件系统抽象,它定义了上层函数的实现方式。它们是给定文件系统(超过 50 个)的插件。文件系统的源代码能够在 ./linux/fs 中找到。

文件系统层之下是缓冲区缓存,它为文件系统层提供了一个通用函数集(与具体文件系统无关)。这个缓存层经过将数据保留一段时间(或者随即预先读取数据以便在须要是就可用)优化了对物理设备的访问。缓冲区缓存之下是设备驱动程序,它实现了特定物理设备的接口。

好了。相信读者已经对linuxkernel 有了一个概观。下面咱们继续分析这个路径背后表明的模型结构。(./linux/driver/tty/serial/samsung.c)

driver是驱动程序的目录。如图所示。





前文对linux设备驱动程序有了一个大概的描述。下面咱们具体看一下linux下的驱动

纵览linux/drivers目录,大概还有35个以上的子目录,每一个子目录基本上就表明了一种设备驱动,有atm、block、char、misc、input、net、usb、sound、video等。这里只描述在嵌入式系统里面用得最为普遍的3种设备。

1.字符设备(char device)

字符设备是Linux最简单的设备,能够像文件同样访问。初始化字符设备时,它的设备驱动程序向Linux登记,并在字符设备向量表中增长一个device_struct数据结构条目,这个设备的主设备标识符用作这个向量表的索引。一个设备的主设备标识符是固定的。chrdevs向量表中的每个条目,一个device_struct数据结构,包括两个元素:一个登记设备驱动程序名称的指针和一个指向一组文件操做的指针。能够参考的代码是include/linux/ major.h。

通常来讲像鼠标、串口、键盘等设备都属于字符设备。

2.块设备(block device)

块设备是文件系统的物质基础,它也能够像文件同样被访问。Linux用blkdevs向量表维护已经登记的块设备文件。它像chrdevs向量表同样,使用设备的主设备号做为索引。它的条目也是device_struct数据结构。与字符设备不一样的是,块设备分为SCSI类和IDE类。向Linux内核登记并向核心提供文件操做。一种块设备类的设备驱动程序向这种类提供和类相关的接口。能够参考的代码是fs/devices.c。

每个块设备驱动程序必须提供普通的文件操做接口和对于buffer cache的接口。每个块设备驱动程序填充blk_dev向量表中的blk_dev_struct数据结构。此向量表的索引是设备的主设备号。其中blk_dev_struct数据结构包括一个请求例程的地址和一个指针,指向一个request数据结构的列表,每个都表达buffer cache向设备读/写一块数据的一个请求。

能够参考的源代码是drivers/block/ll_rw_blk.c和include/linux/blkdev.h。

当buffer cache从一个已登记的设备读/写一块数据,或者但愿读、写一块数据到其余位置时,就在blk_dev_struct中增长一个request数据结构。每一个request数据结构都有一个指向一个或多个buffer_head数据结构的指针,每个都是读/写一块数据的请求。若是buffer_head数据结构被锁定(buffer_cache),可能会有一个进程在等待这个缓冲区的阻塞进程完成。每个request数据结构都是从all_request表中分配的。若是request增长到空的request列表中,就调用驱动程序的request函数处理这个request队列,不然驱动程序只是简单地处理request队列中的每个请求。

块设备驱动程序和字符设备驱动程序的主要区别是:在对字符设备发出读、写请求时,实际的硬件I/O通常紧接着就发生了,块设备则否则,它利用一块系统内存做为缓冲区,当用户进程对设备请求能知足用户的要求时,就返回请求的数据,若是不能就调用请求函数来进行实际的I/O操做。块设备是主要针对磁盘等慢速设备的,以避免耗费过多的CPU时间来等待。

块设备主要有硬盘、光盘驱动器等。能够查看文件/proc/devices得到。

3.网络设备(net device)

网络设备在系统中的做用相似于一个已挂载的块设备。块设备将本身注册到blk_dev数据及其余内核结构中,而后经过本身的request函数在发生请求时传输和接收数据块,一样网络设备也必须在特定的数据结构中注册本身,以便与外界交换数据包时被调用。网络设备在Linux里作专门的处理。Linux的网络系统主要是基于BSD UNIX的Socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。

4.杂项设备(misc device)

杂项设备也是在嵌入式系统中用得比较多的一种设备驱动,在第11章里面介绍的sub LCD和弦芯片的驱动等都是采用 misc device 的驱动方式实现的。在 Linux 内核的include\linux目录下有Miscdevice.h文件,要把本身定义的misc device从设备定义在这里。实际上是由于这些字符设备不符合预先肯定的字符设备范畴,全部这些设备采用主编号10,一块儿归于misc device,其实misc_register就是用主标号10调用register_chrdev()的。

         这是driver目录下的分类。咱们主要调研的串口驱动。属于TTY子系统。因此咱们cd到tty目录下。ls显示里面的文件。如图所示。





下面对linux内核tty设备作一点简要说明。

         tty一词源于Teletypes,或Teletypewriters,它是最先出现的一种终端设备,相似电传打字机,由Teletype公司生产。最初tty是指链接到Unix系统上的物理或者虚拟终端。终端是一种字符型设备,一般使用tty来统称各类类型的终端设备。随着时间的推移,当经过串行口可以创建起终端链接后,这个名字也用来指任何的串口设备。它还有多种类,例如串口(ttySn、ttySACn、ttyOn)、USB到串口的转换器(ttyUSBn),还有须要特殊处理才能正常工做的调制解调器(好比传统的WinModem类设备)等。tty虚拟设备支持虚拟控制台,它能经过键盘及网络链接或者经过xterm会话登陆到计算机上。

        其实起初终端和控制台都不是我的电脑的概念,而是多人共用的小型中型大型计算机上的概念。终端为主机提供了人机接口,每一个人都经过终端使用主机的资源。终端有字符终端和图形终端两种。一台主机能够连不少终端。控制台是一种特殊的人机接口, 是人控制主机的第一人机接口。而主机对于控制台的信任度高于其余终端。对此还能够结合内核启动代码中init进程打开/dev/console和执行两次sys_dup(0),以及标准输入、标准输出、标准出错,还有就是进程fork后的标准输入输出的复制状况来一块儿理解。而我的计算机只有控制台,没有终端。固然愿意的话,能够在串口上连一两台字符哑终端。可是linux按POSIX标准把我的计算机当成小型机来用,在控制台上经过getty软件虚拟了六个字符哑终端(或者叫虚拟控制台终端tty1-tty6)(数量能够在/etc/inittab里本身调整)和一个图型终端, 在虚拟图形终端中又能够经过软件(如rxvt)再虚拟无限多个伪终端(pts/0等)。但这全是虚拟的,虽然用起来同样,但实际上没有物理实体。因此在我的计算机上,只有一个实际的控制台,没有终端,全部终端都是在控制台上用软件模拟的。要把我的计算机当主机再经过串口或网卡外连真正的物理终端也能够,论成本,呵呵。谁会怎么作呢。

终端按照其自身能力分类,能够分为:

一、哑终端(瘦客户端)

早期的计算机终端是经过串行RS-232通讯的,它只能解释有限数量的控制码(CR,LF等),但没有能力处理执行特殊的转义序列功能(如清行、清屏或控制光标的位置)。简单来讲就是处理能力有限的终端机,他们通常基本上只具备和机械电传打字机相似的有限功能。这种类型的终端称为哑终端。如今仍然在现代类Unix系统上获得支持,经过设置环境变量TERM=dumb。哑终端有时用来指任何类型的经过RS-232链接的传统计算机终端,不对数据进行本地处理或本地执行用户程序的串行通讯终端。哑终端有时也指功能有限,只有单色文本处理能力或直接传输每个键入的字符而不等待主机轮询的公共计算机终端。

二、智能终端(胖客户端)

智能终端就是有能力处理转义序列,也就是说处理能力较强的终端机。

Linux系统的终端设备通常有如下几种:

一、 控制台

系统控制台/dev/console

/dev/console是系统控制台,是与操做系统交互的设备。系统所产生的信息会发送到该设备上。平时咱们看到的PC只有一个屏幕和键盘,它其实就是控制台。目前只有在单用户模式下,才容许用户登陆控制台/dev/console。(能够在单用户模式下输入tty命令进行确认)。

console有缓冲的概念,为内核提供打印输出。内核把要打印的内容装入缓冲区__log_buff,而后由console来决定打印到哪里(好比是tty0仍是ttySn等)。console指向激活的终端。历史上,console指主机自己的屏幕和键盘,而tty指用电缆连接的其它位置的控制台。

某些状况下console和tty0是一致的,就是当前所使用的是虚拟终端,也是激活虚拟终端。因此有些资料中称/dev/console是到/dev/tty0的符号连接,可是这样说如今看来是不对的:根据内核文档,在2.1.71以前,/dev/console根据不一样系统设定,符号连接到/dev/tty0或者其余tty*上,在2.1.71版本以后则彻底由内核代码内部控制它的映射。

若是一个终端设备要实现console功能,必须向内核注册一个struct console结构,通常的串口驱动中都会有。若是设备要实现tty功能,必需要内核的tty子系统注册一个struct tty_driver结构,注册函数在drivers/tty/tty_io.c中。一个设备能够同时实现console和tty_driver,通常串口都这么作。

当前控制台: /dev/tty

这是应用程序中的概念,若是当前进程有控制终端(Controlling Terminal),那么/dev/tty就是当前进程控制台的设备文件。对于你登陆的shell,/dev/tty就是你使用的控制台,设备号是(5,0)。不过它并不指任何物理意义上的控制台,/dev/tty会映射到当前设备(使用命令“tty”能够查看它具体对应哪一个实际物理控制台设备)。输出到/dev/tty的内容只会显示在当前工做终端上(不管是登陆在ttyn中仍是pty中)。你若是在控制台界面下(即字符界面下)那么dev/tty就是映射到dev/tty1-6之间的一个(取决于你当前的控制台号),可是若是你如今是在图形界面(Xwindows),那么你会发现如今的/dev/tty映射到的是/dev/pts的伪终端上。/dev/tty有些相似于到实际所使用终端设备的一个联接。

你能够输入命令“tty",将显示当前映射终端如:/dev/tty1或者/dev/pts/0等。也可使用命令“ps -ax”来查看其余进程与哪一个控制终端相连。

在当前终端中输入 echo “tekkaman” > /dev/tty ,都会直接显示在当前的终端中。

虚拟控制台 /dev/ttyn

/dev/ttyn是进程虚拟控制台,他们共享同一个真实的物理控制台。若是在进程里打开一个这样的文件且该文件不是其余进程的控制台时,那该文件就是这个进程的控制台。进程printf数据会输出到这里。在PC上,用户可使用alt+Fn切换控制台,如今不知道怎么回事我用Ctrl + Alt + Fn才能切换。这没具体看过为啥。多是Linux没有继承UNIX这方面的传统罢了。看起来感受存在多个屏幕,这种虚拟控制台对应tty1~n,其中:

/dev/tty1等表明第一个虚拟控制台

例如当使用ALT+F2进行切换时,系统的虚拟控制台为/dev/tty2 ,当前控制台(/dev/tty)则指向/dev/tty2

 

在UNIX系统中,计算机显示器一般被称为控制台(Console)。它仿真了类型为Linux的一种终端,而且有一些设备特殊文件与之相关联:tty0、tty一、tty2等。当你在控制台上登陆时,使用的是tty1。使用Alt+[F1—F6]组合键时,咱们就能够切换到tty二、tty3等上面去。

读者能够登陆到不一样的虚拟控制台上去,于是可让系统同时有几个不一样的会话存在。

而比较特殊的是/dev/tty0,他表明当前虚拟控制台,其实就是当前所使用虚拟控制台的一个别名。所以无论当前正在使用哪一个虚拟控制台(注意:这里是虚拟控制台,不包括伪终端),系统信息都会重定位到/dev/tty0上。只有系统或超级用户root能够向/dev/tty0进行写操做。tty0是系统自动打开的,但不用于用户登陆。在Framebuffer设备没有启用的系统中,可使用/dev/tty0访问显卡。

二、 伪终端pty(pseudo-tty)

伪终端(Pseudo Terminal)是终端的发展,为知足如今需求(好比网络登录、xwindow窗口的管理)。它是成对出现的逻辑终端设备(即master和slave设备, 对master的操做会反映到slave上)。它多用于模拟终端程序,是远程登录(telnet、ssh、xterm等)后建立的控制台设备。

历史上,有两套伪终端软件接口:

BSD接口:较简单,master为/dev/pty[p-za-e][0-9a-f];slave为 /dev/tty[p-za-e][0-9a-f] ,它们都是配对的出现的。例如/dev/ptyp3和/dev/ttyp3。但因为在编程时要找到一个合适的终端须要逐个尝试,因此逐渐被放弃。

Unix 98接口:使用一个/dev/ptmx做为master设备,在每次打开操做时会获得一个master设备fd,并在/dev/pts/目录下获得一个slave设备(如 /dev/pts/3和/dev/ptmx),这样就避免了逐个尝试的麻烦。因为可能有好几千个用户登录,因此/dev/pts/*是动态生成的,不象其余设备文件是构建系统时就已经产生的硬盘节点(若是未使用devfs、udev、mdev等) 。第一个用户登录,设备文件为/dev/pts/0,第二个为/dev/pts/1,以此类推。它们并不与实际物理设备直接相关。如今大多数系统是经过此接口实现pty。

咱们在X Window下打开的终端或使用telnet或ssh等方式登陆Linux主机,此时均经过pty设备。例如,若是某人在网上使用telnet程序链接到你的计算机上,则telnet程序就可能会打开/dev/ptmx设备获取一个fd。此时一个getty程序就应该运行在对应的/dev/pts/*上。当telnet从远端获取了一个字符时,该字符就会经过ptmx、pts/*传递给 getty程序,而getty程序就会经过pts/*、ptmx和telnet程序往网络上返回“login:”字符串信息。这样,登陆程序与telnet程序就经过“伪终端”进行通讯。

telnet<--->/dev/ptmx(master)<--->pts/*(slave)<--->getty

 若是一个程序把 pts/*看做是一个串行端口设备,则它对该端口的读/写操做会反映在该逻辑终端设备对的另外一个/dev/ptmx上,而/dev/ptmx则是另外一个程序用于读写操做的逻辑设备。这样,两个程序就能够经过这种逻辑设备进行互相交流,这很象是逻辑设备对之间的管道操做。对于pts/*,任何设计成使用一个串行端口设备的程序均可以使用该逻辑设备。但对于使用/dev/ptmx的程序,则须要专门设计来使用/dev/ptmx逻辑设备。经过使用适当的软件,就能够把两个甚至多个伪终端设备链接到同一个物理串行端口上。

三、 串口终端(/dev/ttySn)

串行端口终端(Serial PortTerminal)是使用计算机串行端口链接的终端设备。计算机把每一个串行端口都看做是一个字符设备。有段时间串行端口设备一般被称为终端设备,那时它的最大用途就是用来链接终端,因此这些串行端口所对应的设备名称是/dev/tts/0(或/dev/ttyS0)、/dev/tts/1(或/dev /ttyS1)等,设备号分别是(4,0)、(4,1)等(对应于win系统下的COM一、COM2等)。若要向一个端口发送数据,能够在命令行上把标准输出重定向到这些特殊文件名上便可。

咱们能够在命令行提示符下键入:echotekkaman> /dev/ttyS1会把“tekkaman”发送到链接在ttyS1(COM2)端口的设备上。

在2.6之后的内核后、一些三星的芯片将串口终端设备节点命名为ttySACn。TI的Omap系列芯片从2.6.37开始芯片自带的UART设备开始使用专有的的omap-uart驱动,故设备节点命名为ttyOn,以区别于使用8250驱动时的设备名“ttySn”。这其中包括笔者用到的这个S3C2440。因此咱们在Uboot启动参数中要设置console = ttySAC0才能够。这一句的意思其实就是把ttySAC0当作咱们的控制台终端。

四、 其它类型终端

还针对不少不一样的字符设备存在有不少其它种类的终端设备特殊文件,例如针对ISDN设备的/dev/ttyIn终端设备等。

好了。到此为止。相信读者已经对tty设备有了一个概观。

由于咱们和开发板的人机交互的接口是Windows下的串口控制台。这就是上面所说的控制台终端。可是咱们用了console =  ttySAC0.即把串口终端当作控制台终端。因此咱们要研究具体的代码须要cd到serial子目录下。即串口终端目录。ls显示serial下的文件结点。如图所示。





咱们主要关心的是两类文件。一类是与体系结构和板载资源无关的通用串口操做文件。(samsung.c)一类是与体系结构相关的硬件操做文件。(s3c2440.c s3c2410.c s5pv210.c等),咱们为了获得具体的调用链。在具体的发送函数中加入回溯。如图所示。




咱们获得的函数调用链是这样的(以发送函数。即文件的写操做为例.

write->

sys_write->

vfs_write->

redirected_tty_write->

tty_write->

n_tty_write->

uart_write->

uart_start->

s3c24xx_serial_start_tx





从具体代码上来看。这些函数基本上都是经过结构体中的函数指针调用。咱们能够把这个调用链分为三个部分。即tty子系统核心。tty链路规程。tty驱动

tty核心。是对整个tty设备的抽象。对用户提供统一的接口。包括sys_write->vfs_write

tty线路规程。是对传输数据的格式化。在tty_ldisc_N_TTY变量中描述。包括redirected_tty_write->tty_write->n_tty_write->

tty驱动。是面向tty设备的硬件驱动。这里面真正的对硬件进行操做。包括uart_write->uart_start->s3c24xx_serial_start_tx

 

这是从具体函数的角度来看的调用链。下面为了从数据结构的角度来分析调用链。介绍linux内核中针对于这一个串口硬件的主要数据结构。对于具体的字段咱们用到的时候再解释。

uart_driver。

就是uart驱动程序结构。封装了tty_driver,使得底层的UART驱动无需关心tty_driver具体定义以下。




 

uart_port

 

uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或者IO内存地址等信息。




 

uart_ops定义了针对UART的一系列操做。注意这里不要把uart_ops结构和uart_ops变量混淆。uart_ops结构是咱们这里的数据结构。而uart_ops变量则是一个tty_operations的变量。

在serial_core.c中定义了tty_operations的实例。即uart_ops变量,包含uart_open();uart_close();uart_send_xchar()等成员函数,这些函数借助uart_ops结构体中的成员函数来完成具体的操做





uart_ops变量是tty_operations型的一个变量。以下图所示。




、、

uart_state是uart的状态结构。





uart_info是uart的信息结构。在这个体系结构下定义为s3c24xx_uart_info





因此很显然。用数据结构来描述函数调用链就是

uart_driver ->

uart_state->

uart_port->

uart_ops->

特定的函数指针。、

初始化过程比较复杂。不赘述。从函数指针的调用流程为主线。忽略一些入参检查和内核中的信号量代码。大体的初始化流程以下图所示。





打开设备和初始化流程相似。如图所示。




同理数据的发送和接收如图所示。





这里咱们须要注意的是。使能发送并无真正的发送过程。而只是使能发送中断

这一句:enable_irq(ourport->tx_irq);





这是由于ARM9处理器上有一个循环缓冲。用户从write系统调用传下来的数据就会写入这个UTXH0寄存器。发送完事以后处理器会产生一个内部中断。咱们经过这个内部中断就能够实现流控过程、咱们打开芯片手册能够看到以下字样(拿ARM11举例也同样,。这是ARM11的):




 

以下才是发送中断的ISR(Interrupt Service Routine)中断服务例程。一个irqreturn_t类型的handler。





这个wr_regb(port, S3C2410_UTXH, port->x_char);就是往特定寄存器写的过程。

至此咱们的分析已经结束。相信读者对于Linux下的tty子系统已经有一个概观了。下面是这个uart驱动的总图。结合数据结构的调用链。Linux内核完成了驱动模型和特定硬件的分离:

 



 

串口驱动数据结构总图: