嵌入式C-linux IO

I/O

input&output,一切实现的基础,stdio标准io,sysio系统调用IO(文件IO)

stdio:FILE类型贯穿始终

std是[standard].标准的(standard的缩写);stdio就是标准IO;

fopen();fclose();//互逆操作 堆

fgetc();fputc();fgets();fputs();fread();fwrite();

printf();scanf();

fseek();ftell();rewind();

fflush();

打开关闭

  • fopen()
    • open(linux)
    • openfile(win)
  • fclose()
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#ifdef false
#include <stdio.h>
//man手册 
FILE *fopen(const char *path,const char *mode);
FILE *fdopen(int fd,const char *mode);
FILE *freopen(const char *pathmconst char *mode,FILE *);
#endif

int main()
{
#ifdef false
    char *ptr = "abc";
    ptr[0] = 'x';//如果要修改的话,会报段错误。意图修改一个常量
    printf("%s\n",ptr);
#endif

    FILE *fp;
    fp = fopen("log","r");
    if (fp == NULL){
        fprintf(stderr,"fopen() faild! errno = %d\n",errno);//errno已经被私有化
        perror("fopen");
        printf("%s\n",strerror(errno));
        exit(1);
    }else{
        fputs("ok!\n",stdout);
        fputs("OK\n",fp);
    }
    return 0;
}

如果一个系统函数有打开和其对应的关闭操作,那么函数的返回值是放在 堆 上的(malloc 与free),否则有可能在堆上也有可能在静态区

/usr/include/asm-generic/errno-base.h
/usr/include/asm-generic/errno.h
#include <errno.h>
perror("fopen()",errno);
#include <string.h>
strerror(errno);
  • 默认最多打开的文件描述符个数
$ ulimit -a

-n: file descriptors                1024
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main()
{
    int count = 0;
    FILE *fp = NULL;

    while(1)
    {
        fp = fopen("tmp","r");
        if (fp == NULL)
        {
            perror("fopen()");
            break;
        }
        count++;
    }
    printf("%d\n",count);
    return 0;
}
//stream -> stdin stdout stderr
新文件权限产生

mod = 0666 & ~umask
修改方式

umask 022


1 umask 是什么 

当我们登录系统之后创建一个文件是会有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情。umask用于设置用户创建文件或者目录的默认权限,umask设置的是权限的“补码”,而我们常用chmod设置的是文件权限码。一般在/etc/profile,HOME/.bashprofile或者HOME/.profile中设置umask值。

2 umask是用来做什么的 

默认情况下的umask值是022(可以用umask命令查看),此时你建立的文件默认权限是644(6-0,6-2,6-2),建立的目录的默认 权限是755(7-0,7-2,7-2),可以用ls -l验证一下;

读写

  • fgetc()
  • fputc()
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

/*******
 *这节主要讲fgetc 可以用来统计文件的大小 
       int fgetc(FILE *stream);
       char *fgets(char *s, int size, FILE *stream);
       int getc(FILE *stream);
       int getchar(void);
 * *****/

int main(int argc,char **argv)
{
    FILE *fp = NULL;
    int count = 0;

    if (argc < 2){
        fprintf(stderr,"Usage...:\n");
        exit(1);
    }

    fp = fopen(argv[1],"r");
    if (fp == NULL){
        perror("fopen()");
        exit(1);
    }

    while(fgetc(fp) != EOF){
        count++;
    }
    printf("%d\n",count);
    fclose(fp);
    return 0;
}

  • fgets()
  • fputs()
    cp 的fgets/fputs版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SIZE 1024

int main()
{
    FILE *fps,*fpd;
    fps = fopen("./tmp","r");
    if (fps == NULL){
        strerror(errno);
        exit(1);
    }

    fpd = fopen("./copy","w");
    if (fpd == NULL){
        fclose(fps);
        strerror(errno);
        exit(1);
    }

    char buf[SIZE];
    while(fgets(buf,SIZE,fps)){
        fputs(buf,fpd);
    }
    fclose(fps);
    fclose(fpd);
    return 0;
}

fgets遇到 size-1 or '\n’停止

