1open/close函数

查看系统调用的命令:

man 2 functionName

主要查看函数名称,调用方法,描述,参数,返回值等等,先学习如何使用,在学习内部原理

文件描述符是一个整数,会一直伴随着linux各种系统调用

函数名称和功能:open 打开或者创建一个文件

头文件:

#include <sys/types.h>

#include <sys/stat.h>

(上面两个头文件可以用#include <unistd.h>代替)

#include <fcntl.h>

函数原型:

int open(const char* pathname, int flags );

int open(const char *pathname, int flags, mode_t mode);

参数:

*char pathname:**代表文件的目录;

flags: O_RDONLY 只读 O_WRONLY 只写 O_RDWR 可读可写

​ O_APPEND 追加 O_CREAT 创建新文件 O_EXCL 判断文件是否存在 O_TRUNC 将文件阶段为0,清空 O_NONBLOCK 非阻塞

头文件位于 #include <fcntl.h>中

创建文件时,指定文件访问权限。权限同时受到umask影响。结论为(umask默认创建文件权限的反码):

文件权限=mode&~umask

返回值:返回一个int,文件描述符,返回-1代表操作失败

#include <errno.h> errno 操作系统的全局变量???

#include <string.h> char* strerror(int errnum) 打印错误的具体信息

mode:

使用的前提是,参数2指定了O_CREAT。参数3取值取8进制数,用来描述文件的访问权限,rwx, 0664

最终创建得到的文件权限=mode&(~umask)

open函数常见错误:

1)打开文件不存在;

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char** argv)
{int fd;fd=open("./dict.cd",O_RDONLY);printf("fd=%d\n",fd);printf("errno=%d\n",errno);printf("%s\n",strerror(errno));close(fd);return 0;
}
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ ./open
fd=-1
errno=2
No such file or directory

2)以写方式打开只读文件(没有对应的权限);

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char** argv)
{int fd;fd=open("./m6.txt",O_WRONLY);printf("fd=%d\n",fd);printf("errno=%d\n",errno);printf("%s\n",strerror(errno));close(fd);return 0;
}
#修改m6.txt文件的属性为只读
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ chmod 0444 m6.txt
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ ls
dict.CP  dict.txt  m6.txt  open  open.c
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ ls -l
总用量 24
-rw-r--r-- 1 daniel daniel     0 5月  10 04:24 dict.CP
-rw-rw-r-- 1 daniel daniel     0 5月  10 04:14 dict.txt
-r--r--r-- 1 daniel daniel     0 5月  10 05:39 m6.txt
-rwxrwxr-x 1 daniel daniel 16928 5月  10 04:44 open
-rw-rw-r-- 1 daniel daniel   302 5月  10 05:38 open.c
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ gcc open.c -o open2
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ ./open2
fd=-1
errno=13
Permission denied

3)以只写方式打开目录(open只能打开普通文件);

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char** argv)
{int fd;fd=open("./m5",O_WRONLY);printf("fd=%d\n",fd);printf("errno=%d\n",errno);printf("%s\n",strerror(errno));close(fd);return 0;
}
#m5是一个目录文件
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ mkdir m5
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ gcc open.c -o open2
daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ ./open2
fd=-1
errno=21
Is a directory

close函数原型:

int close(int fd)

功能:关闭文件描述符

参数:

fd 调用open函数得到的文件描述符;

返回值:

0代表成功 -1代表失败

错误处理函数:

与errno相关。

//打印错误的编号
printf("errno=%d\n",errno);
//打印错误的具体内容
printf("xxx error:%s\n",strerror(int errno));

void perror(const char *)

是errno和strerror的结合;头文件为#include<stdio.h>

2 read/write文件读写操作

*函数:ssize_t read(int fd, void buf, size_t count)

头文件:#include <unistd.h>

参数:fd 文件描述符 buf 缓冲区指针 count 需要读入的字节数

功能:从fd的文件描述符中读取指定count大小的字节,并且写入缓冲区,buf指针当中;当函数返回0,代表读到了文件结尾,当函数返回-1,代表发生错误;当count=0,或者提示错误,或者返回值为0;

返回值:成功,返回读入的字节数,错误,返回-1;0代表读到文件结尾;

当返回-1,且erron=EAGIN/EVOULDBLOCK时,说明不是read失败,而是以非阻塞的形式读取设备文件,而且设备没有输入

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

头文件:#include <unistd.h>

参数:fd 文件描述符 buf 待写出数据的缓冲区 count 数据大小

功能:从fd的文件描述符中读取指定count大小的字节,并且写入缓冲区,buf指针当中;当函数返回0,代表读到了文件结尾,当函数返回-1,代表发生错误;当count=0,或者提示错误,或者返回值为0;

返回值:成功,返回写入的字节数,错误,返回-1;0代表没有数据可写了

