Linux下的C编程实战之文件系统编程

 在Linux平台下对文件编程可以使用两类函数:(1Linux操作系统文件API;(2C语言I/O库函数。前者依赖于Linux系统调用,后者实际上与操作系统是独立的,因为在任何操作系统下,使用C语言I/O库函数操作文件的方法都是相同的。本章将对这两种方法进行实例讲解。


1. 文件IO操作


S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP


  fd=open("/dev/globalvar", O_RDWR, S_IRUSR|S_IWUSR);     //可读写方式打开设备文件

S_IRUSR

Permits the file's owner to read it.
S_IWUSR
Permits the file's owner to write to it.
S_IRGRP
Permits the file's group to read it.
S_IWGRP
Permits the file's group to write to it.
S_ISDIR ( ) 目录文件
S_ISCHR ( ) 字符特殊文件
S_ISBLK ( ) 块特殊文件
S_ISFIFO ( ) 管道或F I F O
S_ISLNK ( ) 符号连接( P O S I X . 1S V R 4无此类型)
S_ISSOC K ( ) 套接字(P O S I X . 1S V R 4无此类型)

S_ISREG ( ) 普通文件



   2.Linux 文件 API

   Linux 的文件操作 API 涉及到创建、打开、读写和关闭文件。

  创建

int creat(const char *filename, mode_t mode);


  参数mode指定新建文件的存取权限,它同umask一起决定文件的最终权限(mode&umask),其中umask代表了文件在创建时需要去掉的一些存取权限。umask可通过系统调用umask()来改变:

int umask(int newmask);


  该调用将umask设置为newmask,然后返回旧的umask,它只影响读、写和执行权限。

  打开

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

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


  open函数有两个形式,其中pathname是我们要打开的文件名(包含路径名称,缺省是认为在当前路径下面)flags可以去下面的一个值或者是几个值的组合:

标志含义

O_RDONLY以只读的方式打开文件

O_WRONLY以只写的方式打开文件

O_RDWR以读写的方式打开文件

O_APPEND以追加的方式打开文件

O_CREAT 创建一个文件

O_EXEC如果使用了O_CREAT而且文件已经存在,就会发生一个错误

O_NOBLOCK以非阻塞的方式打开一个文件

O_TRUNC如果文件已经存在,则删除文件的内容

   


  O_RDONLYO_WRONLYO_RDWR三个标志只能使用任意的一个。

  如果使用了O_CREATE标志,则使用的函数是int open(const char *pathname,int flags,mode_t mode);这个时候我们还要指定mode标志,用来表示文件的访问权限。mode可以是以下情况的组合:

标志含义

S_IRUSR 用户可以读

S_IWUSR 用户可以写

S_IXUSR 用户可以执行

S_IRWXU用户可以读、写、执行

S_IRGRP 组可以读

S_IWGRP 组可以写

S_IXGRP 组可以执行

S_IRWXG 组可以读写执行

S_IROTH 其他人可以读

S_IWOTH 其他人可以写

S_IXOTH 其他人可以执行

S_IRWXO其他人可以读、写、执行

S_ISUID 设置用户执行ID

S_ISGID 设置组的执行ID


  除了可以通过上述宏进行逻辑产生标志以外,我们也可以自己用数字来表示,Linux总共用5个数字来表示文件的各种权限:第一位表示设置用户ID;第二位表示设置组ID;第三位表示用户自己的权限位;第四位表示组的权限;最后一位表示其他人的权限。每个数字可以取1(执行权限)2(写权限)4(读权限)0()或者是这些值的和。例如,要创建一个用户可读、可写、可执行,但是组没有权限,其他人可以读、可以执行的文件,并设置用户ID位。那么,我们应该使用的模式是1(设置用户ID)0(不设置组ID)7(1+2+4,读、写、执行)0(没有权限)5(1+4,读、执行)10705

open("test", O_CREAT, 10705);


  上述语句等价于:

open("test", O_CREAT, S_IRWXU | S_IROTH | S_IXOTH | S_ISUID );


  如果文件打开成功,open函数会返回一个文件描述符,以后对该文件的所有操作就可以通过对这个文件描述符进行操作来实现。

  读写

  在文件打开以后,我们才可对文件进行读写了,Linux中提供文件读写的系统调用是readwrite函数:

int read(int fd, const void *buf, size_t length);

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


  其中参数buf为指向缓冲区的指针,length为缓冲区的大小(以字节为单位)。函数read()实现从文件描述符fd所指定的文件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数。函数write实现将把length个字节从buf指向的缓冲区中写到文件描述符fd所指向的文件中,返回值为实际写入的字节数。

  以O_CREAT为标志的open实际上实现了文件创建的功能,因此,下面的函数等同creat()函数:

int open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);


  定位

  对于随机文件,我们可以随机的指定位置读写,使用如下函数进行定位:

int lseek(int fd, offset_t offset, int whence);


  lseek()将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。参数whence可使用下述值:

  SEEK_SET:相对文件开头
  SEEK_CUR:相对文件读写指针的当前位置
  SEEK_END:相对文件末尾

  offset可取负值,例如下述调用可将文件指针相对当前位置向前移动5个字节:

lseek(fd, -5, SEEK_CUR);


  由于lseek函数的返回值为文件指针相对于文件头的位置,因此下列调用的返回值就是文件的长度:

lseek(fd, 0, SEEK_END);


  关闭

  当我们操作完成以后,我们要关闭文件了,只要调用close就可以了,其中fd是我们要关闭的文件描述符:

int close(int fd);


  例程:编写一个程序,在当前目录下创建用户可读写文件“hello.txt”,在其中写入“Hello, software weekly”,关闭该文件。再次打开该文件,读取其中的内容并输出在屏幕上。

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#define LENGTH 100

main()

{

 int fd, len;

 char str[LENGTH]; 

 fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /*创建并打开文件 */

 if (fd) 

 {

  write(fd, "Hello, Software Weekly", strlen("Hello, software weekly")); /*写入 Hello, software weekly字符串 */

  close(fd);

 }


 fd = open("hello.txt", O_RDWR);

 len = read(fd, str, LENGTH); /*读取文件内容 */

 str[len] = '\0';

 printf("%s\n", str);

 close(fd);

}


 3.C语言库函数

  C库函数的文件操作实际上是独立于具体的操作系统平台的,不管是在DOSWindowsLinux还是在VxWorks中都是这些函数:

  创建和打开

FILE *fopen(const char *path, const char *mode);


  fopen()实现打开指定文件filename,其中的mode为打开模式,C语言中支持的打开模式如下表:

标志含义

r, rb以只读方式打开

w, wb以只写方式打开。如果文件不存在,则创建该文件,否则文件被截断

a, ab以追加方式打开。如果文件不存在,则创建该文件

r+, r+b, rb+ 以读写方式打开

w+, w+b, wh+以读写方式打开。如果文件不存在时,创建新文件,否则文件被截断

a+, a+b, ab+以读和追加方式打开。如果文件不存在,创建新文件


  其中b用于区分二进制文件和文本文件,这一点在DOSWindows系统中是有区分的,但Linux不区分二进制文件和文本文件。

  读写

  C库函数支持以字符、字符串等为单位,支持按照某中格式进行文件的读写,这一组函数为:

int fgetc(FILE *stream);

int fputc(int c, FILE *stream);

char *fgets(char *s, int n, FILE *stream);

int fputs(const char *s, FILE *stream);

int fprintf(FILE *stream, const char *format, ...);

int fscanf (FILE *stream, const char *format, ...);

size_t fread(void *ptr, size_t size, size_t n, FILE *stream);

size_t fwrite (const void *ptr, size_t size, size_t n, FILE *stream);


  fread()实现从流stream中读取加n个字段,每个字段为size字节,并将读取的字段放入ptr所指的字符数组中,返回实际已读取的字段数。在读取的字段数小于num时,可能是在函数调用时出现错误,也可能是读到文件的结尾。所以要通过调用feof()ferror()来判断。

  write()实现从缓冲区ptr所指的数组中把n个字段写到流stream中,每个字段长为size个字节,返回实际写入的字段数。

  另外,C库函数还提供了读写过程中的定位能力,这些函数包括

int fgetpos(FILE *stream, fpos_t *pos);

int fsetpos(FILE *stream, const fpos_t *pos);

int fseek(FILE *stream, long offset, int whence); 

等。


  关闭

  利用C库函数关闭文件依然是很简单的操作:

int fclose (FILE *stream);


  例程:将第2节中的例程用C库函数来实现。

#include <stdio.h>

#define LENGTH 100

main()

{

 FILE *fd;

 char str[LENGTH];


 fd = fopen("hello.txt", "w+"); /*创建并打开文件 */

 if (fd)

 {

  fputs("Hello, Software Weekly", fd); /*写入Hello, software weekly字符串 */

  fclose(fd);

 }


 fd = fopen("hello.txt", "r");

 fgets(str, LENGTH, fd); /*读取文件内容 */

 printf("%s\n", str);

 fclose(fd);

}


  4.小结

  Linux提供的虚拟文件系统为多种文件系统提供了统一的接口,Linux的文件编程有两种途径:基于Linux系统调用;基于C库函数。这两种编程所涉及到文件操作有新建、打开、读写和关闭,对随机文件还可以定位。本章对这两种编程方法都给出了具体的实例。



带缓冲I/O 不带缓冲I/O详解


 以下是我对这两者的理解:

首先要明白不带缓冲的概念:所谓不带缓冲,并不是指内核不提供缓冲,而是只单纯的系统调用,不是函数库的调用。系统内核对磁盘的读写都会提供一个块缓冲,当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲进行排队,当块缓冲达到一定的量时,才会把数据写入磁盘。因此所谓的不带缓冲的I/O是指进程不提供缓冲功能。每调用一次writeread函数,直接系统调用。

而带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。

因此,带缓冲的I/O在往磁盘写入相同的数据量时,会比不带缓冲的I/O调用系统调用的次数要少。


下面的东西是我从网上查到的对这两者的理解,我觉得还是很到位的:

以下主要讨论关于open,write等基本系统IO的带缓冲与不带缓冲的差别


      带缓存的文件操作是标准C 库的实现,第一次调用带缓存的文件操作函数时标准库会自动分配内存并且读出一段固定大小的内容存储在缓存中。所以以后每次的读写操作并不是针对硬盘上的文 件直接进行的,而是针对内存中的缓存的。何时从硬盘中读取文件或者向硬盘中写入文件有标准库的机制控制。不带缓存的文件操作通常都是系统提供的系统调用, 更加低级,直接从硬盘中读取和写入文件,由于IO瓶颈的原因,速度并不如意,而且原子操作需要程序员自己保证,但使用得当的话效率并不差。另外标准库中的 带缓存文件IO 是调用系统提供的不带缓存IO实现的。


术语不带缓冲指的是每个readwrite都调用嗯内核中的一个系统调用。所有的磁盘I/O都要经过内核的块缓冲(也称内核的缓冲区高速缓 存),唯一例外的是对原始磁盘设备的I/O。既然readwrite的数据都要被内核缓冲,那么术语不带缓冲的I/O“指的是在用户的进程中对这两个 函数不会自动缓冲,每次readwrite就要进行一次系统调用。“--------摘自<unix环境编程>


程序中用openwrite打开创建并把“hello world“写入文件test.txt,相应用fopenfwrite操作文件test2.txt。程序执行到openfopen之后,sleep 15秒,这时用ls查看生成了文件没,这时用open打开的test.txt出现了,但是fopentest2.txt没有;当程序执行完write fwrite之后,fopentest2.txt仍然没有出现(还是用ls查看),再用cattest.txt,可以看到 “helloworld”;最后再关闭test.txttest2.txt,这时test2.txt出现了,并且其内容也是“hello world“

   该例子证明了openwrite是不带缓冲的,即程序一执行其io操作也立即执行,不会停留在系统提供的缓冲里,不需等到close操作完才执行。与之相比的fopenfwrite则是带缓冲的,(一般)要等到fclose操作完后才会执行。

  

  相关的源码示例如下:

 i nclude <unistd.h>

i nclude <iostream>

i nclude <fcntl.h>

i nclude <string>

i nclude <sys/types.h>

i nclude <sys/stat.h>

using namespace std;


int main(){

 int fd;

 FILE *file;

 char *s="hello,world\n";

 if((fd=open("test.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1){

  cout<<"Error open file"<<endl;

  return -1;

 }

 if((file=fopen("test2.txt","w"))==NULL){

  cout<<"Error Open File."<<endl;

  return -1;

 }

 cout<<"File has been Opened."<<endl;

 sleep(15);

 if(write(fd,s,strlen(s))<strlen(s)){

  cout<<"Write Error"<<endl;

  return -1;

 }

 if(fwrite(s,sizeof(char),strlen(s),file)<strlen(s)){

  cout<<"Write Error in 2."<<endl;

  return -1;

 }

 cout<<"After write"<<endl;

 sleep(15);

 cout<<"After sleep."<<endl;

 close(fd);

 return 0;

}

详情请见:http://blog.csai.cn/user1/27828/archives/2007/14285.html


ssize_t write(int filedes, const void *buff, size_t nbytes)size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp)来讲讲自己对unix系统下带缓存的I/O和不带缓存的I/O的区别。


 

    首先要清楚一个概念,所谓的代缓存并不是指上面两个函数的buff参数,而是指unix系统在内核中所设的缓冲存储器。


    当将数据写到文件上时,内核先将该数据写到缓存,如果该缓存未满,则并不将其排入输出队列,直到缓存写满或者内核再次需要重新使用此缓存时才将其排入输入队列,待其到达对首,在进行实际的I/O操作,也就是此时才把数据真正写到磁盘,这种技术叫延迟写。


    现在假设内核所设的缓存是100个字节,如果你使用write,且buffsize10,当你要把9个同样的buff写到文件时,你需要调用9write,也就是9次系统调用,此时也并没有写到硬盘,如果想立即写到硬盘,调用fsync,可以进行实际的I/O操作。


    标准I/O,也就是带缓存的I/O采用FILE*FILE实际上包含了为管理流所需要的所有信息:实际I/O的文件描述符,指向流缓存的指针(标准I /O缓存,由malloc分配,又称为用户态进程空间的缓存,区别于内核所设的缓存),缓存长度,当前在缓存中的字节数,出错标志等,假设流缓存的长度为 50字节,把以上的数据写到文件,则只需要2次系统调用(fwrite调用write系统调用),因为先把数据写到流缓存,当其满以后或者调用 fflush时才填入内核缓存,所以进行了2次的系统调用write


    fflush将流所有未写的数据送入(刷新)到内核(内核缓冲区),fsync将所有内核缓冲区的数据写到文件(磁盘)。

 

    不带缓存的readwrite是相对于fread/fwrite等流函数来说明的,因为freadfwrite是用户函数(3),所以他们会在用户层 进行一次数据的缓存,而read/write是系统调用(2)所以他们在用户层是没有缓存的,所以称readwrite是无缓存的IO,其实对于内核来 说还是进行了缓存,不过用户层看不到罢了。




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383476.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【kali】kali设置burpsuite抓包dvwa

kali自带burpsuite 配置代理 burpsuite是通过代理来抓包dvwa的 burpsuite&#xff1a;proxy—> options 里边监听的应该是127.0.0.1:8080 &#xff08;端口ip如果撞车了都可以自己改&#xff09; 火狐&#xff1a; preferences–>最下边的network settings点击settings —…

fopen函数简介

函数简介 函数功能&#xff1a;打开一个文件 函数原型&#xff1a;FILE * fopen(const char * path,const char * mode); 相关函数&#xff1a;open&#xff0c;fclose&#xff0c;fopen_s[1]&#xff0c;_wfopen 所需库&#xff1a;<stdio.h> 返回值&#xff1a;文件顺利…

【kali】kali换了root权限后无法打开firefox浏览器

从普通权限换成root权限后发现火狐进不去鸟&#xff01;&#xff01; 终端报错&#xff1a; Running firefox as root in a regular user’s sessin is not supported.($HOME is /home/miehahaha which is owned by uid 1000) 分析&#xff1a; 是的&#xff0c;原来普通权限m…

【win10】局域网内两台win10共享文件夹

https://jingyan.baidu.com/article/93f9803f3e9788e0e46f55c8.html

CentOS 7关闭firewalld启用iptables

在CentOS7中&#xff0c;有很多CentOS 6中的常用服务发生了变化。 其中iptables是其中比较大的一个。防火墙iptables被firewalld取代。 本文将介绍&#xff0c;如果采用systemctl关闭firewalld&#xff0c;开启iptables。 1.关闭firewalld [roothwcentos70-01 system]# systemc…

wpa_supplicant wifi密码错误检测

system("/usr/sbin/wpa_supplicant -Dnl80211 -iwlan0 -f /tmp/wpa_log -c/tmp/wpa_supplicant.conf -d -t -B &"); 在/tmp/wpa_log中查看是否有如下的字符串&#xff1a; 1. 针对WPA&#xff0f;WPA2加密方式 //1473218403.305655: wlan0: WPA: 4-Way Handsh…

CentOS 7 安装nginx

1.安装pcre pcre-devel yum install pcre pcre-devel -y rpm -qa pcre pcre-devel 2.安装openssl-devel yum install -y openssl-devel rpm -qa openssl-devel openssl 3.下载nginx cd /home/testuser/mkdir toolscd tools/wget -q http://nginx.org/download/nginx-1.9.9.…

MPEG4与.mp4

流媒体应用中TS和MP4格式分析应该是封包格式。不能简单理解成MPEG4的简称。要详细解释这个问题&#xff0c;需要提一下MPEG4和.mp4在概念上的区别。 一般来说&#xff0c;仅提“MPEG4”&#xff0c;是指一种视频压缩算法。可以把原始画面通过数学运算变换成一组二进制数据&…

MP4文件格式的解析,以及MP4文件的分割算法

mp4应该算是一种比较复杂的媒体格式了&#xff0c;起源于QuickTime。以前研究的时候就花了一番的功夫&#xff0c;尤其是如何把它完美的融入到视频点播应用中&#xff0c;更是费尽了心思&#xff0c;主要问题是处理mp4文件庞大的“媒体头”。当然&#xff0c;流媒体点播也可以采…

MP4文件格式详解

一、基本概念 1.mp4概述 MP4文件中的所有数据都装在box&#xff08;QuickTime中为atom&#xff09;中&#xff0c;也就是说MP4文件由若干个box组成&#xff0c;每个box有类型和长度&#xff0c;可以将box理解为一个数据对象块。box中可以包含另一个box&#xff0c;这种box称为c…

H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流

SkySeraph Apr 1st 2012 Email&#xff1a;skyseraph00163.com 一、MP4格式基本概念 MP4格式对应标准MPEG-4标准(ISO/IEC14496) 二、MP4封装格式核心概念 1 MP4封装格式对应标准为 ISO/IEC 14496-12&#xff08;信息技术 视听对象编码的第12部分: ISO 基本媒体文件格式/Info…

AAC音频格式分析

关于AAC音频格式基本情况&#xff0c;可参考维基百科http://en.wikipedia.org/wiki/Advanced_Audio_Coding AAC音频格式分析 AAC音频格式有ADIF和ADTS&#xff1a; ADIF&#xff1a;Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数…

tar压缩隐藏文件

如果想tar 压缩包含隐藏文件的目录&#xff0c;同时排除掉部分无用的目录 tar -czvf 20161009.tar.gz * .[!.]* --exclude .git 在Linux下打包tar文件时添加密码的方法 在当前目录下有一个pma目录的文件夹: 1、使用tar对文件压缩加密&#xff1a; 代码如下: # tar -zcvf - pma…

linux 怎么把^M去掉

在linux下&#xff0c;不可避免的会用VIM打开一些windows下编辑过的文本文件。我们会发现文件的每行结尾都会有一个^M符号&#xff0c;这是因为 DOS下的编辑器和Linux编辑器对文件行末的回车符处理不一致&#xff0c; 对于回车符的定义&#xff1a; windows&#xff1a;0D0A un…

关于cp命令中拷贝所有的写法

今天在编写一个脚本的时候&#xff0c;发现一个比较奇怪的问题&#xff1a;就是在使用cp拷贝当前目录下所有文件到目标目录的时候&#xff0c;源和目标大大不同。原来一直没有留意有这样的问题&#xff0c;后来查了些资料&#xff0c;才知道以前一直使用的格式有误&#xff0c;…

cp -r 和 cp -R 的区别

今天倒腾linux根文件系统的时候发现 cp -r /dev /dev_bak 时&#xff0c;竟然会 将磁盘设备中的数据进行一次拷贝&#xff0c;而不是仅仅建立设备文件。于是到网上搜了一把&#xff0c;收获不小。http://www.loveunix.net/html/200407/33920.html这里有人问同样的问题&#xff…

Makefile选项CFLAGS,LDFLAGS,LIBS

CFLAGS 表示用于 C 编译器的选项&#xff0c; CXXFLAGS 表示用于 C 编译器的选项。 这两个变量实际上涵盖了编译和汇编两个步骤。 CFLAGS&#xff1a; 指定头文件&#xff08;.h文件&#xff09;的路径&#xff0c;如&#xff1a;CFLAGS-I/usr/include -I/path/include。同样地…

smbclient和挂载samba共享目录

1&#xff0c;列出某个IP地址所提供的共享文件夹 smbclient -L 198.168.0.1 -U marsaber%12332112345672,像FTP客户端一样使用smbclient smbclient //192.168.0.1/tmp -U marsaber%1233211234567 执行smbclient命令成功后&#xff0c;进入smbclient环境&#xff0c;出现提示符…

linux 下source命令

当我修改了/etc/profile文件&#xff0c;我想让它立刻生效&#xff0c;而不用重新登录&#xff1b;这时就想到用 source 命令&#xff0c;如:source /etc/profile对source进行了 学习 &#xff0c;并且用它与sh 执行脚本进行了对比&#xff0c;现在总结一下。source命令&#x…

make Image uImage与zImage的区别

内核编译&#xff08;make&#xff09;之后会生成两个文件&#xff0c;一个Image&#xff0c;一个zImage&#xff0c;其中Image为内核映像文件&#xff0c;而zImage为内核的一种映像压缩文件&#xff0c;Image大约为4M&#xff0c;而zImage不到2M。 那么uImage又是什么的&#…