IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 文件描述符,Linux操作系统文件的系统调用 open read write close -> 正文阅读

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

一、文件系统调用

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 }

?库函数和系统调用的区别

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

库函数的实现在库中。

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

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-19 12:06:11  更:2022-05-19 12:07:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:56:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码