Linux系统编程-读写IO

一:系统调用

3~4G系统的空间,这部分是由操作系统来管理的,如果我们需要操作这块的空间,只能通过系统提供的函数才能进行访问,系统提供了大概200多个系统函数,我们把系统函数的调用称之为系统调用。

二:系统函数

我们在C代码中经常使用 printf() 函数,但是最终的输出是显示在屏幕上的,那么怎么就能够在屏幕上进行打印输出结果呢?其实,printf() 函数最终是通过调用系统函数 write(),然后调用显示驱动程序,最终显示在界面上的。write() 函数就是系统函数,我们可以通过 man 2 write 的命令来查看 write() 函数的相关用法。

三:普通函数和内核函数查看方法

open close read write 这是比较常见的四种内核函数,在Linux中,我们一般用 man 2 xx  来查看某系统函数的用法, man 3 xx 来查看普通函数的用法。

1. 比如 执行 man 2 read 就可以得到 read 内核函数的用法

2. 比如执行  man 3 fread 就可以查看 fread 普通函数的用法 

四:open 函数的介绍

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

pathname: 文件的路径。

flags: 1. O_APPEND 追加内容,2. O_CREAT 没有文件的时候创建, 3.O_TRUNC 会截断,即每次打开文件的时候都会清空内容。

mode_t: 就是创建文件权限。如果需要只是当前用户可读可写的:S_IRUSR|S_IWUSR

返回的是一个文件句柄fd,我们可以直接通过fd 执行包括 read, write, close 等操作。

五:read 函数介绍

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

fd:文件句柄

void  *buf:读入的缓冲区

count: 每次读入的字节数

ssize_t:返回的是读到的真实个数

六:write 函数介绍

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

int fd:文件句柄

void *buf: 写入的缓冲区

 size_t count: 每次写入到缓冲区的字节数

ssize_t: 返回的真实写入的个数

七:文件操作错误提示

文件操作错误,会有一个宏定义 errno,通过 strerror(errnum) 获取错误信息,需要打印错误快捷的方式是 perror。strerror 和 perror  都是普通函数,可以通过man 3 来查看用法。

void perror(const char *s); s 是错误信息的描述,一般使用 perror("perror msg"); 这种会将具体的错误信息跟在后面,如 perror msg: No such file or directory 。

八:拷贝文件

1. 使用普通的 fgetc(),fputc() 函数进行文件读写操作

// f_fget_fput.c
#include<stdio.h>
#include<stdlib.h>


int main(int argc, const char *argv[]){
    if (argv[1] == NULL){
        printf("please input inputfile name\n");
        return 1;
    }
    if (argv[2] == NULL){
        printf("please input outputfile name\n");
        return 1;
    } 
    FILE *rfp = NULL;
    FILE *wfp = NULL;

    // 打开文件
    rfp = fopen(argv[1],"r");
    wfp = fopen(argv[2],"w");
    // 检查文件是否打开成功
    if (NULL == rfp){
        printf("Open the %s error!\n", argv[1]);
        perror("perror msg"); // perror msg: No such file or directory
        exit(1);
    }
    if (NULL == wfp){
        printf("Open the %s error!\n", argv[2]);
        perror("perror msg");
        exit(1);
    }
    // 读取文件并将读取的字符保存到另外一个文件中
    // 定义一个char 变量,用来打开和接收文件内容
    char ch = 0;
    while( (ch=fgetc(rfp)) !=EOF){
         fputc(ch, wfp);
    }
    printf("the file %s has copied to %s\n", argv[1], argv[2]);
    // 关闭文件
    fclose(rfp);
    fclose(wfp);
    return 0;
}
zfz:01-C++简单介绍 zhangfengzhou$ gcc f_fget_fput.c -o f_fget_fput
zfz:01-C++简单介绍 zhangfengzhou$ ./f_fget_fput a.txt b.txt
the file a.txt has copied to b.txt
zfz:01-C++简单介绍 zhangfengzhou$ 

2. 使用内核函数 read(),write() 来进行读写操作

#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>


int main(int argc, char* argv[]){
  int fd1, fd2, flag;
  char buff[1];  

  fd1 = open(argv[1], O_RDONLY); // 打开文件失败,则异常返回
  if (fd1 == -1) {
      printf("%s:%s\n", strerror(errno), argv[1]); 
      return -1;
  } 
  //664 表示的是权限     
  fd2 = open(argv[2], O_WRONLY|O_CREAT, 664); // 打开文件失败,则异常返回
  if (fd2 == -1){
      printf("%s:%s\n", strerror(errno), argv[2]);
      return -1;
  }
  while ((flag= read(fd1, buff, 1))>0)
  {
      write(fd2, buff, flag);
  }
  close(fd1);
  close(fd2);
  return 0;
}
zfz:01-C++简单介绍 zhangfengzhou$ gcc f_read_write.c -o f_read_write
zfz:01-C++简单介绍 zhangfengzhou$ ./f_read_write a.txt b.txt
zfz:01-C++简单介绍 zhangfengzhou$ 
// 引入 unistd.h 就可以很简便的引入系统内核函数的头文件
#include <unistd.h>
errno 是表示创建或者访问文件错误的编号
strerro(errnum) 是将errno 编号翻译成可读的信息

在使用open函数创建文件的时候,设置的文件权限不一定是最终的文件权限,最终权限是和umask一起决定的。

参考:Linux文件默认权限和umask 

 3. 内核函数和普通函数的区别

