文件描述符,Linux操作系统文件的系统调用 open read write close

  • Post author:
  • Post category:linux


一、文件系统调用

fork——父进程打开的文件fork复制到子进程中,子进程也可以访问。

c 语言使用 fopen(),打开一个文件,文件类型为

FILE*

fclose()关闭文件。

Linux通过


操作系统


的子模块——


文件系统





管理文件


通过文件id号来管理文件,每个文件的ID号是唯一的

一、 open

c语言中使用fopen打开文件,fopen是库函数。


Linux 通过open打开文件,是一种

系统调用,在内核中实现。

fopen->open(),fopen底层调用open()

包含头文件


#include<sys/types.h>



#include<sys/stat.h>



#include<fcntl.h>

函数原型:

int open(const char *pathname,int flags);//路径名称,标志位(打开方式)
int open(const char *pathname,int flags,mode_t mode);//路径名称,标志位,文件权限

int

fd

= open(const char *patnname,int flages);


open的返回值(int)fd 称为

文件描述符

,通过返回值操作文件。


mode:仅当创建新文件时才使用

,用于指定文件的

访问权限


pathname

:待打开/创建文件的路径和名称;


flags:打开标志,

用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。

O_RDONLY       只读模式

O_WRONLY      只写模式

O_RDWR          读写模式

以上三者是互斥的,即不可以同时使用。

打开/创建文件时,至少得使用上述三个常量中的一个。以下常量是选用的:

O_APPEND         每次写操作都

写入文件的末尾


O_CREAT            如果指定文件不存在,则

创建这个文件


O_EXCL               如果要创建的文件已存在,则返回 -1,并且修改 errno 的值

O_TRUNC           如果文件存在,并且以只写/读写方式打开,

清空文件全部内容 ,重新写入


O_NOCTTY         如果路径名指向终端设备,不要把这个设备用作控制终端。

O_NONBLOCK   如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)。

//以下用于同步输入输出

O_DSYNC          等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。

O_RSYNC          read 等待所有写入同一区域的写操作完成后再进行

O_SYNC            等待物理 I/O 结束后再 write,包括更新文件属性的 I/O


当你使用带有O_CREAT标志的open调用来创建文件时,你必须使用有3个参数格式的open调用。第三个参数mode是几个标志按位或后得到的,

这些标志在头文件sys/stat.h中定义,如下所示:

S_IRWXU   00700  user (file owner) has read, write, and execute permission

S_IRUSR    00400  读权限,文件属主

S_IWUSR   00200  写权限,文件属主

S_IXUSR    00100  执行权限,文件属主

S_IRWXG   00070  group has read, write, and execute permission

S_IRGRP    00040  读权限,文件所属组

S_IWGRP   00020  写权限,文件所属组

S_IXGRP    00010  执行权限,文件所属组

S_IRWXO  00007  others have read, write, and execute permission

S_IROTH   00004  读权限,其它用户

S_IWOTH  00002  写权限,其它用户

S_IXOTH   00001  执行权限,其它用户


返回值

:成功则


返回文件描述符


,否则返回 -1。  返回文件描述符(整型变量0~255)。由open 返回的文件描述符一定是该进程尚未使用的最小描述符。只要有一个权限被禁止则返回-1。


错误代码

:(均已E开头,将其去掉就是有关于错误的方面的单词或单词的缩写)

EEXIST 参数pathname 所指的文件已存在,却使用了O_CREAT和O_EXCL旗标。

EACCESS 参数pathname所指的文件不符合所要求

测试

的权限。

EROFS 欲测试写入权限的文件存在于只读文件系统内。

EFAULT 参数pathname指针超出可存取内存空间。

EINVAL 参数mode 不正确。

ENAMETOOLONG 参数pathname太长。  ENOTDIR 参数pathname不是目录。

ENOMEM 核心内存不足。

ELOOP 参数pathname有过多符号连接问题。

EIO I/O 存取错误。

二、write

包含头文件


#include<unistd.h>

函数原型:


ssize_t write(int fd,const void *buf, size_t count)


