草庐IT

【Linux】在Linux下的文件IO(一)

我的快乐都是微小的事情 2023-03-28 原文

系统调用

系统调用: 操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务  

为什么用户程序不能直接访问系统内核提供的服务为了更好地保护内核空间,将程序的运行空间分为 内核空间用户空间(也就是常称的内核态用户态),它们分别运行在不同的级别上 在逻辑上是相互隔离的 。 因此 用户进程在通常情况下不允许访问内核数据 ,也无法使用内核函数,它们只能在用户空间操作用户数据 ,调用用户空间的函数 。
进行系统调用时 ,程序运行空间从用户空间进入内核空间 ,处理完后再返回到用户空间系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口 。在实际使用中程序员调用的通常是用户编程接口 API 。
Linux 中的系统调用包含在 Linux 的 libc 库中,通过标准的 C 函数调用方法可以调用系统命令相对 API 更高了一层,它实际上是一个可执行程序,它的内部调用了用户编程接口 (API )来实现相应的功能 。内核如何区分和引用特定的文件

通过文件描述符 。文件描述符是一个非负的整数 ,是一个索引值 ,指向在内核中每个进程打开文件的记录表 。 当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描
述符;当需要读写文件时 也需要把文件描述符作为参数传递给相应的函数 。是一个非负的整数(通常是小整数),posix标准要求每次打开一个文件,必须使用当前进程最小的文件描述符号码,因此打开一定是3.
一个进程启动时 通常会打开 3 个文件:

标准输入 标准输入 标准出错
STDIN_FILENO STDOUT_FILENO STDERR_FILENO
stdin stdout stderr
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define MSG_STR "hello world\n"

int main(int argc, char **argv)
{
   printf("%s",MSG_STR);
   fputs(MSG_STR,stdout);
   write(STDOUT_FILENO,MSG_STR,strlen(MSG_STR));//标准输出到屏幕MSG_STR
   return 0;
}

关于fputs和weitey

fputs(const char *s, FILE *stream);
write(int fd, const void *buf, size_t count);

文件的创建/打开

open() 函数用于打开或者创建文件。其在打开或者创建文件时可以指定文件的属性及用户的权限等各种参数。

int open(const char *pathname, int flags);//打开已存在的文件
int open(const char *pathname, int flags, mode_t mode);//打开不存在的文件
//flags:read write操作文件的权限
//mode:该文件在磁盘中 相对于 用户的权限
int open(const char *path, int oflag, [mode_t mode]);

args:
    const char *path: 文件路径,可以是绝对,也可以是相对路径 
    int oflag       : 文件打开的方式
                        - O_RDONLY 只读打开
                        - O_WRONLY 只写打开
                        - O_RDWR   可读可写打开
                        以上3种必选一个,以下4种可以任意选择
                        - O_APPEND 追加打开,所写数据附加到文件末
                        - O_CREAT  若此文件不存在则创建它
                        - O_EXCL   若文件存在则报错返回 
                        - O_TRUNC  如果文件已存在,并且以只写或可读可写方式打开
                        		   则将其长度截断为0字节
    [mode_t mode]   : 文件权限,只有在创建文件时需要使用
    
return:
    文件描述符,非负整数是成功,-1是失败
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc ,char **argv)
{
        int fd = -1;

        if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
        if(-1==fd)
        {
             printf("文件创建失败\n");
        }
        else
        {
             printf("文件打开成功,fd = %d\n",fd);
        }
        return 0;
}

写文件

当文件打开后,我们就可以向该文件写数据了。在Linux系统中,用 write() 向打开的文件写入数据,要使用这个函数,需要包含 #include <unistd.h> 。下面是函数的说明:

ssize_t write(int fildes, const void *buf, size_t nbyte);

args:
    int fildes     : 写入文件的文件描述符
    const void *buf: 写入数据在内存空间存储的地址
    size_t nbyte   : 期待写入数据的最大字节数
    
return:
    文件实际写入的字节数,非负整数是成功,-1是失败(磁盘已满或者超出该文件的长度等)
        if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
        if(rv == -1)
        {
             printf("写入数据失败\n");
        }
        else
        {
             printf("写入数据成功\n");
        }

读文件

同写文件类似,要使用读文件函数 read() ,需要包含 #include <unistd.h> 。下面是函数的说明:

ssize_t read(int fildes, void *buf, size_t nbyte);

args:
    int fildes  : 读取文件的文件描述符
    void *buf   : 读取数据在内存空间存储的地址
    size_t nbyte: 期待读取数据的最大字节数
    
return:
    文件实际读取的字节数,非负整数是成功,-1是失败
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


#define  BUFSIZE        1024
#define  MSG_STR  "i love  linux\n"


