分类: LINUX
2015-08-12 14:57:52
当用户访问有关普通文件或者目录文件的内容的时候,他实际上访问存储在硬件块设备上的一些数据。从这个意义上说,文件系统是硬盘分区物理组织的用户级视图。
在linux中,一切都是文件,文件为操作系统服务和设备提供了一个简单而统一的接口,这就意味者程序可以像使用文件那样使用各种设备。Linux内核本身并不是一个进程,而是进程的管理者。每个系统调用都设置了一组识别进程请求的参数,然后执行与硬件相关的CPU指令来完成从用户态到内核态的转换。大多数情况下对于文件的操作只用到open,write,lseek,read,close五个系统调用。本文通过一个简单的例子来介绍这五个调用及关联内容。
先看例子:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
int
main(
void )
{
int file_des = open( "my_file.txt", O_RDWR | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR | S_IXUSR | S_IXOTH );
char *write_buf =
"zhujiangfeng\n";
if ( write( file_des,write_buf, strlen( write_buf )) !=
strlen( write_buf ) )
{
write( STDERR_FILENO, "WRITE ERROR!\n", 13
);
exit( 0 );
}
if ( lseek( file_des, 4, SEEK_END ) == -1
)
{
write( STDERR_FILENO, "SEEK ERROR!\n", 11 );
exit( 0
);
}
write( file_des, "AAAAAA", 6 );
lseek( file_des, 0,
SEEK_SET );
char read_buf[50];
if ( read( file_des, read_buf, 50 )
== -1 )
{
write( STDERR_FILENO, "READ ERROR!\n", 12 );
exit(
0 );
}
write( STDOUT_FILENO, read_buf, 50 );
close( file_des
);
exit( 1
);
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
这是一个简单的读写文件的例子,先创建一个文本文件,写入一些内容,再把文本内容输出到标准输出,下面开始分析这个例子:
1 int file_des =
open( "my_file.txt", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IXUSR |
S_IXOTH );
以读写方式打开一个新建文件 my_file.txt,指定其访问权限为
文件属主具有读,写,执行权限,组用户没有任何权限,其他用户只有执行权限。
open调用用于创建或打开文件,返回一个文件描述符。
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
int open( const char *path,
int oflags );
int open( const char *path, int oflags,
mode_t mode );
第一个oen用于打开已有的文件,第二个open用于创建新文件。
(1)oflags参数说明了文件的打开方式,值为以下一个或多个常量的“或”运算(这些常量定义在<fcntl.h>):
O_RDONLY
只读打开
O_WRONLY 只写打开
O_RDWR 读,写打开
以上常量为必选,且只能选一个,下列常量为可选:
O_APPEND 在文件尾端追加
O_TRUNC 将文件长度截短为0
O_CREATE 若文件不存在,按照参数mode指定的访问权限创建
O_EXCL
测试要创建的文件是否存在,和O_CREATE一起使用,使得文件的测试和创建是一个院子操作
(2)mode参数指定了新建文件的访问权限,值为以下一个或多个标识的“或”运算(这些标识定义在<sys/stat.h>):
S_IRUSR 文件属主具有读权限
S_IWUSR 文件属主具有写权限
S_IXUSR
文件属主具有执行权限
S_IRGRP 文件所属组具有读权限
S_IWGRP 文件所属组具有写权限
S_IXGRP 文件所属组具有执行权限
S_IROTH 其他用户具有读权限
S_IWOTH
其他用户具有写权限
S_IXOTH 其他用户具有执行权限
注:mode参数实际上是设置文件访问权限的请求,该请求是否被允许取决于此时umask的设置。
(3)如果两个程序同时打开同一个文件,会得到两个不同的文件描述符。如果都进行写操作,他们的数据将会相互覆盖,而不是交织在一起。两个文件对读写的起始位置(偏移值)也有各自的理解。文件锁可以防止此情况的发生,以后将会提到这个概念。
2 write( file_des,write_buf, strlen( write_buf )) != strlen( write_buf )
将缓冲区write_buf中的所有字节写入与文件描述符file_des关联的文件中,并且判断是否成功写入。
#include<unistd.h>
size_t write( int file_des,
const void *buf, size_t bytes );
(1)write的返回值可能会小于bytes,但这并不一定是个错误,需要检查全局变量errno来确定。
3 if ( lseek(
file_des, 4, SEEK_END ) == -1 )
{
write( STDERR_FILENO,
"SEEK ERROR!\n", 11 );
exit( 0 );
}
将文件的读写偏移量推进到超过文件结尾4个字节处,如果失败,像标准输出输出错误信息。
注:对于普通文件,可以顺序和随机访问;而对设备文件命名管道文件,通常只能顺序访问。在这两种访问方式中,内核把文件指针存放在打开文件对象中,也就是说,当前位置就是下一次进行读或者写操作的位置。顺序访问是文件的默认访问方式,即:read()和write()系统调用总是从文件指针的当前位置开始读或者写。为了修改文件指针的值,必须在程序中显式地调用lseek()系统调用。当打开文件的时候,内核让文件指针指向文件的第一个字节(偏移量为0)。
#include<unistd.h>
#include<sys/types.h>
off_t lseek( int file_des,
off_t off_set, int whence );
lseek用于设置文件的读写偏移量,返回新的读写偏移量。
file_des:打开文件的文件描述符
off_set:指定一个有符号整数值,用来计算文件指针的新位置。
whence :指定文件指针新位置的计算方式:可以是offset加0,表示文件指针从文件头移动;也可以是offset加文件指针的当前位置,表示文件从当前位置移动;还可以是offset加文件最后一个字节的位置,表示文件指针从文件末尾开始移动。
(1)off_t是一个与具体实现有关的类型,定义在<sys/types.h>中;
(2)whence的取值如下:
SEEK_SET 将文件的读写偏移量设置为距离文件开始处off_set个字节
SEEK_CUR 将文件的读写偏移量设置为当前值加上off_set,off_set可正可负
SEEK_END
将文件的读写偏移量设置为文件长度加上off_set,off_set可正可负
(3)当在超过文件尾端之后写入时,就会在文件中形成一个空洞。文件空洞并不占用磁盘空间,处理方式与文件系统的实现有关。可以用$od -c file
查看空洞文件的内容。
(4)STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO分别对应程序的标准输入,标注输出,标准出错。这些常量定义在<unistd.h>中。
4 #include<unistd.h>
int close( int file_des
);
终止文件描述符file_des与对应文件的关联。
(1)文件描述符被释放并重新利用;
(2)文件关闭后,进程还会释放加在文件上的所有记录锁;
(3)进程终止后,内核会自动关闭所有打开的文件。
5 其他与文件有关的系统调用
(1)#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
int fstat( int
file_des, struct stat *buf );
int stat( const char
*path, struct stat *buf );
int lstat( const char
*path, struct stat *buf );
这三个系统调用用于获取文件的信息并填充在struct
stat中,成功返回0,出错返回-1。当path指向的对象是符号链接时,stat返回的是该链接指向的文件的信息,而lstat返回的是该链接的信息。这三个系统调用将在以后作为一个专题来讨论。
(2)#include<unistd.h>
int dup( int file_des
);
int dup2( int file_des, int file_des2
);
dup系统调用复制文件描述符file_des,返回一个新的最小值的可用文件描述符。通过两个或多个文件描述符可以实现在文件的不同位置读写数据。dup2明确指定将file_des复制为file_des2。这在通过管道进行进程间通信时很有用。