abcd
1-> a b c d '\0'//第一次
2-> '\n' '0' //第二次
#define SIZE 5
int main()
{
    FILE *fp = NULL;
    fp = fopen("./tmp","r");
    if (fp == NULL){
        perror("fopen()");
        exit(1);
    }

    char buf[SIZE];

    while(fgets(buf,SIZE,fp)){
        printf("%s\n",buf);
    }
    return 0;
}
  • fread()

  • fwrite()
    cp的fread/fwrite版本

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SIZE 1024
/***************
 *fread 实现文件复制 
 *       
 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

 size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
       一次性读写
 **************/
int main()
{
    FILE *fps,*fpd;
    fps = fopen("./tmp","r");
    if (fps == NULL){
        strerror(errno);
        exit(1);
    }

    fpd = fopen("./copy","w");
    if (fpd == NULL){
        fclose(fps);
        strerror(errno);
        exit(1);
    }

    char buf[SIZE];

    int n = 0;
    while ((n = fread(buf,1,SIZE,fps)) > 0){
        printf("%d\n",n);
        fwrite(buf,1,n,fpd);
    }
    fclose(fps);
    fclose(fpd);
    return 0;
}

打印与输入

  • printf()
  • scanf()
#include <stdio.h>
/*****************
 *sprintf atoi fprintf
 *sprintf 可以看作是 atoi(把字符串转换成整型数)的反向操作
 * **************/
int main()
{
    char buf[1024];
    int y = 2020;
    int m = 12;
    int d = 24;
    sprintf(buf,"%d/%d/%d",y,m,d);
    printf("%s",buf);
    puts(buf);
    
    //char str[]="123a456",buf[1024];
    //int year=2023,month=9,day=19;
    //sprintf(buf,"%d-%d-%d",year,month,day);
    //printf("%d\n",atoi(str));
    return 0;
}

文件指针

何谓“文件指针”?

就像读书时眼睛移动一样,文件指针逐行移动

什么时候用?

对一个文件先读后写的时候,比如:

FILE *fp = fopen();
fputc(fp)  *10;
//fseek(fp,-10,SEEK_CUR);
fgetc() *10;//无法得到刚刚写入的东西

int fseek(FILE *stream,long offset,int whence);
//设置文件指针位置 offset是偏移位置
long ftell(FILE *stream);
//返回文件指针位置
//可以利用fseek(fp,0,SEEK_END)直接读到末尾,然后用ftell读出偏移字节,即文件大小

//fseek和ftell的2G限制
void rewind(FILE *stream);
//使得文件指针回到文件开始位置
  • fseek()
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
    const int SIZE = 1024;
    FILE *fp = NULL;
    char buf[SIZE];
    buf[0] = 'A';
    buf[1] = 'A';
    buf[2] = 'A';
    buf[3] = 'A';

    fp = fopen("./tmp","w+");
    if (fp == NULL){
        strerror(errno);
        exit(1);
    }
    
    int i = 0;
    while(i < 10){
        unsigned long n = fwrite(buf,1,4,fp);
        fseek(fp,-n,SEEK_CUR);
        unsigned long len =  fread(buf,1,n,fp);
        printf("%lu\n",len);
        fseek(fp,0,SEEK_END);
        i++;
    }
    
    fseek(fp,1024,SEEK_CUR);
    return 0;
}
// int fseeko(FILE *stream, off_t offset, int whence);
// off_t ftello(FILE *stream);
  • ftell()
  • rewind()

刷新缓存

  • fflush()
printf("Before while");//行缓冲刷新模式
fflush();
while(1)
printf("After while";)
// 缓冲区
// 缓冲区的作用:大多数情况是好事,合并系统调用

//行缓冲:换行时刷新,满了的时候刷新,强制刷新(标准输出stdout是这样的,因为是终端设备)

//全缓冲:满了的时候刷新,强制刷新(默认,只要不是终端设备)

//无缓冲:如stderr,需要立即输出的内容
    
 //setvbuf,控制缓冲模式

