Linux的I/O系统调用函数之read,open,write

**

(一)open

**
进程是通过调用open函数来打开一个已存在的文件或者创建一个新文件的。

int open(char *filename,int flags,mode_t mode);

open函数将filename转换为一个文件描述符,并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。

1.flags参数指明了进程打算如何访问这个文件:

O_RDONLY只读
O_WRONLY只写
O_RDWR可读可写

下面的代码说明如何以读的方式打开一个已存在的文件:

fd=open("foo.txt",O_RDONLY,0);

flags参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:

O_CREAT如果文件不存在,就创建它的一个截断的(空)文件
O_TRUNC如果文件已存在,就截断它
O_APPEND在每次写操作前,设置文件位置到文件的结尾处

下面的代码说明的是如何以读的方式打开一个已存在文件,并在后面添加一些数据:

fd=open("foo.txt",O_WRONLY|O_APPEND,0);

mode参数指定了新文件的访问权限位。

掩码描述
S_IRUSR使用者(拥有者)能够读这个文件
S_IWUSR使用者(拥有者)能够写这个文件
S_IXUSR使用者(拥有者)能够执行这个文件
S_IRGRP拥有者所在组的成员能够读这个文件
S_IWGRP拥有者所在的组成员能够写这个文件
S_IXGRP拥有者所在组的成员能够执行这个文件
S_IROTH其他人(任何人)能够读这个文件
S_IWOTH其他人(任何人)能够写这个文件
S_IXOTH其他人(任何人)能够执行这个文件

作为上下文的一部分,每个进程都有一个umask,它是通过调用umask函数来设置的。当进程通过带某个参数的open函数用来创建一个新文件时,文件的访问权限位被设置为mode&~umask。例如,假设我们给定下面的mode和umask默认值:
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_TROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
接下来,下面的代码片段一个新文件,文件的拥有者有读写权限,而所有其他的用户都有读权限:
umask(DEF_UMASK);
fd=open(“foo.txt”,O_CREAT|O_TRUNC|O_WRONLY,DEF_MODE);

int main()
{
    int fd1,fd2;
    fd1=open("foo.txt",O_RDONLY,0);
    close(fd1);
    fd2=open("baz.txt",O_RDONLY,0);
    printf("fd2=%d\n",fd2);
    exit(0);
}

结论:fd2=3

在对fd1进行open时,fd1=3,进行close时,3被释放了
所以在对fd2进行open时,fd2=3了。

(二)read和write

读文件:ssize_t read(int fd, void *buf, size_t n);

read函数从描述符为fd的当前文件位置复制最多n个字节到内存位置buf。返回值-1表示一个错误,而返回值0表示EOF。否则,返回值表示的实际传送的字节数量。

写文件:ssize_t write ( int fd, const void *buf , size_t n );

write函数从内存位置buf复制最多n个字节到描述符fd的当前文件位置。

(size_t被定义为unsigned long,而ssize_t(有符号的大小)被定义long)

(共享文件)
可以用许多不同的方式来共享Linux文件。内核用三个相关的数据结构来表示打开的文件:

描述符表每个进程都有它独立的描述符表,它的表项是由进程打开的文件描述符来索引的。每个打开的描述符表指向文件表中的一个表项
文件表打开文件的集合是由一张文件表来表示,所有的进程共享这张表
v-node表同文件表一样,所有的进程共享这张v-node表,每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员

下图展示了一个示例,其中描述符1和4通过不同的打开文件表表项来引用两个不同的文件。这是一种典型的情况,没有共享文件,并且每个描述符对应一个不同的文件。

在这里插入图片描述
图10-12
下图所示,多个描述符也可以通过不同的文件表表项来引用同一个文件。例如,如果以同一个filename调用open函数两次,就会发生这种情况。关键思想是每个描述符都有它自己的文件位置,所以对不同描述符的读操作可以从文件的不同位置获取数据。

在这里插入图片描述
图10-13

我们也能理解父子进程是如何共享文件的。假设在调用fork之前,父进程有如图10-12所示的打开文件。然后,图10-14展示了调用fork后的情况。子进程有一个父进程描述表的副本。父子进程共享相同的打开文件表集合,因此共享相同的文件位置。一个很重要的结果就是,在内核删除相应文件表表项之前,父子进程必须都关闭了它们的描述符。

在这里插入图片描述

关于dup2:

int dup2(int oldfd,int newfd);

dup2函数复制描述表表项oldfd到描述表项newfd,覆盖描述表表项newfd以前的内容。如果newfd已经打开,dup2会在复制oldfd之前关闭newfd.

argv[1]=abcde
例子一

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl,h>
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t n);
ssize_t write(int fd,const void *buf,size_t  n);
#include<stdio.h>
int main(int argc,char *argv[])
{
    int fd1,fd2,fd3;
    char c1,c2,c3;
    char *fname=argv[1];
    fd1=open(fname,O_RDONLY,0);
    fd2=open(fname,O_RDNOLY,0);
    fd3=open(fname,O_RDONLY,0);
    dup2(fd2,fd3);
    read(fd1,&c1,1);
    read(fd2,&c2,1);
    read(fd3,&c3,1);
    printf("c1=%c,c2=%c,c3=%c\n",c1,c2,c3);
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
 }

在这里插入图片描述对fd1来说使用open得到3,对fd2来说使用open得到4,对fd3来使用open得到5,然后使用dup2(fd2,fd3),实际上对fd3的操作就是读对fd2的操作。这时使用read,fd1读到了a,fd2读到了a,因为此时对fd3的操作相当于对fd2的操作,所以按照顺序fd3读到了b。

例子二

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl,h>
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t n);
ssize_t write(int fd,const void *buf,size_t  n);
#include<stdio.h>
int main(int argc,char *argv[])
{
   int fd1;
   int s=getpid()&0x1;
   char c1,c2;
   char *fname=argv[1];
   fd1=open(fname,O_RDONLY,0);
   read(fd1,&c1,1);
   if(fork()){
       sleep(s);
       read(fd1,&c2,1);
       printf("Parent:c1=%c,c2=%c\n",c1,c2);
       }else{
              sleep(1-s);
              read(fd1,&c2,1);
              printf("Child:c1=%c,c2=%c\n",c1,c2);
          }
          return 0;
  }

在这里插入图片描述

因为没有将s打印出来,所以通过分析及视觉,子进程打印结束后,大概等待一秒,父进程才打印出来。,read在fork之前进行了第一次读,c1没有争议的读到了a,此时光标在‘a’后。子进程因为没有sleep,从而先比父进程先读,读到了b,此时光标在‘b’后。父进程sleep结束后,读取c2,也就读到了c。

例子三

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl,h>
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t n);
ssize_t write(int fd,const void *buf,size_t  n);
#include<stdio.h>
int main(int argc,char *argv[])
{
      int fd1,fd2,fd3;
      char *fname=argv[1];

     //创建一个名为fname的新文件,可读可写,在里面写入pqrs
      fd1=open(fname,O_CREAT|O_TRUNC|O_RDWR,S_IRUSR|S_IWUSR);
      write(fd1,"pqrs",4);

    //以追加写的方式打开刚刚创建号的fname的文件,在里面写入jklmn
      fd3=open(fname,O_APPEND|O_WRONLY,0);
      write(fd3,"jklmn",5);

    //dup,使之后对fd2的操作都变为对fd1的操作
      fd2=dup(fd1);
      write(fd2,"wxyz",4);
      write(fd3,"ef",2);
      close(fd1);
      close(fd2);
      close(fd3);
      return 0;
      }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述所以最后打印出来pqrswxyznef

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值