系统调用及底层文件访问

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

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误
系统调用

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

下面是write系统调用的原型:

#include<unistd.h>
size_t write(int fildes,const coid *buf,size_t nbytes);

编写第一个程序write.c吧

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
	if(write(1,"hello\n",6)!=6)
	{
		write(2,"write error\n",12);
	}
	exit(0);
}

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

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


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

#include<unistd.h>
size_t read(int fildes,void *buf,size_t nbytes);

示例

#include<unistd.h>
#include<stdlib.h>
int main()
{
	char buffer[128];
	int fd;
	fd=read(0,buffer,128);
	if(read==-1)
		write(2,"hello\n",6);
	if((write(1,buffer,fd))!=fd)
		write(2,"write error\n",12);
	exit(0);
}

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

#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int open(const char* path,int oflags);
int open(const char* path,int oflags,mode_t mode);

严格来说,在遵循POSIX规范的系统上,使用open系统调用并不需要包括问头肩sys/types.h和sys/stat.h。但在某些UNIX系统上,它们可能是必不可少的。

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

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

  • O_RDONLY ------ 以只读方式打开
  • O_WRONLY ------ 以只写方式打开
  • O_RADW ------ 以读写方式打开

open调用还可以在oflags参数中包括下列可选模式的组合(使用按位或

  • O_APPEND-----把写入数据追加在文件末尾
  • O_TRUNC------把文件长度设置为0,丢弃已有的内容
  • O_CREAT------如果需要,就按参数mode中给出的访问模式创建文件
  • O_EXCL-------与O_CREAT一起使用,确保调用者创建出文件。open调用是一个原子操作,也就是说,它只执行一个函数调用。使用这个可选模式可以防止两个程序同时创建同一个文件。如果文件已经存在,则open调用失败。

open调用在成功时返回一个新的文件描述符(总是一个非负整数),在失败时返回-1并设置全局变量errno来指明失败的原因。新的文件描述符总是使用未用的描述符的最小值。例如,如果一个程序关闭了它的标准输出,然后再次调用open,文件描述符1就会被重新使用,并且标准输出将被有效地重定向到另一个文件或设备。

POSIX规范还标准化了一个creat调用,但它并不常用。这个调用不仅会像我们预期那样创建文件,还会打开文件。它的作用相当于以oflags标志O_CREAT|O_WRONLY|O_TRUNC来调用open。

open的第三个参数mode是几个标志按位或得到的,这些标志在头文件sys/stat.h中定义,如图:

  • S_IRUSR-------读权限,文件属主
  • S_IWUSR------写权限,文件属主
  • S_IXUSR------执行权限,文件属主
  • S_IRGRP-----读权限,文件所属组
  • S_IWGRP----写权限,文件所属组
  • S_IXGRP-----执行权限,文件所属组
  • S_IROTH-----读权限,其他用户
  • S_IWOTH----写权限,其他用户
  • S_IXOTH----执行权限,其他用户

示例

#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
int main()
{
    int fd=open("a.txt",O_RDWR|O_CREAT|O_TRUNC,0600);
    assert(fd!=-1);
    
    write(fd,"hello",5);
    close(fd);
    
    exit(0);
}
文件描述符

在C程序中,文件由文件指针或者文件描述符表示。ISO C的标准I/O库函数(fopen,fclose,fread,fwrite,fscanf,fprintf等)使用文件指针,UNIX的I/O函数(open,close,read,write,ioctl)使用文件描述符。

下面来说一下文件描述符是如何工作的。

文件描述符相当于一个逻辑句柄,而open,close等函数则是将文件或者物理设备与句柄相关联。句柄是一个整数,可以理解为进程特定的文件描述符表的索引。

文件描述符表:用户区的一部分,除非通过使用文件描述符的函数,否则程序无法对其进行访问。对进程中每个打开的文件,文件描述符表都包含一个条目。

系统文件表:为系统中所有的进程共享。对每个活动的open,他都包含一个条目。系统文件表的每个条目都包含文件偏移量、访问模式(读、写、可读可写)以及指向它的文件描述符表的条目奇计数。

内存索引节点表:对系统中的每个活动的文件(被某个进程打开了),内存中索引节点都包含一个条目。几个系统文件表条目可能对应于同一个内存索引节点表(不同进程打开同一个文件)。

举例:

myfd = open( “/home/lucy/my.dat”, O_RDONLY);

执行后,三个表的对应关系如下:
在这里插入图片描述
系统文件表包含一个偏移量,给出了文件当前的位置。若两个进程同时打开一个文件,做读操作,每个进程都有自己相对于文件的偏移量,而且读入整个文件是独立于另一个进程的;如果两个进程打开同一个文件做写操作,写操作是相互独立的,每个进程都可以重写另一个进程的内容。

如果上面进程在open以后又执行了close函数,操作系统会删除文件描述符的第四个条目3,和系统文件表的对应条目(若指向它的描述符表唯一),并对内存索引节点表条目中的计数减1,如果减后为0,说明没有其他进程链接此文件,就将索引节点表也删除,而这里进程B也在open这个文件,所以索引节点表保留。

文件描述符的继承
通过fork()创建子进程时,子进程继承父进程环境和上下文的大部分内容的拷贝,其中就包括文件描述符表。

1.对于父进程在fork()之前打开的文件来说,子进程都会继承,与父进程共享相同的文件偏移量
在这里插入图片描述
系统文件表位于系统空间中,不会被fork()复制,但是系统文件表中的条目会保存指向它的文件描述符表的计数,fork()时需要对这个计数进行维护,以体现子进程的文件描述符表也指向它。程序关闭文件时,也是将系统文件表条目内部的计数减1,若减为0就删除。

总结一下:父进程打开某文件,fork()后,子进程也可以访问该问渐渐,即父子进程共享该文件;先fork,在打开文件,则各自独享。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值