course2610_lab20_Linux 设备操作命令及代码

本文详细介绍了Linux系统中查看设备的各种命令,如`uname -a`、`lspci -tv`和`lsusb -tv`等,以及如何通过`/proc/devices`和`/dev`目录了解设备信息。此外,文章讨论了字符设备和块设备的区别,主设备号和次设备号的作用,并展示了如何使用`mknod`创建设备文件。还涵盖了设备号的动态分配、查看和释放方法,以及设备号在区分块设备分区中的应用。最后,提到了标准输入输出的读写操作与系统调用`read()`、`write()`的使用。

Linux 查看设备的命令

uname -a 查看内核、操作系统、CPU 信息
cat /proc/cpuinfo 查看 CPU 信息

lspci -tv 列出所有 PCI 设备(Ubuntu 的安装包 pciutils ,sudo apt-get install pciutils)
lsusb -tv 列出所有 USB 设备(Ubuntu 的安装包 usbutils ,sudo apt-get install usbutils)

free -m 查看内存使用量和交换区使用量
df -h 查看各分区使用情况
du -sh 查看指定目录的大小
cat /proc/loadavg 查看系统负载 磁盘和分区

fdisk -l 查看所有分区
swapon -s 查看所有交换分区

1. Linux 设备文件

1.1 概述

Linux 中的设备有 2 种类型:

  • 字符设备(无缓冲且只能顺序存取)、
  • 块设备(有缓冲且可以随机存取)。

每个字符设备和块设备都必须有主、次设备号,主设备号相同的设备是同类设备(使用同一个驱动程序)。

这些设备中,有些设备是对实际存在的物理硬件的抽象,而有些设备则是内核自身提供的功能(不依赖于特定的物理硬件,又称为"虚拟设备")。

每个设备在/dev目录下都有一个对应的文件(节点)。可以通过 cat /proc/devices 命令查看当前已经加载的设备驱动程序的主设备号。

内核能够识别的所有设备都记录在原码树下Documentation/devices.txt 文件中。

/dev目录下除了字符设备和块设备节点之外还通常还会存在:FIFO 管道、Socket、软硬连接、目录。这些东西没有主次设备号。

1.2 主设备号和 次设备号

linux 的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在 /dev 目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux 有约定俗成的编号,如硬盘的主设备号是 3。

一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备。例如一个嵌入式系统,有两个 LED 指示灯,LED 灯需要独立的打开或者关闭。那么,可以写一个 LED 灯的字符设备驱动程序,可以将其主设备号注册成 5 号设备,次设备号分别为 1 和 2。这里,次设备号就分别表示两个 LED 灯。

设备文件通常都在/dev目录下, 如:ls -l /dev

ll /dev |more
总计 0
crw-rw----  1 root uucp    4,  70 04-14 18:16 ttyS6
crw-rw----  1 root uucp    4,  71 04-14 18:16 ttyS7
crw-rw----  1 root tty         7,   0 08-08 18:58 vcs
crw-rw----  1 root tty         7,   1 08-08 18:58 vcs1
crw-rw-rw-  1 root root    1,   7 08-08 18:58 full
crw-rw-rw-  1 root root    1,   3 04-14 18:16 null

如上,前面第一个字符为c 的表示字符设备。在字符设备里,有主设备号和次设备号。如上1,4,7 分别是主设备号,0,1,3,7,70,71都是次设备号。一般的,主设备号标识出与设备关联的设备驱动。如 /dev/null 和 /dev/full 由 1 号驱动来管理,/dev/vcs 和/dev/vcs1由 7 号驱动来管理,/dev/ttyS6 由 4 号驱动来管理。

现在的 Linux 内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。

内核由次设备号确定当前所指向的是哪个设备。根据所编写的驱动程序,可以从内核那里得到一个直接指向设备的指针,或者使用次设备号作为一个设备本地数组的索引。但不论如何,内核自身几乎不知道次设备号的什么事情。

1.3 创建自己的设备文件

创建设备文件的命令为 mknod。

比如,创建一个设备名为 mycdev 的字符设备,主设备号为 231,次设备号为 0:

sudo mknod /dev/mycdev c 231,0

2. 设备号内部表示

在内核中,dev_t 类型( 在 <linux/types.h> 头文件有定义 ) 用来表示设备号,包括主设备号和次设备号两部分。对于 2.6.x 内核,dev_t 是个 32 位量,其中 12 位用来表示主设备号,20 位用来表示次设备号。
linux/types.h 头文件里定义有:

typedef __kernel_dev_t    dev_t;
typedef __u32 __kernel_dev_t;

2.1 主设备号和次设备号的获取

为了写出可移植的驱动程序,不能假定主设备号和次设备号的位数。不同的机型中,主设备号和次设备号的位数可能是不同的。

应该使用MAJOR宏得到主设备号,使用MINOR宏来得到次设备号。下面是两个宏的定义:(linux/kdev_t.h)

#define MINORBITS   20                                 
 //次设备号/  

#define MINORMASK   ((1U << MINORBITS) - 1)           
  /*次设备号掩码*/  
  