其实通过测试情况来看,普通函数比内核函数执行效率更高,原因在于调用内核函数会切换到内核态,且反复切换上下文环境会比较耗时,所以内核函数就慢了,同时普通的写函数是带有缓冲区的,当缓冲区满的时候,即 buf = 4096 长度的时候,就会将数据刷新到内核区域,避免了多次进入内核区域,提高了效率,所以应该尽量减少内核函数调用。

内核函数什么时候调用?

1. 当你已经了解普通函数的实现机制,认为自己当前项目需要优化。

2. 没有较好的封装时,可以自己直接调用系统函数。

3. 有普通函数时,尽量调用普通函数。

九:系统中断

系统中断是计算机学中的一个名词,一般是指硬件中断和软件中断的综合,中断发生后,系统会停止响应,并执行中断响应。

系统中断,一般是硬件中断和软件中断的综合,“中断”是一个计算机术语,意思跟我们的请求差不多,鼠标、键盘、板卡或者是一些系统内核组件,要想为你服务,都要向系统提出申请,然后等待操作系统的分配。如果没有这个过程,你什么也干不了。CPU占用高的原因就是,系统要保持“随时”能为你提供服务,就必须保证它的优先权力。

所谓中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保留现场后自动地转去执行相应的处理程序,处理完该事件后再返回断点继续执行被“打断”的程序。

中断可分为三类,第一类是由CPU外部引起的,称作中断,如I/O中断、时钟中断、控制台中断等。第二类是来自CPU的内部事件或程序执行中的事件引起的过程,称作异常,如由于CPU本身故障(电源电压低于1.05V或频率在47~63Hz之外)、程序故障(非法操作码、地址越界、浮点溢出等)等引起的过程。第三类由于在程序中使用了请求系统服务的系统调用而引发的过程,称作“陷入”(trap,或者陷阱)。前两类通常都称作中断,它们的产生往往是无意、被动的,而陷入是有意和主动的。

中断分为硬中断和软中断,函数调用属于软中断。

十: PCB 进程控制块

PCB 进程控制块就是一个结构体 task_struct,存在于内核中,这个结构体内有很多成员变量,其中之一就是文件描述符表。一个进程中的文件描述符表默认有三个进程描述符,STDIN(0),STDOUT(1),STDERR(2),接下来,在这个进程中每打开一次文件,就会有一个文件描述符添加到这个文件描述符表中,而每关闭一个文件,就会在文件描述符表中减少一个占用。在一个进程中,一般最大的文件打开数目是1024,但是如果文件句柄超过800就会比较危险。多次打开同一个文件而没有关闭,那么打开多少次,就会多占用多少个文件描述符表中的位置。

十一:lseek 和 fseek

十二:stat 和 lstat

stat f_fget_fput 可以查看文件的去权限(0755/-rwxr-xr-x),inode 

Inode:  411892,记录文件的权限和位置

man 2 stat 查看stat 用法

stat 和 lstat的区别:stat 会穿透的,lstat 不会穿透

利用 stat 获取文件的属性,大小

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdint.h>
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/sysmacros.h>


int main(int argc, char* argv[]){
   struct stat sb;
   const char* pathname = argv[1];
   stat(pathname, &sb);
   printf("I-node number:            %ju\n", (uintmax_t) sb.st_ino);
   printf("Mode:                     %jo (octal)\n", (uintmax_t) sb.st_mode);
   printf("Link count:               %ju\n", (uintmax_t) sb.st_nlink);
   printf("Ownership:                UID=%ju   GID=%ju\n", (uintmax_t) sb.st_uid, (uintmax_t) sb.st_gid);
   printf("Preferred I/O block size: %jd bytes\n", (intmax_t) sb.st_blksize);
   printf("File size:                %jd bytes\n",(intmax_t) sb.st_size);
   printf("Blocks allocated:         %jd\n",(intmax_t) sb.st_blocks);
   printf("Last status change:       %s", ctime(&sb.st_ctime));
   printf("Last file access:         %s", ctime(&sb.st_atime));
   printf("Last file modification:   %s", ctime(&sb.st_mtime));

   return 0;
}
[root@localhost c_learn]# gcc stat_learn.c -o stat_learn
[root@localhost c_learn]# ./stat_learn a.txt
I-node number:            411895
Mode:                     100644 (octal)
Link count:               1
Ownership:                UID=0   GID=0
Preferred I/O block size: 4096 bytes
File size:                12 bytes
Blocks allocated:         8
Last status change:       Mon Apr 19 20:36:13 2021
Last file access:         Mon Apr 19 20:36:39 2021
Last file modification:   Mon Apr 19 20:36:13 2021

十三:目录 opendir

man 3 opendir 查看 opendir 函数

#include<stdio.h>
#include<errno.h>
#include<dirent.h>
#include<sys/stat.h>
#include<stdbool.h>

bool isdirs(const char* fileName){
   struct stat buf;
   stat(fileName, &buf);
   mode_t st_mode = buf.st_mode;
   return  S_ISDIR(st_mode);
}


int main(int argc, char* argv[]){
  // 打开目录
  DIR *dir = opendir(argv[1]);
  if (dir==NULL){
     printf("open dir error\n");
     return -1;
  }
  struct dirent* dirp;
  while((dirp=readdir(dir))!=NULL){
    //printf("%s\n", dirp->d_name);
    bool isdir = isdirs(dirp->d_name);
    printf("%d %s\n", isdir, dirp->d_name);
  }
  return 0;
}
[root@localhost c_learn]# ./opendir xuexi
0 orange.txt
1 ..
1 .
0 apple.txt
[root@localhost c_learn]#

注意:C语言中没有bool 类型,如果要使用,需要引入头文件 #include<stdbool.h> 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值