通过调用系统函数read()和write()实现shell里面的cp命令:

#include <unistd.h>
#include <fcntl.h>
//#include <stdio.h>
//从命令行接收参数,并且初始化堆栈
int main(int argc, char *argv[])
{//创建一个1024字节的缓冲区char buf[1024];//用于接收读取字符的数量int n=0;//以只读的方式打开传入参数的第一个文件int fd1=open(argv[1],O_RDONLY);//以读写方式打开第二个文件,如果没有,需要创建,如果有,需要截断为0int fd2=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);//持续读取字符,直到返回值  n=0 标志读到文件尾部为止while((n=read(fd1,buf,1024))!=0){//将读入的字符写入文件write(fd2,buf,n);}//关闭文件描述符close(fd1);close(fd2);return 0;
}

3 系统调用和库函数的比较-缓冲区相关

使用系统调用函数,实现每次拷贝1个字符到另外的文件和使用库函数fputc//从表面看也是每次拷贝一个字符到另外的文件。比较两者的运行速度。

使用库函数实现的代码如下:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{FILE *fp, *fp_out;int n;fp=fopen(argv[1],"r");if(fp==NULL){perror("fopen error");exit(1);}fp_out=fopen(argv[2],"w");if(fp==NULL){perror("fopen error");exit(1);}while((n=fgetc(fp))!=EOF){fputc(n,fp_out);}fclose(fp);fclose(fp_out);return 0;
}

使用系统调用函数实现的方法如下:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define N 1
//从命令行接收参数,并且初始化堆栈
int main(int argc, char *argv[])
{//创建一个1024字节的缓冲区char buf[N];//用于接收读取字符的数量int n=0;//以只读的方式打开传入参数的第一个文件int fd1=open(argv[1],O_RDONLY);if (fd1==-1){perror("open argv1 error");exit(1);}//以读写方式打开第二个文件,如果没有,需要创建,如果有,需要截断为0 int fd2=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);if (fd2==-1){perror("open argv2 error");exit(1);}//持续读取字符,直到返回值  n=0 标志读到文件尾部为止while((n=read(fd1,buf,N))!=0){//将读入的字符写入文件write(fd2,buf,n);}//关闭文件描述符close(fd1);close(fd2);return 0;
}

最后经过比较发现,方法2的耗时比方法1耗时长的多。

原因,使用strace指令可以查看程序运行时使用的系统调用

daniel@daniel-Vostro-5471:~/文档/OS/test/IO_test$ strace ./myCP2.out xajh.txt 1.txt
read(3, "\215\212\344\270\252\345\270\210\347\210\266\357\274\214\345\216\237\350\257\245\345\216\273\350\260\242\347\232\204\343\200\202"..., 4096) = 4096
write(4, " \r\n\n    \350\277\231\346\227\266\344\273\244\347\213\220\345\206\262\345\267\262\345\260\206\346\201\222"..., 4096) = 4096
read(3, "\350\265\260\345\207\272\345\261\213\345\216\273\343\200\202 \r\n\n    \347\233\210\347\233\210\345\276\252"..., 4096) = 4096
write(4, "\215\212\344\270\252\345\270\210\347\210\266\357\274\214\345\216\237\350\257\245\345\216\273\350\260\242\347\232\204\343\200\202"..., 4096) = 4096

经过查看得知,使用库函数C程序,底层也是调用了系统调用的read()和write()函数,但是每次操作的字节数是4096个字节;也就是说虽然fputc表面每次只写入1个字节,但每4096个字节调用1次读或者写函数。

而使用同样的命令查看系统调用C函数用到的系统调用,发现每次调用系统函数只读入或者写入一个字节;这便导致了运行速度的差异;

read(3, "\243", 1)                      = 1
write(4, "\243", 1)                     = 1
read(3, "\350", 1)                      = 1
write(4, "\350", 1)                     = 1
read(3, "\243", 1)                      = 1
write(4, "\243", 1)                     = 1
read(3, "\263", 1)                      = 1
write(4, "\263", 1)                     = 1

fopen>>>>>>4096字节>>>>>>4096字节>>>>>>4096字节>>>>>>>BINGO!

用户区 标准库函数 内核区 缓冲区 硬件(硬盘区)

open>>>>>>>>>>>>>>>>>>>>>1字节>>>>>>>>4096字节>>>>>>>BINGO!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mAecj6Wk-1642598841072)(/home/daniel/.config/Typora/typora-user-images/image-20210512224341331.png)]

使用库函数,将每次读写字节的缓冲区定义为1字节时,使得用户区和内核区交互过于频繁,而这个过程时相当耗时的。如果将read()和write()函数的字节数,修改回4096,两个程序的运行时间,应该是使用系统调用更快。