#define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))   
/*dev右移20位得到主设备号*/  

#define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))   
/*与次设备掩码与,得到次设备号*/ 

MAJOR宏将dev_t向右移动20位,得到主设备号;
MINOR宏将dev_t的高12位清零,得到次设备号。
相反,可以将主设备号和次设备号转换为设备号类型(dev_t),
使用宏MKDEV可以完成这个功能。

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

2.2 静态分配设备号

静态分配设备号,就是驱动程序开发者,静态地指定一个设备号。对于一部分常用的设备,内核开发者已经为其分配了设备号。

这些设备号可以在内核源码documentation/ devices.txt文件中找到。

如果只有开发者自己使用这些设备驱动程序,那么其可以选择一个尚未使用的设备号。在不添加新硬件的时候,这种方式不会产生设备号冲突。但是当添加新硬件时,则很可能造成设备号冲突,影响设备的使用。

2.3 动态分配设备号

由于静态分配设备号存在冲突的问题,所以内核社区建议开发者使用动态分配设备号的方法。动态分配设备号的函数是alloc_chrdev_region()

2.4 查看设备号

当静态分配设备号时,需要查看系统中已经存在的设备号,从而决定使用哪个新设备号。可以读取/proc/devices文件获得设备的设备号。/proc/devices文件包含字符设备和块设备的设备号,如下所示:

 cat /proc/devices 
Character devices:                  /*字符设备*/  

   1 mem  
   4 /dev/vc/0  
   7 vcs  
   13 input  
   14 sound  
   21 sg  

Block devices:                      /*块设备*/  
   1 ramdisk  
   2 fd  
   8 sd  
   253 device-mapper  
   254 mdp 

3. 申请和释放设备号

内核维护着一个特殊的数据结构,用来存放设备号与设备的关系。在安装设备时,应该给设备申请一个设备号,使系统可以明确设备对应的设备号。设备驱动程序中的很多功能,是通过设备号来操作设备的。下面,首先对申请设备号进行简述。

3.1.申请设备号

在构建字符设备之前,首先要向系统申请一个或者多个设备号。完成该工作的函数是register_chrdev_region(),该函数在<linux/fs.h>中定义:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

其中,from是要分配的设备号范围的起始值。一般只提供from的主设备号,from的次设备号通常被设置成0。count是需要申请的连续设备号的个数。最后name是和该范围编号关联的设备名称,该名称不能超过64字节。

和大多数内核函数一样,register_chrdev_region()函数成功时返回0。

错误时,返回一个负的错误码,并且不能为字符设备分配设备号。

在Linux中有非常多的字符设备,在人为的为字符设备分配设备号时,很可能发生冲突。Linux内核开发者一直在努力将设备号变为动态的。可以使用alloc_chrdev_region()函数达到这个目的。(linux/fs.h)

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

在上面的函数中,dev作为输出参数,在函数成功返回后将保存已经分配的设备号。函数有可能申请一段连续的设备号,这是dev返回第一个设备号。baseminor表示要申请的第一个次设备号,其通常设为0。

countnameregister_chrdev_region()函数的对应参数一样。count表示要申请的连续设备号个数,name表示设备的名字。

3.2.释放设备号

使用上面两种方式申请的设备号,都应该在不使用设备时,释放设备号。设备号的释放统一使用下面的函数:

void unregister_chrdev_region(dev_t from, unsigned count); 

在上面这个函数中,from表示要释放的设备号,count表示从from开始要释放的设备号个数。

通常,在模块的卸载函数中调用unregister_chrdev_region()函数。

次设备号的主要用途

1、区分设备驱动程序控制的实际设备;

2、区分不同用途的设备 (misc 系列设备)

3、区分块设备的分区 (partition)

通常,为了使应用程序区分所控制设备的类型,内核使用主设备号。而存在多台同类设备时,为了选择其中的一种,设备驱动程序就使用次设备号。

4. 区分块设备的分区

块设备具有被称为分区的分配领域。例如,硬盘在物理上是一个设备,从内核的角度,硬盘被分为多个分区,而以这些分区为对象则形成了文件系统,此时,次设备号既表示设备,也表示分区。


brw-rw----  1 root disk    8,  16 2009-09-24 sdb
brw-rw----  1 root disk    8,  17 2009-09-24 sdb1
brw-rw----  1 root disk    8,  18 2009-09-24 sdb2
brw-rw----  1 root disk    8,  21 2009-09-24 sdb5
brw-rw----  1 root disk    8,  22 2009-09-24 sdb6
brw-rw----  1 root disk    8,  23 2009-09-24 sdb7
brw-rw----  1 root disk    8,  24 2009-09-24 sdb8

5. 标准输入,输出的读写探讨

给出关于文件操作的相关内容,请对 read() write() 操作的代码进行运行,代码中把文件描述符 0,1,2 分别换为 /dev/stdin/dev/stdou /dev/stderr(先用open()函数打开,不能直接换),说明scanf() 和 read()的差别,printf() write()的差别,哪一种效率更高?为什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值