取到完整的一行

  • getline()
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc ,int **argv){
    
    if(argc<2){
        fprintf(stderr,"Usage ...");
        exit(0);
    }

    FILE *fp=NULL;
    //下面两个参数必须初始化
    size_t linesize=0;
    char *linebuf=NULL;

    fp=fopen(argv[1],"r");
    if(fp==NULL){
        perror("fopen()");
        exit(0);
    }
    while(1){
        if(getline(&linebuf,&linesize,fp)<0){
            exit(0);
        }
        printf("%d\n",strlen(linebuf));
        printf("--->%d\n",linesize);
    }

    fclose(fp);
    exit(1);
}
1、 使用strerror函数
函数原型:
char * strerror(int errno)
使用方法:
fprintf(stderr, "%s", strerror(errno));
通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误。
2、 使用perror函数
函数原型:
void perror(const char *s)
使用说明
参数s指定的字符串是要先打印出来的信息,可以由我么自己定义,然后系统会在s字符串后加上错误原因的字符串。
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    FILE *fp = NULL;
    fp = fopen("./tmp","r");
    if (fp == NULL){
        strerror(errno);
        exit(1);
    }

    size_t linesize = 0;
    char *line = NULL;

    while(1){
        if (getline(&line,&linesize,fp) < 0){
            break;
        }
        printf("%ld\n",strlen(line));
        printf("%ld\n",linesize);
    }
    return 0;
}

// ssize_t getline(char **lineptr, size_t *n, FILE *stream);
第一个参数是每行实占字符,第二个参数是每行能获取lineptr字符的长度,如果不够会通过realloc来增加空间,第三个参数文件流;前两个参数必须初始化。

临时文件

​ 1.如何不冲突 2.及时销毁

  • tmpfile()//产生匿名文件,直接占一块内存空间
  • tmpnam() //这个函数创建临时文件分了两部走,不怎么安全
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
    FILE *tmpfp;
    tmpfp = tmpfile();

    const char* oribuf = "2020/12/25";
    int SIZE = 0;
    while(*(oribuf+SIZE) != 0){
        SIZE++;
    }
    printf("%d\n",SIZE);
    
    fwrite(oribuf,1,SIZE,tmpfp);
    fseek(tmpfp,0,SEEK_SET);
    

    FILE *fp;
    fp = fopen("tmp","w");
    char desbuf[SIZE];
    int n = fread(desbuf,1,SIZE,tmpfp);
    fwrite(desbuf,1,n,fp);

    fclose(tmpfp);
    fclose(fp);
    return 0;
}

sysio(系统IO)

文件IO/系统调用IO

fd(文件描述符)是在文件IO中贯穿始终的类型

文件描述符的概念

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

FILE *

(整型数,文件描述符的本质是数组下标,文件描述符优先使用当前可用范围内最小的)

文件IO操作

  • open
  • close
  • read
  • write
  • lseek
//int open(const char *pathname, int flags);
//int open(const char *pathname, int flags, mode_t mode);
//int close(int fd);

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

#define Countsize 1024 

int main(int argc,char **argv){

    char buf[Countsize];

    if(argc<3){
        fprintf(stderr,"error information....\n");
        exit(1);
    }
   int file1 = open(argv[1],O_RDONLY);
    if(file1<0){  
        perror("open()--1\n");
        exit(1);
    }
   int file2 =open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
    if(file2<0){
        close(file2);
        perror("open()--2\n");
        exit(1);
    }
    int len = 0;
    int ret = 0;
    while(1){
        len=read(file1,buf,Countsize);
        if(len==0){
            break;
        }
        if(len<0){
            perror("read()");
            break;
        }
        int pos=0;
        while(len>0){
            ret=write(file2,buf+pos,len);
            if(ret<0){
                perror("write()");
                exit(1);
            }
            pos+=ret;
            len -= ret;
        }
    }

    close(file2);
    close(file1);
    exit(0);
}
// r- ===>O_RDONLY
// r+ ===>O_RDWR
// w  ===>O_WRONLY|O_CREAT|O_TRUNC
// w+ ===>O_RDWR|O_CREAT|O_TRUNC
//mode_t mode参数:例如文件test.txt 全部权限打开为0777,对应的权限为rwxrwxrwx,第一组为用户,第二组为组,第三组为其他人