;//fd  文件描述符,要写入数据的文件 , *buf 文件数据内容 ,count计划写入字节数 ,ssize_t实际写入字节数




ssize_t write(int fd, const void *buf, size_t count);




参数



fd:对应打开的文件描述符。

buf:存放数据的空间需要输出的缓冲区

count:计划一次从文件中读多少字节数据最大输出字节计数


返回值

:成功返回实际写入的字节数,出错返回-1并设置errno

三、close

关闭文件



int close(int fd);

参数:

fd 要关闭的文件描述符

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     int fd = open("a.txt",O_WRONLY|O_CREAT,0600);//打开(默认就是当前路径)文件a.txt,O_WRONLY只写,O_CREAT,如果文件不存在,则创建此文件,0600文件权限,自己可读可写,其他没有权限
  9     if(fd == -1)
 10     {
 11         exit(1);//如果失败,则返回1
 12     }
 13  
 14     write(fd,"hello",5);//向fd文件中写入hello,计划输入5个字节数据
 15  
 16     close(fd);//关闭文件
 17  
 18     exit(0);
 19 }

四、read


读数据

包含头文件

#include <unistd.h>

函数原型:


ssize_t read(int fd, void *buf, size_t count);


// ssize_t实际读数据字节数,fd 文件描述符 ,buf: 为读出数据的缓冲区; count: 计划读数据数,为每次读取的字节数(是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移)



ssize_t read(int fd, void *buf, size_t count);




参数

fd :对应打开的文件描述符

buf:存放待写入的数据,需要读取的缓冲区

count:计划一次向文件中写入多少数据最大读取字节计数


返回值:

成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0





ssize_t 返回值为0 ,说明:1.文件本身为0;2.读到了文件末尾



返回值为0,读完文件的唯一标志,判断循环的条件

文件读与写,有一个偏移量count的存在,读写的时候系统会记住位置,会一直往后进行

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     int fd = open("a.txt",O_RDONLY);//打开文件a.txt,此文件已存在,只读模式
  9     if(fd == -1)
 10     {
 11         exit(1);
 12     }
 13 
 14     char buff[128] = {0};
 15     int n =read(fd,buff,127);
 16     printf("n =%d,buff = %s\n",n,buff);
 17 
 18     close(fd);
 19     exit(0);
 20 }
~      

二、文件复制

两个文件要同时打开,文件A需以只读的方式打开,新文件B需要以写的方式打开。

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     int fdr = open("passwd",O_RDONLY);//原文件以只读的方式打开
  9     int fdw = open("newfile",O_WRONLY);//复制的文件以只写的方式打开,若文件不存在,则可以使用O_CREAT创建
 10 
 11     if(fdr == -1 ||fdw == -1)
 12     {
 13         exit(1);
 14     }
 15 
 16     char buff[128]={0};//保存实际数据
 17     int num =0;//保存读到的实际字节数
 18     while((num = read(fdr,buff,128)) >0)//read返回值为0,则读完了
 19     {
 20         write(fdw,buff,num);//写入实际读的数据
 21     }
 22 
 23     close(fdw);
 24     close(fdr);
 25     exit(0);
 26 }


main函数传参形式

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main(int argc,char *argv[],char * envp[])
  7 {
  8     if(argc != 3)
  9     {
 10         printf("mycp arg err\n");//main函数传参,复制必须两个文件,再加上main可执行文件本身,总共三个参数
 11         exit(1);
 12     }
 13     int fdr = open(argv[1],O_RDONLY);//原文件以只读的方式打开
 14     if(fdr == -1)
 15     {
 16         printf("source erro\n");//原文件打开失败
 17         exit(1);
 18     }
 19     int fdw = open(argv[2],O_WRONLY|O_CREAT,0600);//复制的文件以只写的方式打开
 20 
 21     if(fdw == -1)
 22     {
 23         printf("newfile erro\n");
 24         exit(1);
 25     }
 26 
 27     char buff[128]={0};//保存实际数据
 28     int num =0;//保存读到的实际字节数
 29     while((num = read(fdr,buff,128)) >0)//read返回值为0,则读完了
 30     {
 31         write(fdw,buff,num);//写入实际读的数据
 32     }
 33 
 34     close(fdw);
 35     close(fdr);
 36     exit(0);
 37 }
 38 