read和write函数被称为 Unbuffered I/O,指的是无用户缓冲区,但是不保证不使用内核缓冲区

预读入,缓输出机制

即使学了系统调用,能使用库函数的时候,还是尽量使用库函数。

因为库函数久经考验,效率相对较高,而对于系统调用来说,如果对于原理不深刻了解,可能会出现效率低下问题。

4 文件描述符

PCB 进程控制块 本质是一个结构体 与之相对的是TCB 线程控制块

struct task_struct{ 结构体成员 } 其中有个文件描述符表

成员中有一个指针,指针指向一个文件描述符表

文件描述符表,实际上是一个key-value表,键值对表,key是一个正整数,也就是我们见到的文件描述符,而value是一个指针,指向一个文件结构体,内部含有文件的各种信息;

主要包含文件描述符,文件读写位置和IO缓冲区3部分内容

struct file {

​ …

​ 文件偏移量

​ 文件访问权限

​ 文件的打开标志

​ 文件内核缓冲区的首地址

​ struct operations *f_op;

​ } 结构体中包含文件的描述信息;

查看方法:

(1)/usr/src/linux-headers-3.16.0-30/include/linux/fs.h

0 STDIN_FILENO 标准输入

1 STDOUT_FILENO 标准输出

2 STDERR_FILENO 标准错误

3

1023 …

单个进程最多打开1024个文件,其中0,1,2已经被标准输入,标准输出和标准错误所占用,只能使用剩余编号的最小值。

文件描述符相当于句柄。我们在程序中通过文件描述符,对对应的文件进行操作。

使用 ulimit -a 查看

5 阻塞和非阻塞

阻塞/非阻塞:

​ 产生阻塞的场景:读设备文件和读网络文件(读常规文件没有阻塞概念)

​ /dev/tty --终端文件

读常规的文件是不会产生堵塞的,无论读多少字节,read一定会在有限的时间内返回。

1个产生堵塞的示例:

//block.c>>>>block.out
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char **argv)
{int fd1;char* buf[20];int n=20;//从标准输入中读入n个字符,键盘的输入储存在标准输入中//即标准输入是一个由外部设备给定的输入read(STDIN_FILENO,buf,n);//将读到的字符写入标准输出中,标准输出是终端显示write(STDOUT_FILENO,buf,n);return 0;
}

执行程序之后,终端会一直等待用户输入字符,这便是一种阻塞。

当read函数,当返回-1,且erron=EAGIN/EVOULDBLOCK时,说明不是read失败,而是以非阻塞的形式读取设备文件,而且设备没有输入

阻塞和非阻塞不是read和write函数的特性,而是设备文件或者网络文件的属性;

//block2.c
//以非阻塞形式打开设备文件
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char **argv)
{int fd;char buf[10];int n;//以只读和非阻塞方式打开设备输入文件//原来设备文件是阻塞性质的,现在修改为非阻塞性质的fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);if (fd < 0){perror("open /dev/tty");exit(1);}while (1){n = read(fd, buf, 10);if (n < 0){if (errno != EAGAIN){perror("read /dev/tty");exit(1);}//n=-1,errno=EAGAIN,以非阻塞形式读数据,但是设备没有输入//每隔2s重新读取一次数据else{write(STDOUT_FILENO, "try agin\n", strlen("try again\n"));sleep(2);continue;}}else{//读取数据成功,将数据写到终端,关闭文件描述符write(STDOUT_FILENO, buf, n);close(fd);break;}}    return 0;
}

**阻塞:**调用结果返回值,当前程序被挂起,直到结果返回;

**非阻塞:**如果不能立即得到结果,则调用者不会阻塞当前线程;所以非阻塞的状态下,需要不断进行轮询;

6 fcntl改文件属性

函数:int fcntl(int fd, int cmd, …)

文件操作杂货铺,有诸多功能

int flags=fcntl(fd,F_GETFL);

获取文件状态:F_GETFL

设置文件状态:F_SETFL

位图:是一串二进制数,使用0或者1代表某个功能是否打开

和stm32 的pin脚定义类似

将标准输入的属性修改成 非阻塞状态 的代码如下:

int flags=fcntl(STDIN_FILENO,F_GETFL);
flags|=O_NONBLOCK;
fcntl(STDIN_FILENO,F_GETFL,flags)

头文件:#include <unistd> #include <fcntl.h>

功能:用于改变文件的状态

参数:fd是对应的文件描述符,cmd是对应的指令,…是可选项,是由cmd决定的参数,通常情况下是整数;

F_GETFD (void)Return  (as  the function result) the file descriptor flags; arg is ignored.
F_SETFD (int)Set the file descriptor flags to the value specified by arg.