文件IO与标准IO的区别

  • 区别 标准IO的吞吐量大 系统IO的响应速度快(是对程序而言,对用户而言stdio的速度“更快”)
  • 面试题:如何使一个程序变快?
  • 用户角度更注重吞吐量,所以实际使用中标准IO用的更多
  • 转换 fileno fdopen
  • 注意:标准IO(具有缓冲机制)与系统IO不可以混用(结构不一样,不能混用)
  • 举例 :传达室老大爷跑邮局
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    for (int i = 0;i < 1025;i++){
        putchar('a');
        write(1,"b",1);//stdout
    }

    return 0;
}
//strace 命令 跟踪看效果

//[sysio]$ time ./mycpy mycpy test
real    0m0.005s
user    0m0.003s
sys     0m0.001s

上面程序可以用来查看一个可执行文件的系统调用,运行可以看到先进行1024次系统调用,然后缓冲区满了1024合并为一次系统调用。

IO的效率问题

文件共享

​ 多个任务共同操作一个文件或者协同完成任务

truncate 或者 ftruncate

//伪代码
while(){
	lseek11 +read + lseek10 +write
}

1->open r ->fd1 --> lseek 11
2->open r+ ->fd2 -->lseek 10
while(){
 1->fd1->read
 2->fd2->write
}
//进程间通信
process1 -> open ->r
process2 -> open ->r+

p1->read -> p2 ->write

原子操作

不可分割的操作

  • 作用: 解决竞争和冲突 比如tmpnam就操作不原子

程序中的重定向

  • dup
    将传入的文件描述符复制到可使用的(未使用的)最小新文件描述符,在下面的例子中,将标准输出关闭后,文件描述符1空闲(不发生竞争时),dup将会把打开了文件/tmp/out的文件描述符复制给文件描述符1 ,之后对文件描述符1 的操作就相当与操作文件/tmp/out
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define FNAME "/tmp/out"


int main()
{
    int fd;
    fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
    if (fd < 0) {
        perror("open()");
        exit(1);
    }
    //dup 不原子
    close(1);//关闭标准输出
    dup(fd);
    close(fd);
    
    /*********************/
    printf("Hello world\n");
    return 0;
}
//可以用strace命令跟踪代码,帮助理解
/*write(1, "dup2\n", 5)                   = -1 EBADF (Bad file descriptor)*/
  • dup2
    dup2可以避免关闭文件描述符后被其他线程抢占
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define FNAME "/tmp/out"


int main()
{
    int fd;
    fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
    if (fd < 0) {
        perror("open()");
        exit(1);
    }
    //dup2 原子
    dup2(fd,1);
	
	if (fd != 1) {//打开的文件描述符如果不是1自己,就可以把他关掉了,有重定向后的 1 可以访问文件,保持尽量少的资源使用
		close(fd);
	}

    /*********************/
    printf("Hello world\n");
    return 0;
}

同步

  • sync
    设备即将解除挂载时进行全局催促,将buffer cache的数据刷新
  • fsync
    刷新文件的数据
  • fdatasync
    刷新文件的数据部分,不刷亚数据(文件时间,文件属性),不修改文件元数据

fcntl():

文件描述符所变的魔术几乎都来源与该函数

ioctl():

ioctl - control device

设备相关的内容

/dev/fd/目录:

虚目录,显示的是当前进程的文件描述符信息。
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define FNAME “/tmp/out”

int main()
{
int fd;
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if (fd < 0) {
perror(“open()”);
exit(1);
}
//dup2 原子
dup2(fd,1);

if (fd != 1) {//打开的文件描述符如果不是1自己,就可以把他关掉了,有重定向后的 1 可以访问文件,保持尽量少的资源使用
	close(fd);
}

/*********************/
printf("Hello world\n");
return 0;

}


### 同步
- sync
设备即将解除挂载时进行全局催促,将buffer cache的数据刷新
- fsync
刷新文件的数据
- fdatasync
刷新文件的数据部分,不刷亚数据(文件时间,文件属性),不修改文件元数据

### fcntl():

文件描述符所变的魔术几乎都来源与该函数

### ioctl():

 ioctl - control device 

设备相关的内容

### /dev/fd/目录:

虚目录,显示的是当前进程的文件描述符信息。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值