您现在的位置是:首页 > 正文

【Linux】系统调用文件操作

2024-01-30 20:22:19阅读 2

系统调用与库函数

文件操作的系统调用,系统调用的实现在内核中,需要用户态切换到内核态使用。

文件操作用库函数和系统调用很相似,但是库函数一般不会切到内核。

库函数的实现在库中,是用户自己的代码。

四个系统调用:

打开:open();

int open(const char* pathname, int flags);//用于打开一个已存在的文件
int open(const char* pathname, int flags,mode_t mode);//用于新建一个文件,
并设置访问权限

参数介绍:

pathname:将要打开的文件路径和名称

flags : 打开标志,如 O_WRONLY 只写打开

O_RDONLY 只读打开

O_RDWR 读写方式打开

O_CREAT 文件不存在则创建

O_APPEND 文件末尾追加

O_TRUNC 清空文件,重新写入

mode: 权限 如: “0600”

返回值: 为文件描述符


读:read();

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

参数介绍:

fd 对应打开的文件描述符

buf 存放数据的空间

count 计划一次从文件中读多少字节数据

返回值: 为实际读到的字节数


写:write();

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

参数介绍:

fd 对应打开的文件描述符

buf 存放待写入的数据

count 计划一次向文件中写多少数据


关闭:close();

int close(int fd);

参数介绍:

fd 要关闭的文件描述符


文件描述符

Linux系统没有文本和二进制之分,所有的东西都是文件,文件描述符是一个整形数字,储存在PCB的文件描述符表中(一个数组),它从0开始递增,是文件的一个id。

每打开一个文件,就会在文件表中占一个位置,文件表默认大小为1024(可以打开1024个文件)。

文件表中的每一个指针都指向一个文件的结构体,里面有文件打开信息,使用量(状态),inode和文件偏移量。每一个文件都有一个inode,它包含了文件详细信息。

进程开始后会有三个文件默认被打开:标准输入(stdin) 0,标准输出(stdout) 1,标准错误输出(stderr) 2;这三个文件被存在文件表中


文件操作

文件操作分三步:

  1. 打开文件

  1. 读/写

  1. 关闭文件

文件放在磁盘上,可以永久存储,由内核管理文件(文件系统)。

打开文件需要文件名,r/w,(权限);


写(文件描述符,“数据”,大小)

 #include<stdio.h>
 #include<stdlib.h>
 #include<unistd.h>
 #include<string.h>
 #include<fcntl.h>
int main()
{   
    int fd = open("a.txt",O_WRONLY|O_CREAT,0600);//打开,权限0600第一个0是占位符
     printf("fd = %d\n",fd);
  
     write(fd,"hello",5);//写
   
     close(fd);//关闭
   
     exit(0);
}

因为三个默认文件把012占用了,所以这个输出文件描述符为3

1表示屏幕标准输入,可以直接向屏幕上写数据

write(1,"hello",5);

读(文件描述符,读到的位置,大小)

int fd = open("a.txt",O_RDONLY);     
if(fd==-1)//打开失败
{
    printf("open a.txt err\n");
}
char buff[128]={0};//申请空间
int n = read(fd,buff,127);//期望读127个字符,但因为只有hello五个字符,所 以只读五个
printf("n=%d,buff=%s\n",n,buff);
close(fd);
exit(0);

如果一次读不完,需要读多次到文件末尾,当某一次读到0个字节时,文件内容读完,即n=0;每次读都会接着上一次的位置继续读。


应用:复制文件

复制文件就是一个文件一直读,另一个文件一直写。

int main()
{
//打开文件
    int fdr = open("passwd",O_RDONLY);
    int fdw = open("newpasswd",O_WRONLY|O_CREAT,0600);
    if(fdr==-1||fdw==-1)
    {
        printf("open file err\n");
        exit(0);
    }
//读、写
    char buff[1024]={0};
    int n=0;
    while((n=read(fdr,buff,1024))>0)    
    {
       write(fdw,buff,n);
    }
//关闭文件
    close(fdr);
    close(fdw);
 
    exit(0);

}

复制成功:

其他方法

可以将需要复制的文件名作为参数传入主函数

char* filename=argv[1];
char* newfilename = argv[2];
int fdr = open(filename,O_RDONLY);
int fdw = open(newfilename,O_WRONLY|O_CREAT,0600);

运行时输入./main passwd newpasswd

也可以完成上面的复制。


父、子进程读同一文件

当先打开文件再复制进程,父、子进程会共享这份文件(都读这一份),读的偏移量会相互影响。