简明Linux-Linux下的常用文件操作(1)相关推荐

  1. linux在A目录下创建B文件,Linux课程---5、常用文件命令和目录命令(创建文件命令)...

    Linux课程---5.常用文件命令和目录命令(创建文件命令) 一.总结 一句话总结: touch file1 1.管道符|有什么用? 将前一个命令的结果作为后一个命令的输入:比如查看文件前3行:ca ...

  2. Linux操作系统下/etc/hosts文件

    1. 关于/etc/host,主机名和IP配置文件 Hosts - The static table lookup for host name(主机名查询静态表) Linux 的/etc/hosts是 ...

  3. linux删去分区的文件夹,如何恢复 Linux 分区下误删的文件?

    以前总是在网上看到很多人问怎么恢复 Linux 分区下误删的文件.当时并没有仔细关注过,真没想到昨天这事摊我身上了. 大致说说这场悲剧的整个过程吧(为了避免一些不必要的麻烦,本文中的文件夹名和设备名都 ...

  4. 如何恢复 Linux 分区下误删的文件?

    以前总是在网上看到很多人问怎么恢复 Linux 分区下误删的文件.当时并没有仔细关注过,真没想到昨天这事摊我身上了. 大致说说这场悲剧的整个过程吧(为了避免一些不必要的麻烦,本文中的文件夹名和设备名都 ...

  5. CentOS Linux系统下swp,swo文件扩展名临时隐藏文件

    CentOS Linux系统下swp,swo文件扩展名的隐藏文件 系统版本: CentOS Linux 6.8 场景: 今天巡检oracle系统的时候,发现/home目录大小超过80%,因一般不允许超 ...

  6. 如何解压服务器系统,如何解压Linux系统下的zip文件

    如何解压Linux系统下的zip文件 发布时间:2020-08-10 09:28:56 来源:亿速云 阅读:76 作者:Leah 这期内容当中小编将会给大家带来有关如何解压Linux系统下的zip文件 ...

  7. Linux环境下移动一个文件夹下的所有文件到另一个文件夹

    Linux环境下移动一个文件夹下的所有文件到另一个文件夹 研究命令 最近需要把一个环境里的文件,迁移到另一个环境里去,但是两个环境里的数据会有一些同名的子目录,要求不能覆盖数据而是合并,迁移之前我先做 ...

  8. 成功解决Windows10环境下运行Linux系统下的.sh文件

    成功解决Windows10环境下运行Linux系统下的.sh文件 目录 解决问题 解决方法 解决问题 Windows10环境下运行Linux系统下的.sh文件 解决方法 .sh是shell scrip ...

  9. linux下分割文件的方法,关于linux系统下分割大文件的方法

    <关于linux系统下分割大文件的方法>由会员分享,可在线阅读,更多相关<关于linux系统下分割大文件的方法(4页珍藏版)>请在人人文库网上搜索. 1.关于 linux系统下 ...

最新文章

  1. Levenshtein distance 编辑距离算法
  2. Python源码学习:多线程实现机制
  3. HelloWorld !
  4. yolo-v2 v3实现笔记 mAP:mean average precision 平均精度均值
  5. 远程debug Tomcat工程
  6. 吐槽贴:用ELECTRA、ALBERT之前,你真的了解它们吗?
  7. 石头剪刀布程序流程图_石头剪刀布!我要与电脑决战到天明!(14天)
  8. 如何让字体大小12px
  9. SQL Server中的表变量
  10. /usr/bin/ld: cannot find -l*
  11. 小程序 switch 自定义_微信小程序自定义组件问题一:获取组件DOM元素
  12. Python帮助文档的设置
  13. python3实现国密SM4算法
  14. python导入鸢尾花数据集_python数据挖掘学习笔记】十九.鸢尾花数据集可视化、线性回归、决策树花样分析...
  15. python求均值方差不用numpy_【Python】不用numpy用纯python求极差、平均数、中位数、众数与方差,python的打印到控制台...
  16. 人工智能——机器学习
  17. 传奇服务器怎么设置状态是开区还是合区,传奇私服开区合区教程全解
  18. JSP——连接数据库、编写api接口
  19. iptables实战演练
  20. 每节课都是一个项目 手把手用STM32打造联网气象站-4-STM32基础三件套-TIM定时器和SYSTICK初始化

热门文章

  1. 运算放大器基本电路——11个经典电路
  2. IBM大连工作室成立 Design thinking实现快速创新
  3. C语言指针的一些易错点
  4. 代码优化大盘点:35 个 Java 代码优化魔鬼细节
  5. 【转】Zcash 网络升级 Sapling 简介
  6. 跟我一起重写JAVA WEB网络硬盘( 1 )
  7. Python王者荣耀皮肤批量下载
  8. LeetCode第一阶段(一)【数组篇】
  9. 谷歌SEO-外链策略
  10. [转]轻松获得网通、电信、铁通IP地址分配段