标签:
在Linux下使用文件描述符来表示设备文件和普通文件。文件描述符是一个整型的数据,所有对文件的操作都通过文件描述符实现。文件描述符的范围是0~OPEN_MAX,系统中有3个已经分配的文件描述符,即标准输入、标准输出、和标准错误,他们的文件描述符的值分别为0、1、2。
文件描述符是文件系统中连接用户空间和内核空间的枢纽。当打开一个或者创建一个文件时,内核空间创建相应的结构,并生成一个整型的变量传递给用户空间的对应进程,进程用这个文件描述符来对文件进行操作。
头文件:<fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open()函数根据用户设置的标志flags和模式mode在路径pathname下建立或者打开一个文件。当函数成功时,返回一个整型的文件描述符,出错时返回-1.
文件的打开标志flags用于设置文件打开后允许的操作方式。
flags的取值:
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:读写
O_APPEND:对文件的读写追加到文件的尾端
O_CREAT:文件不存在则创建它,同时第三个参数mode需要设定新文件的权限.
O_TRUNC:将文件的长度断为0。常用来对文件进行清空。
参数mode用于表示打开文件的权限,mode必须结合flags的O_CREAT使用,否则无效。
mode的取值:
S_IRWXU:用户(文件所有者)有读、写、执行的权限
S_IRUSR:用户对文件有读的权限
S_IWUSR:用户对文件有写的权限
S_IXUSR:用户对文件又执行的权限
S_IRWXG:组用户(文件所有者)有读、写、执行的权限
S_IRGRP:组用户对文件有读的权限
S_IWGRP:组用户对文件有写的权限
S_IXGRP:组用户对文件有执行的权限
S_IRWXO:其他用户(文件所有者)有读、写、执行的权限
S_IROTH:其他用户对文件有读的权限
S_IWOTH:其他用户对文件有写的权限
S_IXOTH:其他用户对文件有执行的权限
1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<fcntl.h> 4 #include<stdio.h> 5 6 int main(void) 7 { 8 int fd = -1; 9 char filename[]="test.txt"; 10 fd = open(filename,O_CREAT|O_RDWR,S_IRWXU); 11 if(fd==-1) 12 { 13 printf("Open file %s failure!,fd:%d\n",filename,fd); 14 } 15 else 16 { 17 printf("Open file %s success,fd:%d\n",filename,fd); 18 } 19 return 0; 20 } 21
编译执行后会在当前目录下创建test.txt
头文件: <unistd.h>
#include<unistd.h> int close(int fd);
close()函数关闭一个文件描述符,关闭以后此文件描述符不再指向任何文件,从而描述符可以再次使用。当函数执行成功的时候返回0,如果有错误发生,返回-1.
如果每次打开文件不关闭,则会将系统的文件描述符耗尽,导致不能再打开文件:
1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<fcntl.h> 4 #include<stdio.h> 5 #include<unistd.h> 6 7 int main(void) 8 { 9 int i = 0; 10 int fd = 0; 11 for(i=1;fd>=0;i++) 12 { 13 fd=open("test.txt",O_RDONLY); 14 if(fd>0) 15 { 16 printf("fd:%d\n",fd); 17 } 18 else 19 { 20 printf("error,can‘t openf file \n"); 21 exit(1); 22 } 23 } 24 return 0; 25 }
共使用65535个文件描述符后将文件描述符耗尽:
1 ... 2 fd:65530 3 fd:65531 4 fd:65532 5 fd:65533 6 fd:65534 7 error,can‘t openf file
所以,打开文件使用完毕后,要使用close()函数将文件关闭,释放文件描述符。示例如下:
1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<fcntl.h> 4 #include<stdio.h> 5 #include<unistd.h> 6 7 int main(void) 8 { 9 10 int fd = -1; 11 char filename[]="test.txt"; 12 fd = open(filename,O_CREAT|O_RDWR,S_IRWXU); 13 if(fd==-1) 14 { 15 printf("Open file %s failure!,fd:%d\n",filename,fd); 16 } 17 else 18 { 19 printf("Open file %s success,fd:%d\n",filename,fd); 20 } 21 //对文件进行读写的操作(略) 22 ... 23 24 close(fd); 25 return 0; 26 }
头文件: <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
read()函数从文件描述符fd对应的文件中读取count字节,放到buf开始的缓冲区。如果count的值为0,read()函数返回0,不进行其他操作;读取成功时,文件对应的读取位置指针向后移动,移动的大小为读取的字节数。
如果read()函数读取成功,返回读取的字节数;当返回值为-1的时候,读取函数有错误发生;如果已经达到文件的末尾,返回0.
从test.txt中读取数据,文件中存放的字符串 quick brown fox jump over the lazy dog。读取后将数据打印出来:
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<sys/types.h> 4 #include<sys/stat.h> 5 #include<fcntl.h> 6 7 int main(void) 8 { 9 int fd = -1,i; 10 ssize_t size =-1; 11 char buf[10]; 12 char filename[] = "test.txt"; 13 14 fd = open(filename,O_RDONLY); 15 if(-1==fd) 16 { 17 printf("Open file %s failuer,fd:%d\n",filename,fd); 18 }else{ 19 20 printf("Open file %s success,fd:%d\n",filename,fd); 21 } 22 //循环读取数据,直到文件末尾或者出错 23 while(size) 24 { 25 size = read(fd,buf,10); 26 if(-1==size) 27 { 28 close(fd); 29 printf("Read file %s error occurs\n",filename); 30 return -1; 31 }else{ 32 if(size>0) 33 { 34 printf("read %d bytes:",size); 35 printf("\""); 36 for(i =0;i<size;i++) 37 { 38 printf("%c",*(buf+i)); 39 } 40 printf("\"\n"); 41 }else{ 42 printf("reach the end of file \n"); 43 } 44 45 } 46 } 47 return 0; 48 }
输出结果:
Open file test.txt success,fd:3 read 10 bytes:"quick brow" read 10 bytes:"n fox jump" read 10 bytes:"s over the" read 10 bytes:" lazy dog " reach the end of file
头文件:<unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
与read()函数的含义类似,write()函数向文件描述符fd写入数据,数据的大小有count指定,buf为要写入数据的指针。写入成功返回写入数据的字节数,写入出错则返回-1。当操作的对象是普通文件时,写文件的位置从文件的当前开始,如果在打开文件时指定了O_APPEND项,每次写操作时,会将写操作的位置移到文件的结尾处。
向test.txt末尾中写入数据 quick brown fox jumps over the lazy dog。
1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<stdio.h> 4 #include<unistd.h> 5 #include<fcntl.h> 6 7 int main(void) 8 { 9 int fd = -1,i; 10 ssize_t size =-1; 11 int input =0; 12 13 char buf[] = "quick brown box jumps over the lazy dog"; 14 char filename[] = "test.txt"; 15 16 fd = open(filename,O_RDWR|O_APPEND); 17 if(-1==fd) 18 { 19 printf("Open file %s faliluer, fd: %d\n",filename,fd); 20 }else{ 21 printf("Open file %s success, fd:%d\n,=",filename,fd); 22 } 23 size = write(fd,buf,strlen(buf)); 24 printf("write %d bytes to file %s\n",size,filename); 25 26 close(fd); 27 return 0; 28 }
执行前,test.txt中的内容:
1 quick brown box jumps over the lazy dog
执行后,test.txt中的内容:
1 quick brown box jumps over the lazy dog 2 quick brown box jumps over the lazy dog
文件偏移量指的是当前文件操作位置相对于文件开始位置的偏移。当打开一个文件时,如果没有指定O_APPEND参数,文件的偏移量为0。如果指定了O_APPEND选项,文件的偏移量与文件的长度相等,即文件的当前操作位置移到了末尾。
头文件:<unistd.h>
off_t lseek( int fildes, off_t offset, int whence)
函数对文件描述符fildes所代表的文件,按照操作模式whence和偏移量的大小off_t,重新设定文件偏移量。如果lseek()函数操作成功,则返回新的文件偏移量的值;如果失败返回-1。由于文件的偏移量可以为负值,判断lseek()是否操作成功时,不要使用小于0的判断,要使用是否等于-1来判断。参数offet和whence搭配使用,具体含义如下:
whence值为SEEK_SET时,offset为相对文件开始处的值;
whence值为SEEK_CUR时,offset为相对当前位置的值;
whence值为SEEK_END时,offset为相对文件结尾的值;
首先打开文本test.txt,然后将偏移量定位到离开始处10字节处,写入字符串“0123456”。
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<unistd.h> 5 #include<fcntl.h> 6 7 int main(void) 8 { 9 int fd =-1; 10 ssize_t size = -1; 11 off_t offset = -1; 12 13 char buf[]="0123456"; 14 char filename[]="test.txt"; 15 16 fd = open(filename,O_RDWR); 17 if(-1==fd) 18 { 19 printf("Open file %s failure,fd:%d",filename,fd); 20 return -1; 21 } 22 offset = lseek(fd,10,SEEK_SET); 23 if(-1==offset) 24 { 25 printf("lseek file %s failure,fd:%d",filename,fd); 26 return -1; 27 } 28 size = write(fd,buf,strlen(buf)); 29 if(size!=strlen(buf)) 30 { 31 printf("write file %s failure,fd:%d",filename,fd); 32 return -1; 33 } 34 close(fd); 35 return 0; 36 }
执行前test.txt内容:
1 quick brown box jumps over the lazy dog
执行后test.txt内容:
1 quick brow0123456umps over the lazy dog
如果偏移量的设置超出文件的大小,会造成文件空洞。即文件尾部到设置位置之间被"\0"填充。
在程序设计的时候经常要用到文件的一些特征值,如文件的所有者,文件的修改时间、文件的大小等。stat()函数、fstat()函数和lstat()函数可以获得文件的状态。
头文件:<sys/stat.h>
1 int stat(const char *path, struct stat *buf); 2 int fstat(int filedes, struct stat *buf); 3 int lstat(const char *path, struct stat *buf);
函数的第一个参数时文件描述的参数,可以为文件描述符或文件的路径(含文件名),buf为指向struct stat结构体的指针,获得的状态从这个参数中传回。当函数执行成功时返回0,执行失败返回-1。
fstat区别于另外两个系统调用的地方在于,fstat系统调用接受的是 一个“文件描述符”,而另外两个则直接接受“文件全路径”。文件描述符是需要我们用open系统调用后才能得到的,而文件全路经直接写就可以了。stat和lstat的区别:当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;而stat返回的是该链接指向的文件的信息。
结构体struct stat 为一个描述文件状态的结构,定义如下:
1 struct stat { 2 mode_t st_mode; //文件对应的模式,文件,目录等 3 ino_t st_ino; //inode节点号 4 dev_t st_dev; //设备号码 5 dev_t st_rdev; //特殊设备号码 6 nlink_t st_nlink; //文件的连接数 7 uid_t st_uid; //文件所有者 8 gid_t st_gid; //文件所有者对应的组 9 off_t st_size; //普通文件,对应的文件字节数 10 time_t st_atime; //文件最后被访问的时间 11 time_t st_mtime; //文件内容最后被修改的时间 12 time_t st_ctime; //文件状态改变时间 13 blksize_t st_blksize; //文件内容对应的块大小 14 blkcnt_t st_blocks; //伟建内容对应的块数量 15 };
获取当前文件夹下test.txt文件的状态,并打印部分信息。
1 #include<sys/stat.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 #include<stdio.h> 5 6 int main(void) 7 { 8 struct stat st; 9 10 if(-1==stat("test.txt",&st)) 11 { 12 printf("get file status failure\n"); 13 return -1; 14 } 15 printf("此文件的大小:%d\n",st.st_size); 16 printf("此文件的租后修改时间:%d\n",st.st_mtime); 17 printf("此文件的节点:%d\n",st.st_ino); 18 printf("此文件的保护模式:%d\n",st.st_mode); 19 }
输出:
此文件的大小:40 此文件的租后修改时间:1462535869 此文件的节点:917523 此文件的保护模式:33216
mmap()函数将普通文件映射到内存中,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。 mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。
mmap()映射后,让用户程序直接访问设备内存,相比较在用户控件和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap映射内存必须是页面大小的整数倍,面向流的设备不能进行mmap,mmap的实现和硬件有关。
mmap()函数
头文件:<sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
参数说明:
start:映射区的起始地址,通常为NULL(或0),表示由系统自己决定映射到什么地址。
length:映射数据的长度,即文件需要映射到内存中的数据的大小。
prot:映射区保护方式,取一下某个值或者它们的组合:
PROT_EXEC:映射区可被执行
PROT_READ:映射区可读取
PROT_WRITE:映射区可写入
PROT_NONE:映射区不可访i问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合:
MAP_FIXED:如果参数start指定了用于需要映射到的地址,而所指的地址无法成功建立映射,则映射失败。通常不推荐使用此设置,而将start设置为null(或0),由系统自动选 取映射地址。
MAP_SHARED:共享映射区域,映射区域允许其他进程共享,对映射区域写入数据将会写入到原来的文件中。
MAP_RIVATE:对映射区域进行写入操作时会产生一个映射文件的复制,即写入复制(copy on write),而读操作不会影响此复制。对此映射区的修改不会写回原来的文件,即不会影响原来文件的内容。
MAP_ANONYMOUS:建立匿名映射。映射区不与任何文件关联,而且映射区无法与其他进程共享。
MA_DENYWRITE:对文件的写入操作将被禁止,不允许直接对文件进行操作。
MAP_LOCKED:将映射区锁定,防止页面被交换出内存。
参数flags必须为MAP_SHARED或者MAP_PRIVATE二者之一的类型。MAP_SHARED类型表示多个进程使用的是一个内存映射的副本,任何一个进程都可对此映射进行修改,其他的进程对其修改是可见的。而MAP_PRIVATE则是多个进程使用的文件内存映射,在写入操作后,会复制一个副本给修改的进程,多个进程之间的副本是不一致的。
fd:文件描述符,一般由open()函数返回。
offse:被映射数据在文件中的起点。
mmap()执行成功后返回映射区的起始地址,执行失败返回-1.
munmap()函数
头文件:<sys/mman.h>
int munmap(void *start, size_t length);
munmap()函数的作用是解除mmap()函数的映射关系。参数start时mmap()函数成功后的返回值,即映射的内存地址;length为映射区的长度。
函数执行成功返回0,执行失败返回-1。
msync()函数
头文件:<sys/mman.h>
int msync ( void * start , size_t length, int flags)
1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<fcntl.h> 4 #include<sys/mman.h> 5 #include<string.h> 6 #include<stdio.h> 7 #define FILELENGTH 800 8 9 int main(void) 10 { 11 int fd = -1; 12 char buf1[] = "Linux Command line and shell Scripting bible"; 13 char buf2[]= "a"; 14 char *addr = NULL; 15 ssize_t size = -1; 16 off_t cur_pos = 0; 17 //打开文件,将文件的长度缩小为0 18 fd = open("mmap.txt",O_RDWR|O_CREAT, S_IRWXU); 19 if(-1==fd) 20 { 21 return -1; 22 } 23 cur_pos = lseek(fd,FILELENGTH-1, SEEK_SET); 24 size = write(fd,buf2,strlen(buf2));//随意写入一个字符,此时文件长度为800 25 if(1==size) 26 { 27 printf("write success\n"); 28 } 29 30 //将文件mmap.txt中的数据段从开头到1M的数据映射到内存中,对文件的操作立刻显示在文件上,可读写. 31 addr = (char *)mmap(NULL, FILELENGTH, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 32 if((char *)-1 == addr) 33 { 34 printf("mmap failure !\n"); 35 close(fd); 36 return -1; 37 } 38 //将buf中的字符串复制到映射区域中,起始位置为addr偏移16 39 memcpy(addr +16 ,buf ,strlen(buf)); 40 munmap(addr,FILELENGTH);//取消内存映射 41 close(fd);//关闭文件 42 return 0; 43 }
运行结果是生成一个mmap.txt文件并填充一下内容:
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@Linux Command line and shell Scripting bible^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@a
fcntl()函数用于获得和改变已经打开文件的性质。
头文件:<fcntl.h>
int fcntl(int fd, int cmd); int fcnt(int fd, int cmd, long arg); int fcnt(int fd, int cmd, struct flock *lock);
参数说明:
fd:文件描述符
cmd:操作命令
arg:供命令使用的参数
lock:同上
返回值:
fcntl()的返回值与命令有关。如果出错,所有命令返回-1,如果成功则返回某个其他值。下列三个命令有特定的返回值:
F_DUPFD:返回新的文件描述符
F_GETFD:返回相应标志
F_GETFL,F_GETOWN:返回一个正的进程ID或负的进程组ID
fcntl()函数有5种功能:
1.复制一个现有的描述符(cmd = F_DUPFD)
2.获得/设置文件描述符标记(cmd = F_GETFD或F_SETFD)
3.获得/设置文件状态标记(cmd = GETFL或F_SETFL)
4.获得/设置异步I/O所有权(cmd = F_GETOWN或F_SETOWN)
5.获得/设置记录锁(cmd = F_GETLK,F_SETLK或F_SETLKW)
详细的介绍请参考另一篇博文:httpc://blog.csdn.net/pbymw8iwm/article/details/7974789
2. fcntl()函数的例子
如下代码为修改文件状态值的一个实例,在文本文件fcntl.txt中的内容是"1234567890abcdefg",打开文件fcntl.txt时设置为O_RDWR,此时文件的偏移量位于文件开头,修改状态值的时候增加O_APPEND项,此时文件的偏移量移到文件末尾,写入字符串FCNTL,然后关闭。
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<fcntl.h> 4 #include<stdio.h> 5 #include<string.h> 6 7 int main(void) 8 { 9 int flags = -1; 10 char buf[] = "FCNTL"; 11 int fd = open("fcntl.txt",O_RDWR); 12 flags = fcntl(fd, F_GETFL, 0); 13 14 flags |= O_APPEND; 15 flags = fcntl(fd, F_SETFL, flags); 16 if(flags<0) 17 { 18 printf("failure to use fcntl\n"); 19 return -1; 20 } 21 22 write(fd, buf, strlen(buf)); 23 close(fd); 24 return 0; 25 }
ioctl()函数通过对文件描述符的发送命令来控制设备。
头文件:<sys/ioctl.h>
int ioctl(int d, int rquest, ...);
ioctl()函数通过对文件描述符发送特定的命令来控制文件描述符所代表的设备。参数d时一个已经打开的设备。通常情况下ioctl()函数出错会返回-1,成功返回0或者大于1的值,取决于对应设备的驱动程序对命令的处理。
使用ioctl()像其他的系统调用一样:打开文件,发送命令,查询结果。ioctl()函数像一个杂货铺,对设备的控制通常都通过这个函数来执行。具体对设备的操作方式取决于设备驱动程序的编写。
下面是一个控制CDROM打开的简单程序,因为CDROM控制程序的数据结构在头文件<liinux/cdrom.h>中,所以要包含此文件。
1 #include <Linux/cdrom.h> 2 #include <stdio.h> 3 #include <fcntl.h> 4 5 int main(void) 6 { 7 int fd = open("/dev/cdrom",O_RDONLY); 8 if(fd<0) 9 { 10 printf("Open cdrom failure\n"); 11 } 12 if(!ioctl(fd, CDROMEJECT,NULL)) 13 { 14 printf("成功弹出 CDROM\n"); 15 }else{ 16 printf("弹出 CDROM失败"); 17 } 18 close(fd) 19 return 0; 20 }
标签:
原文地址:http://www.cnblogs.com/kooyun/p/5465686.html