底层文件访问

底层文件访问,第1张

底层文件访问 底层文件访问

每个运行中的程序被称为进程( process),它有一些与之关联的文件描述符。这是一些小值整数,你可以通过它们访问打开的文件或设备。有多少文件描述符可用取决于系统的配置情况。当一个程序开始运行时,它一般会有3个已经打开的文件描述符:

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误

你可以通过系统调用open把其他文件描述符与文件和设备相关联,稍后讲解。其实使用自动打开的文件描述符就已经可以通过write系统调用来创建一些简单的程序了。

write系统调用

系统调用write的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。它返回实际写入的字节数。如果文件描述符有错或者底层的设备驱动程序对数据块长度比较敏感,该返回值可能会小于nbytes。如果这个函数返回0,就表示未写入任何数据;如果它返回的是-1,就表示在write调用中出现了错误,错误代码保存在全局变量errno里。
下面是write系统调用的原型:

#include 
size_t write(int fildes,const void *buf, size_t nbytes);

有了这些知识,你就可以编写第一个程序simple_write.c了:

#inelude 
#inciude 
int main()
{
	if ((write(1, "Here is some datan", 18)) != 18)
		write(2, "A write error has occurred on file descriptor 1n",46);
		exit(0);
}

这个程序只是在标准输出上显示一条消息。当程序退出运行时,所有已经打开的文件描述符都会自动关闭,所以你不需要明确地关闭它们。但处理被缓冲的输出时,情况就不一样了。

需要再次提醒的是,write可能会报告写入的字节比你要求的少。这并不一定是个错误。在程序中,你需要检查errno以发现错误,然后再次调用write写入剩余的数据。

read系统调用

系统调用read的作用是:从与文件描述符fildes相关联的文件里读入nbytes个字节的数据,并把它们放到数据区buf中。它返回实际读入的字节数,这可能会小于请求的字节数。如果read调用返回0,就表示未读入任何数据,己到达了文件尾。同样,如果返回的是-1,就表示read调用出现了错误。

#include 
size_t read(int fildes, void *buf, size_t nbytes);

下面这个程序simple_read.c把标准输入的前128个字节复制到标准输出。如果输入少于128个字节,就把它们全体复制过去。

#include 
#include 
int main()
{
	char buffer[128];
	int nread;
	nread = read(0,buffer, 128);
	if (nread ==-1)
		write(2,"A read error has occurredn" ,26);
	if ((write(1,buffer,nread)) != nread)
		write(2,"A write error has occurredn",27);
	exit(0);
}

我们可以通过echo或重定向为它提供输入。

open系统调用

为了创建一个新的文件描述符,你需要使用系统调用open。

#include 
#include 
#include 
int open(const char *path, int oflags);
int open(const char *path,int oflags,mode_t mode) ;

简单地说,open建立了一条到文件或设备的访问路径。如果调用成功,它将返回一个可以被read,write和其他系统调用使用的文件描述符。这个文件描述符是唯一的,它不会与任何其他运行中的进程共享。如果两个程序同时打开同一个文件,它们会分别得到两个不同的文件描述符。如果它们都对文件进行写 *** 作,那么它们会各写各的,它们分别接着上次离开的位置继续往下写。它们的数据不会交织在一起,而是彼此互相覆盖。两个程序对文件的读写位置(偏移值)不同。你可以通过使用文件锁功能来防止出现冲突。

准备打开的文件或设备的名字作为参数path传递给函数,oflags参数用于指定打开文件所采取的动作。
oflags参数是通过必需文件访问模式与其他可选模式相结合的方式来指定的。open调用必须指定表中所示的文件访问模式之一。

open调用还可以在oflags参数中包括下列可选模式的组合(用“按位或” *** 作)。O_APPEND:把写入数据追加在文件的末尾。
O_TRUNC:把文件长度设置为零,丢弃已有的内容。
O_CREAT:如果需要,就按参数mode中给出的访问模式创建文件。
O_EXCL:与O_CREAT一起使用,确保调用者创建出文件。open调用是一个原子 *** 作,也就是说,它只执行一个函数调用。使用这个可选模式可以防止两个程序同时创建同一个文件。如果文件己经存在,open调用将失败。

open调用在成功时返回一个新的文件描述符(它总是一个非负整数),在失败时返回-1并设置全局变量errno来指明失败的原因。我们将在本章后面对errno做进一步讨论。新文件描述符总是使用未用描述符的最小值,这个特征在某些情况下非常有用。例如,如果一个程序关闭了它的标准输出,然后再次调用open,文件描述符l就会被重新使用,并且标准输出将被有效地重定向到另一个文件或设备。

访问权限的初始值

当你使用带有O_CREAT标志的open调用来创建文件时,你必须使用有3个参数格式的open调用。第三个参数mode是几个标志按位或后得到的,这些标志在头文件sys/stat.h.中定义,如下所示。
S_IRUSR:读权限,文件属主。
S_IWUSR:写权限,文件属主。
S_IXUSR:执行权限,文件属主。
S_IRGRP:读权限,文件所属组。
S_IWGRP:写权限,文件所属组。
S_IXGRP:执行权限,文件所属组。
S_IROTH:读权限,其他用户。
S_lWOTH:写权限,其他用户。
S_IXOTH:执行权限,其他用户。
当然我们更多使用的是(数值标记)

