linux编程之文件操作
在linux下用文件描述符来表示设备文件盒普通文件,文件描述符是一个整型的数据,所有对文件的操作都是通过文件描述符来实现的。
文件描述符是文件系统中连接用户空间和内核空间的枢纽,当我们打开一个或者创建一个文件时,内核空间会创建相应的结构,并且生
成一个整型的变量传递给用户空间的对应进程,而进程则用这个文件描述符来对文件进行操作。要注意的是,文件描述符是一个有限的
资源,因此,在使用完毕后要及时释放,一般是调用close()函数来关闭的。在linux系统中有3个已经分配好的文件描述符,那就是标准
输入,标准输出和标准错误,他们的文件描述符分别为0,1,2.
1.open() ,create()函数
在linux下,open()函数用于打开一个已经存在的文件或者创建一个新文件,而create()函数用于创建一个新的文件:
int open(const char * pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
其中,flags为用户设置 的标准,其可能值为:O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写),O_APPEND(追加)
O_CREAT(不在则创建)等等,这里压迫特别注意的是,当我们选择了O_CREAT时,则要在mode参数中设定新文件的权限。
pathname为文件路径
mode参数用于表示打开文件的权限,它必须与flags的O_CREAT结合使用。
int create(const char *parhname,mode_t mode);
而create函数相当于open的缩写版本:open(parhname,O_WRONLY|O_CREAT|O_TRUNC,mode);
2.读取文件read函数
ssize_t read(int fd,void *buf,size_t count)
read函数是负责从fd中读取count字节内容并且放在buf开始的缓冲区.当读成功时,文件对应的读取位置指针向后移动位置,移动的大小为
成功读取的字节数,read返回实际所读的字节数,如果返回的值是0 表示已经读到文件的结束了,小于0表示出现了错误。
3.写文件write()函数
ssize_t write(int fd, const void*buf,size_t count);
write函数将buf中的count字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量.
4文件偏移函数lseek()
每个打开的文件都有一个与其相关联的“当前文件偏移量”。通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。
按系统默认的情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。可以调用lseek显式地为一个打开的文件设
置起偏移量。
- #include <unistd.h>
- off_t lseek(int filedes, off_t offset, int whence);
返回值:成功返回新的文件偏移量,出错返回-1.对参数offset的解释与参数whence的值有关。
- 若是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。
- 若是SEEK_CUR,则将该文件的偏移量设置为当前值加offset,offset可为正或负。
- 若是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可为正或负。
可以用下列方式确定打开文件的当前偏移量:
- off_t currpos;
- currpos = lseek(fd, 0, SEEK_CUR);
这种方法也可用来确定所涉及的文件是否可以设置偏移量。如果文件描述符引用的是一个管道、FIFO或网络套接字,则lseek返回-1,并将
errno设置为ESPIPE。通常,文件的当前偏移量应当是一个非负整数,但是,某些设备也可能允许负的偏移量。但对于普通文件,则其偏移
量必须是非负值。因为偏移量可能是负值,所有在比较lseek的返回值时应当谨慎,不要测试它是否小于0,而要测试它是否等于-1。
lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作。
文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成了一个空洞。位于文件中但没
有写过的字节都被读为0。文件中的空洞并不要求在磁盘上占用存储区。
5.获取文件状态fstat()函数
#include <sys/stat.h>
int stat(const char*pathname,struct stat*buf);
int fstat(int filedes,struct stat*buf);
int lstat(const char *pathname,struct stat*buf);
这三个函数的返回:若成功为0,出错为-1。 给予一个pathname,stat函数返回一个与此命名文件有关的信息结构,fstat函数获
得已在描述符filedes上打开的文件的有关信息。 lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关
信息,而不是由该符号连接引用的文件的信息。
第二个参数是个指针,它指向一个我们应提供的结构。这些函数填写由buf指向的结构。该结构的实际定义可能所实施而有所不同,但其基本形式是:
struct stat{
mode st_mode; /*文件类型和方式(许可数)*/
ino st_ino;/* i-节点号(序列号)*/
dev st_dev;/*设备号(文件系统)*/
dev st_rdev;/*特殊文件的设备号*/
nlink st_nlink;/*连接数*/
uid st_uid;/*属主的用户ID*/
gid st_gid;/*属主的组ID*/
off st_size;/*普通文件的字节长度*/
time st_atime;/*最后存取时间*/
time st_mtime;/*最后修改存取时间*/
time st_ctime;/*最后文件状态更改时间*/
long st_blksize;/*最佳I/O块长*/
long st_blocks;/*分配的512字节块块数
};
6.文件空间映射mmap()函数
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);
该函数主要用途有三个:
1、将一个普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能;
2、将特殊文件进行匿名内存映射,可以为关联进程提供共享内存空间;
3、为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。
mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件
进行访问,不必再调用read(),write()等操作。
参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。
参数length:代表将文件中多大的部分映射到内存。
参数prot:映射区域的保护方式。可以为以下几种方式的组合:
PROT_EXEC 映射区域可被执行
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
PROT_NONE 映射区域不能存取
参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写
回原来的文件内容。
MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存
映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。
参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值:
若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。
系统调用mmap()用于共享内存的两种方式:
(1)使用普通文件提供的内存映射:
适用于任何进程之间。此时,需要打开或创建一个文件,然后再调用mmap()
典型调用代码如下:
fd=open(name, flag, mode);
if(fd<0)
.........
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);
..........
munmap(ptr,len);
close(fd);
当对文件的操作完毕后,需要使用munmap()函数将mmap()映射的地址取消并关闭打开的文件.
(2)使用特殊文件提供匿名内存映射:
适用于具有亲缘关系的进程之间。由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用 fork()。那么在调用fork()之后,
子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区 域进行通信了。注意,这里
不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。 对于具
有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可
7.文件属性fcntl()函数
fcntl()函数向打开的文件fd发送命令,更改其属性。函数原型如下:
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd,int cmd, struct flock *lock);
操作成功,其返回值依赖于cmd,出错返回-1。以下命令有特殊返回值:
F_DUPFD 返回值为新的文件描述符
F_GETFD 返回值为获得的相应标志
F_GETFL 返回值为文件描述符的状态标志
F_GETOWN 返回值如果为正数则是进程ID号,如果为负数则是进程组ID号
函数fcntl()的功能分为以下6类:
(1) 复制文件描述符(cmd=FD_DUPFD)
(2) 获得/设置文件描述符(cmd=F_GETFD或者F_SETFD)
(3) 获得/设置文件状态值(cmd=F_GETFL或者F_SETFL)
(4) 获得/设置信号发送对象(cmd=F_GETLEASE、F_SETOWN、F_GETSIG或者F_SETSIG)
(5) 获得/设置文件记录锁(cmd=F_GETLK或者F_SETLK或者F_SETLKW)
(6) 获得/设置文件租约(cmd=F_GETLEASE或者F_SETLEASE)
8. 文件输入输出控制ioctl()函数
Ioctl是input output control的简写,表示输入输出控制,ioctl()函数通过对文件描述符的发送命令来控制设备。
函数原型如下:
#include <sys/ioctl.h>
int ioctl(int d, int request,…);
使用ioctl()像其他的系统调用一样:打开文件,发送命令,查询结果。iocntl()函数像个杂货铺,对设备的控制通常都通过这个函数来实行。
具体对设备的操作方式取决于设备驱动程序的编写。