三、fork

在c语言中,使用库函数

类型
标准输入 键盘 FILE* stdin
标准输出 屏幕 FILE* stdout
标准错误输出 屏幕 FILE* stderr

在Linux中,万物皆文件


0 1 2 文件描述符一直存在。

a.txt文件


先open文件再fork

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     int fd = open("a.txt",O_RDONLY);//打开文件a.txt
  9     if(fd == -1)
 10     {
 11         exit(1);
 12     }
 13 
 14     pid_t pid =fork();//fork()一次
 15     if(pid == -1)
 16     {
 17         exit(1);
 18     }
 19     if(pid ==0)//子进程
 20     {
 21         char buff[32] = {0};
 22         read(fd,buff,1);//读取一个字节的数据
 23         printf("child buff = %s\n",buff);
 24         sleep(1);
 25         read(fd,buff,1);
 26         printf("child buff = %s\n",buff);
 27     }
 28     else//父进程
 29     {
 30 
 31         char buff[32] = {0};
 32         read(fd,buff,1);
 33         printf("parent buff = %s\n",buff);
 34         sleep(1);
 35         read(fd,buff,1);
 36         printf("parent buff = %s\n",buff);
 37     }
 38     close(fd);
 39     exit(0);
 40 }


先打开文件,再fork(),文件描述符也会被复制给子进程,父子进程通过同一文件描述符访问文件,

父子进程共享文件。


先fork再open文件

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     pid_t pid =fork();
  9     if(pid == -1)
 10     {
 11         exit(1);
 12     }
 13 
 14     int fd = open("a.txt",O_RDONLY);
 15     if(fd == -1)
 16     {
 17         exit(1);
 18     }
 19 
 20     if(pid ==0)
 21     {
 22         char buff[32] = {0};
 23         read(fd,buff,1);
 24         printf("child buff = %s\n",buff);
 25         sleep(1);
 26         read(fd,buff,1);
 27         printf("child buff = %s\n",buff);
 28     }
 29     else
 30     {
 31 
 32         char buff[32] = {0};
 33         read(fd,buff,1);
 34         printf("parent buff = %s\n",buff);
 35         sleep(1);
 36         read(fd,buff,1);
 37         printf("parent buff = %s\n",buff);
 38     }
 39     close(fd);
 40     exit(0);
 41 }


先fork再打开文件,对于父子进程访问文件来说,

父子进程是相互独立的

,父子进程会有各自的文件描述符,父子进程通过各自的文件描述符访问文件。

write()输出数据

使用printf()->write()系统调用,在内核中

printf(“hello”)->write(1,”hello”,5)

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     write(1,"hello",5);
  9  
 10  
 11     exit(0);
 12 }
      

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     printf("A");//printf是先将数据攒在缓冲区中,然后调用write()输出数据
  9     write(1,"B",1);//write()不会影响缓冲区,有数据直接输出
 10     fork();//printf没有\n,缓冲区有数据,fork也复制了缓冲区的数据
 11 
 12 
 13     exit(0);
 14 }

read()键盘读取数据

getchar(),sacnf_s() ->read()

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<fcntl.h>
  5 
  6 int main()
  7 {
  8     char buff[128]={0};
  9     read(0,buff,127);
 10     printf("buff = %s\n",buff);
 11     
 12     
 13     
 14     
 15     
 16   
 17    
 18     
 19     
 20     exit(0);
 21 }

库函数和系统调用的区别


系统调用在内核中实现,一旦系统调用就会产生中断,进入内核空间,陷入内核(从用户态切换到内核态)。


库函数的实现在库中。



但凡涉及操作系统的硬件资源,但凡需要操作内核中的数据结构,一律需要内核去做,用户没有权限。



版权声明:本文为swint_er原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。