int main(int argc ,char **argv)
{
        int     fd = -1;
        int     rv = -1;
        char    buf[BUFSIZE];

        if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
        if(fd==-1)
       	{
            printf("文件创建失败\n");
        }
        else
        {
            printf("文件打开成功,fd = %d\n",fd);
        }

        if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
        if(rv == -1)
        {
             printf("写入数据失败\n");
        }
       	else
        {
             printf("写入数据成功\n");
        }
		
        memset(buf,0,sizeof(buf));//把buf的值为0,
		if((rv = read(fd,buf,sizeof(buf)))<0)
        if(rv == -1)
         {
             printf("读写失败\n");
         }
         else
         {
             printf("读写成功\n");
             goto cleanup;
         }

        printf("读写的数据 %d\n %s\n",rv,buf);
        return 0;
}    

但是这样读出是空的,所以要使用lseek文件偏离量,通俗来说:就是改变光标的位置,从哪里读数据.
memset函数
memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。

void *memset(void *s, int ch, size_t n);
	str  -- 指向要填充的内存块。
	c 	 -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
	n	 -- 要被设置为该值的字符数。

文件的偏移量
在每个打开的文件中都有一个文件的偏移量,文件的偏移量会根据文件的读写而改变位置。我们可以通过 lseek() 函数来调整文件的偏移量。默认情况下,新打开文件的文件偏移量在文件的开始。同 write() 和 read() 函数类似,要使用这个函数,需要包含 #include <unistd.h> 。下面是函数的说明:

off_t lseek(int fildes, off_t offset, int whence);

args:
    int fildes  : 修改文件的文件描述符
    off_t offset: 文件偏移量移动的距离
    int whence  : 文件偏移量的基址
                    - SEEK_SET 文件开始处
                    - SEEK_CUR 文件当前位置
                    - SEEK_END 文件结束处
    
return:
    当前文件指针的位置,非负整数是成功,-1是失败
lseek(fd , 0 ,SEEK_SET);//将文件偏移量设置到文件开始第一个字节上
lseek(fd , -1 ,SEEK_END);//将文件偏移量设置在文件倒数第一个字节上

在数据写入成功后,把文件偏移量移到文件开始第一行字节上

 if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
 if(rv == -1)
 {
     printf("写入失败\n");
 }
 else
 {
     printf("写入数据成功\n");
     goto cleanup;
 }
 lseek(fd,0,SEEK_SET);

关闭文件
当文件不再被使用时,可以调用 close() 函数来关闭被打开的文件。 除了用 close() 显示地关闭文件外,通过结束进程也能隐式地关闭被该进程打开的所有文件。要使用该函数,需要包含 #include <unistd.h> 。下面是函数的说明:

int close(int fildes);

args:
   int fildes: 要关闭文件的文件描述符
   
return:
    文件关闭状态,0是成功,-1是失败

代码实现

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


#define  BUFSIZE        1024
#define  MSG_STR  "i love  linux\n"


int main(int argc ,char **argv)
{
        int     fd = -1;
        int     rv = -1;
        char    buf[BUFSIZE];

        if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
        if(fd==-1)
       	{
              printf("文件创建失败\n");
              goto cleanup;
        }	
        else
        {
              printf("文件打开成功,fd = %d\n",fd);
        }

        if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
        if(rv == -1)
        {
               printf("写入数据失败\n");
               goto cleanup;
        }
       	else
        {
               printf("写入数据成功\n");
        }
		lseek(fd,0,SEEK_SET);
		
        memset(buf,0,sizeof(buf));
        if((rv = read(fd,buf,sizeof(buf)))<0)
        if(rv == -1)
        {
            printf("读写失败\n");
            goto cleanup;
        }
        else
        {
            printf("读写成功\n");
        }

        printf("读写的数据 %d\n %s\n",rv,buf);

cleanup:
        close(fd);
        return 0;
}

strerror函数
C 库函数 char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。strerror 生成的错误字符串取决于开发平台和编译器。

char *strerror(int errnum)

进一步代码实现

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>


#define  BUFSIZE        1024
#define  MSG_STR        "hello world\n"


int main(int argc ,char **argv)
{
        int     fd = -1;
        int     rv = -1;
        char    buf[BUFSIZE];


        fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
        if(fd < 0)
        {
                perror("创建/打开文件失败");
                return 0;
        }
        printf("打开文件成功 [%d]\n",fd);

        if((rv = write(fd,MSG_STR,strlen(MSG_STR))) < 0)
        {
                printf("文件写入失败:%s\n",strerror(errno));
                goto cleanup;
        }
        lseek(fd, 0 ,SEEK_SET);

        memset(buf, 0 ,sizeof(buf));
        if((rv = read(fd,buf,sizeof(buf))) < 0)
        {
                printf("读文件失败:%s\n",strerror(errno));
                goto cleanup;
        }
        printf("从文件读出%d数据:%s\n",rv,buf);

cleanup:
        close(fd);
        return 0;
}

有关【Linux】在Linux下的文件IO(一)的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  8. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  9. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

随机推荐