一:Linux中的设备管理
Linux采用文件系统管理硬件设备,所有的设备都看成是特殊的文件,从而将硬件设备的特性及管理细节对用户隐藏起来,实现设备无关性。
二:设备管理的特点
每个设备都对应文件系统中的一个索引节点,都有一个文件名。
应用程序通常可以通过系统调用open()打开设备文件,建立起与目标设备的连接。
对设备的使用类似于对文件的存取。
设备驱动程序都是系统内核的一部分,它们必须为系统内核或者它们的子系统提供一个标准的接口。
设备驱动程序使用一些标准的内核服务,如内存分配等。
三:设备分类
1. 按设备属主关系
系统设备
用户设备
2. 按设备信息交换单位来分
字符设备
块设备
3. 按设备共享属性来分
独享设备
共享设备(打印机,U盘)
四:设备工作原理
五:Linux设备操作
设备或文件操作两种方式:
用户编程接口 API
系统调用
六:系统调用
系统调用是操作系统提供给用户的一组“特殊”接口
系统调用并非直接和程序员或系统管理员直接打交道,而是通过软中断的方式向内核提交请求,从而获取内核函数的服务入口(系统调用表)
系统调用让系统从用户空间进入内核空间内运行,运行后将结果返回给应用程序(内核态->用户空间)
七:系统调用和系统API等区别
1. 系统API
主要是通过C库libc来实现,程序员多采用这种方式与内核交互,这些API通过系统调用来实现
2. 系统命令
系统管理员采用系统命令与内核交互,是一个可执行文件,通过系统API及系统调用来实现
3. 外壳程序
一系列系统命令和SHELL脚本共同组合成的程序。
八:函数库调用 与 系统调用
九:C库的文件操作
十:文件系统调用
open系统调用
read系统调用
write系统调用
create系统调用
close系统调用
mkdir系统调用
…
十一:文件描述符fd
每个进程PCB结构中有文件描述符指针,指向files_struct的文件描述符表,记录每个进程打开的文件列表
系统内核不允许应用程序访问进程的文件描述符表,只返回这些结构的索引即文件描述符ID(File Description)给应用程序
Linux系统中,应用程序通过这些文件描述符来实现让内核对文件的访问
每个进程能够访问的文件描述符是有限制的,通过#ulimit –n可以查看
十二:特殊文件描述符号
标准输入STDIN_FILENO
标准输出STDOUT_FILENO
标准错误STDERR_FILENO
每个进程被加载后,默认打开0,1,2这三个文件描述符
十三:open系统调用
open写法一:
有几种方法可以获得允许访问文件的文件描述符。最常用的是使用open()(打开)系统调用
函数原型
int open(const char *path, int flags);
参数
path :文件的名称,可以包含(绝对和相对)路径
flags:文件打开模式
返回值
打开成功,返回文件描述符;
打开失败,返回-1
open写法二:
函数原型
int open(const char *path, int flags,mode_t mode);
参数
path :文件的名称,可以包含(绝对和相对)路径
flags:文件打开模式
mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限,则文件权限为:mode&(~umask)
返回值
打开成功,返回文件描述符;
打开失败,返回-1
十四:打开文件的方式
所有这些标志值的符号名称可以通过#include<fcntl.h>访问
十五:访问权限
十六:文件打开示例
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
void main()
{
int outfd = 0;
outfd = open(“myfile",O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP);
if(outfd==-1)
{
perror(“fail to open file\n”);
exit(-1);
}
else
{
perror(“success to open file\n”);
}
close(outfd); //关闭文件描述符
}
十七:关闭文件close
将进程中fd对应的文件描述表结构释放
函数原型:
int close(int fd);
函数参数:fd :要关闭的文件的文件描述符
返回值
如果出现错误,返回-1
调用成功返回0
十八:典型的字符设备
控制终端tty:tty是控制终端设备文件的统称,代表正控制着系统的终端
伪终端pty(pseudo tty):由pty master和pty slave构成;图形终端和远程控制终端都是pty
控制台终端console:主机的显示器称为控制台终端console,当在控制台终端登录时,tty1是虚拟终端,使用ALT+F(1-6)来切换虚拟终端tty[1~6],tty0是代表当前所使用的虚拟终端(tty?)的别名
串口终端ttyS[n]
控制终端tty
tty 表示正在控制系统的终端
十九:write系统调用
用write()系统调用将数据写到一个文件中
函数原型:
int write(int fd,void *buf,size_t nbytes);
函数参数:
fd :要写入的文件的文件描述符
buf: 指向内存块的指针,从这个内存块中读取数据写入到文件中
nbytes: 要写入文件的字节个数
返回值
如果出现错误,返回-1
如果写入成功,则返回写入到文件中的字节个数
write()示例如下:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
void main()
int outfd = 0, r_size = 0;
char buf[ ] = “Hello world!!”;
outfd = open(“first”,O_WONLY | O_TRUNC | O_CREAT,S_IRWXU);
if(outfd>0)
{
r_size = write(outfd,buf,sizeof(buf));
if(r_size>0)
{
printf(“write data to file success”);
}
close(outfd);
}
}
二十:read系统调用
一旦有了与一个打开文件描述相连的文件描述符,只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节
函数原型:
int read(int fd, void *buf, size_t nbytes);
参数
fd :想要读的文件的文件描述符
buf: 指向内存块的指针,从文件中读取来的字节放到这个内存块中
nbytes: 从该文件复制到buf中的字节个数
返回值
如果出现错误,返回-1
返回从该文件复制到规定的缓冲区中的字节数,文件结束,返回0否则
二十一:close系统调用
为了重新利用文件描述符,用close()系统调用释放打开的文件描述符
函数原型:
int close(int fd);
函数参数:fd :要关闭的文件的文件描述符
返回值
如果出现错误,返回-1
调用成功返回0
二十二:终端间交互
…
int fd1,fd2;
char buf[80];
fd1 = open("/dev/tty0",O_RDONLY);
if(fd1==-1)
{
printf("can't open dev file\n");
exit(-1);
}
fd2 = open("/dev/tty",O_WRONLY);
if(fd2==-1)
{
printf("can't open dev file\n");
exit(-2);
}
while(1)
{
memset(buf,0x00,80);
read(fd1,buf,10);
if(strcmp(buf,"exit\n")==0) break;
write(fd2,buf,10);
}
…
在secure CRT的伪终端下运行该段代码,然后在控制台终端下输入命令,会是什么结果?
二十三:文件的随机读写
到目前为止的所有文件访问都是顺序访问。这是因为所有的读和写都从当前文件的偏移位置开始,然后文件偏移值自动地增加到刚好超出读或写结束时的位置,使它为下一次访问作好准备。
有个文件偏移这样的机制,在Linux系统中,随机访问就变得很简单,你所需做的只是将当前文件移值改变到有关的位置,它将迫使一次read()或write()发生在这一位置。(除非文件被O_APPEND打开,在这种情况下,任何write调用仍将发生在文件结束处)
二十四:lseek系统调用
原型:
off_t lseek (int fd, off_t offset, int base);
参数分别是:需设置的文件标识符、偏移量、搜索的起始位置
返回值:返回新的文件偏移值
base 表示搜索的起始位置,有以下几个值:(这些值定义在<unistd.h>)
二十五:chmod和fchmod系统调用
功能说明:用来改变给定路径名pathname的文件的权限位
原型
int chmod (char *pathname, mode_t mode);
参数分别是:文件的路径名、权限位
int fchmod (int fd, mode_t mode);
参数分别是:文件描述符、权限位
返回值:调用成功返回0,失败返回-1
二十六:chown和fchown系统调用
功能说明:用来改变文件所有者的识别号(owner id)或者它的用户组识别号(group ID)
原型:
int chown (char *pathname, uid_t owner,gid_t group);
参数分别是:文件的路径名、所有者识别号、用户组识别号
int fchown (int fd, uid_t owner,gid_t group);
参数分别是:文件描述符、所有者识别号、用户组识别号
返回值:调用成功返回0,失败返回-1
二十七:mkdir系统调用
功能说明:用来创建一个称为pathname的新目录,它的权限位设置为mode
原型:
int mkdir(char *pathname,mode_t mode);
参数分别是:文件的路径名、权限位
返回值:调用成功返回0,失败返回-1
二十八:rmdir系统调用
功能说明:删除一个空目录
原型:
int rmdir(char *pathname);
参数是:文件的路径名
二十九:目录访问
目录访问写法一:
功能说明:打开一个目录
原型:
DIR* opendir(char *pathname);
参数是:文件的路径名
返回值:打开成功,返回一个目录指针打开失败,则返回0
目录访问写法二:
功能说明:访问指定目录中下一个连接的细节
原型:
struct dirent* readdir ( DIR *dirptr);
参数是:目录指针
返回值:返回一个指向dirent结构的指针,它包含指定目录中下一个连接的细节;没有更多连接时,返回NULL
目录信息结构体:
struct dirent
{
long d_ino; /* 目录i结点编号 */
off_t d_off; /* 目录文件开关至此目录进入点的位移 */
unsigned short d_reclen; /* d_name的长度 */
char d_name [NAME_MAX+1]; /* 以NULL结尾的文件名 */
}
如果调用opendir打开某个目录之后,第一次调用readdir函数,则返回的是该目录下第一个文件的信息,第二次调用readdir函数返回该目录下第二个文件的信息,依此类推。如果该目录下已经没有文件信息可供读取,则返回NULL。
目录访问写法三:
int closedir (DIR *dirptr);
参数是:目录指针
返回值:调用成功返回0,失败返回-1
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
int my_read_dir(const char *path){
DIR * dir;
struct dirent *ptr;
if(( dir = opendir(path)) == NULL ){
return -1;
}
while(( ptr = readdir(dir)) != NULL ){
printf("file name: %s\n",ptr->d_name);
}
closedir(dir);
return 0;
}
int main(){
if(my_read_dir(argv[1]) < 0 ){
exit(1);
}
return 0;
}
三十:文件纪录锁
什么是文件锁:当有多个进程同时对某一文件进行操作时,就有可能发生数据的不同步,从而引起错误,该文件的最后状态取决于写该文件的最后一个程序。Linux中文件记录锁可以对文件某一区域进行文件记录锁的控制。它是通过fcntl函数来实现的。
三十一:fcntl函数
功能说明:管理文件记录锁的操作
原型:
#include <unistd.h>
#include <fcntl.h>
int fcntl (int fd,int cmd,struct flck *lock);
参数:fd:文件描述符;cmd:功能符号;(F_SETLK用来设置或释放锁;F_GETLK用来获得锁信息;)lock:存储锁信息的结构体指针;
锁信息结构体:
struct flock
{
short l_type; /* 锁的类型 */
short l_whence; /* 偏移量的起始位置: */
off_t l_start; /* 从l_whence的偏移量 */
off_t l_len; /* 从l_start开始的字节数 */
pid_t l_pid; /* 锁所属进程ID(一般不用) */
}
l_type有F_RDLCK读锁、F_WRLCK写锁及F_UNLCK空锁。
l_whence有SEEK_SET、SEEK_CUR和SEEK_END。
l_len为0时表示从起点开始直至最大可能位置为止。
fcntl()示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(){
int fd;
struct flock lock;
if((fd = open("example",O_CREAT | O_TRUNC | O_RDWR, S_IRWXU)) == -1){
printf("open file error\n");
return -1;
}
memset(&lock,0,sizeof(struct flock));
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if(fcntl(fd,F_GETLK,&lock) == 0){
if(lock.l_type != F_UNLCK){
printf("lock can not by set in fd\n");
}else{
lock.l_type = F_WRLCK;
if(fcntl(fd,F_SETLK,&lock) == 0)
printf("set write lock success!\n");
else
printf("set write lock fail!\n");
getchar();
lock.l_type = F_UNLCK;
fcntl(fd,F_SETLK,&lock);
}
}
close(fd);
return 0;
}