本文共 3422 字,大约阅读时间需要 11 分钟。
Linux系统将硬件设备与文件统一管理,使得设备的操作方式与文件操作相似,从而实现了设备的无关性。所有设备都可看作特殊文件,通过文件系统对设备进行管理,隐藏了硬件设备的具体特性。
①每个设备对应文件系统中的索引节点,具有唯一的文件名。
②应用程序通常通过系统调用open()打开设备文件,与目标设备建立连接。
③设备的使用方式类似于文件的读写操作。
④设备驱动程序是系统内核的一部分,必须遵循标准接口,为内核或子系统提供服务。
⑤设备驱动程序可利用内核服务,如内存分配等功能。
Linux系统通过虚拟文件系统的方式,将硬件设备抽象为文件。用户应用程序可以通过文件操作的方式进行设备操作,内核负责将操作转化为实际的硬件操作。这种机制实现了设备的透明化管理。
按设备属性可分为以下几类:
1. 按属主关系:系统设备(如显卡、网卡)与用户设备(如存储设备、终端设备)
2. 按设备信息交换单位:字符设备(如终端、网络)与块设备(如硬盘、分区)
3. 按共享属性:独享设备与共享设备
Linux系统提供了丰富的接口供应用程序与设备交互。用户编程接口包括设备特定的函数库,而系统调用则为设备操作提供了更底层的接口。
系统调用是操作系统为用户提供的特殊接口,通过软中断将用户空间的请求提交给内核,完成后将结果返回用户空间。系统调用让程序能够进入内核空间执行特定操作。
系统API主要通过C库libc实现,程序员常用这些接口与内核交互。系统命令则是通过可执行文件与内核交互,外壳程序则由系统命令和SHELL脚本组合而成。
| 函数库调用 | 系统调用 |
|---|---|
| 所有ANSI编译器版本中的C库函数一致 | 各操作系统的系统调用不同 |
| 调用函数库中的特定函数 | 调用系统内核服务 |
| 用户程序与系统相连 | 操作系统入口函数 |
| 用户地址空间执行 | 内核地址空间执行 |
| 用户时间 | 系统时间 |
| 过程调用,效率高 | 上下文切换,效率低 |
| 约300个C函数 | 约90个系统调用 |
如system、fprintf等 | 如chdir、fork等 |
| 函数名 | 功能 |
|---|---|
fopen() | 打开文件 |
fclose() | 关闭文件 |
fputc() | 写入文件 |
fgetc() | 读取文件 |
fread() | 读取文件数据 |
fwrite() | 写入文件数据 |
fseek() | 定位文件位置 |
fprintf() | 格式化输出 |
fscanf() | 格式化输入 |
feof() | 判断是否到达文件末尾 |
ferror() | 判断文件操作是否出错 |
rewind() | 文件位置重置 |
remove() | 删除文件 |
fflush() | 刷新文件缓冲区 |
每个进程维护一个文件描述符表,记录打开的文件信息。内核禁止直接访问文件描述符表,返回文件描述符ID(File Description ID)给用户程序。
系统预留三个特殊文件描述符:0(标准输入)、1(标准输出)、2(标准错误流)。这些描述符在进程加载时自动开放。
文件描述符的使用具有资源限制,可通过ulimit –n查看。
open函数用于打开或创建文件,支持多种模式和权限设置:
参数:
- path:文件路径- flags:文件打开模式(如O_RDONLY、 O_WRONLY、 O_RDWR等)- mode:文件权限掩码(可选) 返回值:
常用模式:
-O_RDONLY:只读- O_WRONLY:只写- O_RDWR:读写- O_APPEND:追加模式- O_CREAT:创建文件(需提供mode参数)- O_EXCL:创建时如果文件已存在则失败- O_TRUNC:截断文件 权限掩码由S_IRUSR、S_IWUSR等宏定义组成,支持灵活的权限设置。
示例:
#include#include #include void main() { int outfd = 0; if ((outfd = open("test", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU)) == -1) { perror("fail to open file"); return -1; } // 后续操作 close(outfd);}
close函数用于关闭已打开的文件或设备:
#includeint close(int fd);
read函数用于从文件或设备读取数据:
#includeint read(int fd, void *buf, size_t nbytes);
write函数用于向文件或设备写入数据:
#includeint write(int fd, const void *buf, size_t nbytes);
lseek函数用于移动文件读写位置:
#include#include off_t lseek(int fd, off_t offset, int base);
fcntl函数用于管理文件锁:
#include#include int fcntl(int fd, int cmd, struct flock *lock);
锁信息结构体struct flock包含:
struct flock { short l_type; /* 锁类型:F_RDLCK、F_WRLCK、F_UNLCK */ short l_whence; /* 偏移基点:SEEK_SET、SEEK_CUR、SEEK_END */ off_t l_start; /* 锁的起始偏移量 */ off_t l_len; /* 锁的长度 */ pid_t l_pid; /* 锁所属进程ID */}; 示例:
#include#include int main() { int fd; struct flock lock; if ((fd = open("test", O_RDWR | O_CREAT, S_IRWXU)) == -1) { printf("open file error"); return -1; } memset(&lock, 0, sizeof(struct flock)); lock.l_start = SEEK_SET; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(fd, F_GETLK, &lock) == 0) { if (lock.l_type != F_UNLCK) { printf("lock cannot be set in fd"); } else { lock.l_type = F_WRLCK; if (fcntl(fd, F_SETLK, &lock) == 0) { printf("set write lock success!"); } else { printf("set write lock fail!"); } getchar(); lock.l_type = F_UNLCK; fcntl(fd, F_SETLK, &lock); } } close(fd); return 0;}
转载地址:http://odah.baihongyu.com/