有几个因素会对文件的访问权限产生影响。首先,指定的访问权限只有在创建文件时才会使用。其次,用户掩码(由shell的umask命令设定)会影响到被创建文件的访问权限。open调用里给出的mode值将与当时的用户掩码的反值做AND *** 作。因此,open和creat调用中的标志实际上是发出设置文件访问权限的请求,所请求的权限是否会被设置取决于当时umask的值。

close系统调用

你可以使用close调用终止文件描述符fildes与其对应文件之间的关联。文件描述符被释放并能够重新使用。close调用成功时返回o,出错时返回-1。

#include 
int close(int fildes) ;

注意,检查close调用的返回结果非常重要。有的文件系统,特别是网络文件系统,可能不会在关闭文件之前报告文件写 *** 作中出现的错误,这是因为在执行写 *** 作时,数据可能未被确认写入。

ioctl系统调用

ioctl调用有点像是个大杂烩。它提供了一个用于控制设备及其描述符行为和配置底层服务的接口。终端、文件描述符、套接字甚至磁带机都可以有为它们定义的ioctl,具体细节可以参考特定设备的手册页。下面是ioctl的原型:

#include 
int ioctl(int fildes, int cmd, ...);

ioctl对描述符fildes引用的对象执行cmd参数中给出的 *** 作。根据特定设备所支持 *** 作的不同,它还可能会有一个可选的第三参数。
例如,在Linux系统上对ioctl的如下调用将打开键盘上的LED灯
:ioctl(tty_fa, KDSETLED,LED_NUM | LED_CAP|LED_SCR);

其他与文件管理有关的系统调用

还有许多其他的系统调用能够 *** 作这些底层文件描述符。通过它们,程序可以控制文件的使用方式和返回文件的状态信息。

lseek系统调用

lseek系统调用对文件描述符fildes的读写指针进行设置。也就是说,你可以用它来设置文件的下一个读写位置。读写指针既可被设置为文件中的某个绝对位置,也可以把它设置为相对于当前位置或文件尾的某个相对位置。

#include 
#include 
off_t lseek(int fildes,off_t offset, int whence);

offset参数用来指定位置,而whence参数定义该偏移值的用法。whence可以取下列值之一。
SEEK_SET:offset是一个绝对位置。
SEEK_CUR: offset是相对于当前位置的一个相对位置。
SEEK_END:offset是相对于文件尾的一个相对位置。

lseek返回从文件头到文件指针被设置处的字节偏移值,失败时返回-1。参数offset的类型off_t是一个与具体实现有关的整数类型,它定义在头文件sys/types.h中。

fstat、stat和lstat系统调用

fstat系统调用返回与打开的文件描述符相关的文件的状态信息,该信息将会写到一个buf结构中,buf的地址以参数形式传递给fstat.
下面是它们的原型:

#include 
#include 
#include 
int fstat(int fi1des, struct stat *buf);
int stat(const char *path, struct stat *buf) ;
int lstat(const char *path, struct stat *buf);

注意;包含头文件sys/types.h是可选的,但由于一些系统调用的定义针对那些某天可能会做出调整的标准类型使用了别名,所以但在程序中使用系统调用时,我们还是推荐将这个头文件包含进去。
相关函数stat和lstat返回的是通过文件名查到的状态信息。它们产生相同的结果,但当文件是一个符号链接时,lstat返回的是该符号链接本身的信息,而stat返回的是该链接指向的文件的信息。
stat结构的成员在不同的类UNIX系统上会有所变化,但一般会包括表中所示的内容。

stat结构中返回的st_mode标志还有一些与之关联的宏,它们定义在头文件sys/stat.h中。这些宏包括对访问权限、文件类型标志以及一些用于帮助测试特定类型和权限的掩码的定义。
访问权限标志与前面介绍的ooen系统调用中的内容是一样的。文件类型标志如下:
S_IFBLK:文件是一个特殊的块设备。
S_IFDIR:文件是一个目录。
S_IFCHR:文件是一个特殊的字符设备。
S_IFIFO:文件是一个FIFO(命名管道)。
S_IFREG:文件是一个普通文件。
S_FLNK:文件是一个符号链接。
以下是其他模式标志。
S_ISUID:文件设置了SUID位。
S_ISGID:文件设置了SGID位。
下面列出了用于解释st_mode标志的掩码。
S_IFMT:文件类型。
S_IRWXU:属主的读/写/执行权限。
S_IRWXG:属组的读/写/执行权限。
S_IRwXO:其他用户的读/写/执行权限。
下面是一些用来帮助确定文件类型的宏定义。它们只是对经过掩码处理的模式标志和相应的设备类型标志进行比较。
S_ISBLK:测试是否是特殊的块设备文件。v
S_ISCHR:测试是否是特殊的字符设备文件。
S_ISDIR:测试是否是目录。
S_ISFIFO:测试是否是FIFO。
S_ISREG:测试是否是普通文件。
S_ISLNK:测试是否是符号链接。

dup和dup2系统调用

dup系统调用提供了一种复制文件描述符的方法,使我们能够通过两个或者更多个不同的描述符来访问同一个文件。这可以用于在文件的不同位置对数据进行读写。dup系统调用复制文件描述符fildes,返回一个新的描述符。dup2系统调用则是通过明确指定目标描述符来把一个文件描述符复制为另外一个。
它们的原型如下:

#include 
int dup (int fi1des);
int dup2 (int fi1des, int fildes2);

当你通过管道在多个进程间进行通信时,这些调用也很有用。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5504434.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存