int main()
{
    int fd = open("a.txt",O_RDONLY);
    if(fd==-1)//文件打开失败
    {
        printf("open file a.txt faild\n");
        exit(0);
    }
    pid_t pid = fork();
    if(pid==-1)//复制进程失败
    {   
        printf("fork err\n");
        exit(0);
    }   
    if(pid==0)//子进程执行读
    {   
        char buff[128]={0};
        read(fd,buff,1);
        printf("child read:%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child read:%s\n",buff);
    }   
    else//父进程执行读
    {
        char buff[128]={0};
        read(fd,buff,1);
        printf("parent read:%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent read:%s\n",buff);
    }

    close(fd);
    exit(0);
}

但当先复制进程再打开文件读,子进程和父进程是各自读各自的

int main()
{
    pid_t pid = fork();//复制进程
    if(pid==-1)
    {   
        printf("fork err\n");
        exit(0);
    }   
    int d = open("a.txt",O_RDONLY);//打开文件
    if(fd==-1)
    {   
        printf("open file a.txt faild\n");
        exit(0);
    }   
    if(pid==0)
    {   
        char buff[128]={0};
        read(fd,buff,1);
        printf("child read:%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("child read:%s\n",buff);
    }   
    else

    {
        char buff[128]={0};
        read(fd,buff,1);
        printf("parent read:%s\n",buff);
        sleep(1);
        read(fd,buff,1);
        printf("parent read:%s\n",buff);
    }

    close(fd);
    exit(0);
}

因为文件打开后文件描述符存放在文件表中,当进程复制时,PCB会先被复制一份,文件表会随着PCB一起复制

当先打开文件,再复制进程,进程被复制后,父、子进程文件表中共用一个文件文件表,共享文件及其偏移量。

当先复制进程再打开文件,这两个进程都会单独打开一个,不再共享。

网站文章

  • linux用户管理常用命令

    主题:linux用户管理常用命令 1)管理用户(user)的工具或命令; useradd 注:添加用户 adduser 注:添加用户 passwd 注:为用户设置密码 usermod 注:修改用户命令,可以通过usermod 来修改登录名、用户的家目录等等; pwcov 注:同步用户从/etc/passwd 到/etc/shadow pwck 注:pwck是校验用户配置文件/etc/passwd

    2024-01-30 20:22:12
  • npm 下载包失败解决方案

    npm 下载包失败解决方案

    npm下载依赖包失败解决方案

    2024-01-30 20:22:04
  • flink归纳总结

    1.14 Flink1 简单介绍一下 FlinkFlink 是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。并且 Flink 提供了数据分布、容错机制以及资源管理等核心功能。Flin...

    2024-01-30 20:21:58
  • Spring Cache详解

    从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:当调用一个缓存方法时会把该方法参...

    2024-01-30 20:21:28
  • SparkStreaming 介绍及 wordcount 案例

    SparkStreaming 介绍及 wordcount 案例

    一、介绍1、Spark Streaming 是什么?Spark Streaming 用于流式数据的处理。Spark Streaming 支持的数据输入源很多,例如:Kafka、Flume、Twitte...

    2024-01-30 20:21:21
  • 抽象工厂模式--实例分析

    应用场景 某手机操作系统可以根据用户不同的喜好在多种主题之间进行切换,随着主题的变化,系统中的字体、app图标、壁纸、锁屏壁纸等元素会随之发生变化。 使用抽象工厂模式设计手机主题库 创建元素接口 //...

    2024-01-30 20:21:14
  • “原来我的专业考不了公务员!” 要考公,你的专业能报什么?

    “原来我的专业考不了公务员!” 要考公,你的专业能报什么?

    #原来我的专业考不了公务员#也登上热搜,引发网友热议这里说的“考不了”并非不能考,而是专业能够选择的岗位很少。每到公务员考试报名选岗阶段,很多”公考冷门专业“的同学都非常抓狂,筛了半天愣是一个匹配专业...

    2024-01-30 20:20:44
  • GDB——GDB调试工具简介

    什么是GDB? GDB的常规应用 GDB的启动方式 什么是GDB? GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。与Window下的IDE不同,GDB是纯命令行执行的,并没有图形界面方法。 问题:既然windows下有对用户友好的图形界面的调试工具了。那么为什么我们还需要回到命令行的模式呢? 如果我们是在UNIX平台下做软件,我们会发现基本...

    2024-01-30 20:20:37
  • 打卡:Java面试系列基础题(3)

    内部类与静态内部类的区别:普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法静态内部类是相对外部类独立存在的,静态内部类中无法直接访问外部类中变量,方法,如果要...

    2024-01-30 20:20:29
  • VS编译fopen函数和fscanf函数报错

    今天用VS2017编译fopen函数和fscanf函数报错,提示不安全,建议更换为fopen_s和fscanf_s函数。error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_

    2024-01-30 20:20:01