LinuxC应用开发学习笔记(八)—系统调用I/O

UNIX环境高级编程(APUE)

一、文件系统

第四、六、七章

二、 I/0

3-5章、14章
文件I/O
标准I/O

三、并发

信号10章节
多进程并发
多线程并发10-11章节

四、IPC:进程间通信

8章节 进程基础(涉及到多进程)
13章 守护进程
15-16章
注意事项:
1、弃用root用户
2、重构代码
3、课堂重点:项目,课堂代码,面试题,实验性题目,推荐书籍课后题

I/O:input & output,是一切实现的基础

标准io:stdio 依赖于sysio
系统调用io:sysio(文件IO)
优先使用标准IO,可移植性强。

I/O操作

stdio:FILE 类型贯穿始终
打开文件流
fopen();
r r+ 文件必须存在,w w+ a a+文件不存在则创建。

关闭文件流
fclose();

从文件流获取单个字符
fgetc();

从文件流打印单个字符
fputc();

获取从文件流中获取字符串
fgets();
eg:fgets(buf,size,stream);
退出条件
1、输入字符达到了size-1
2、‘\n’

从文件流中打印字符串
fputs();
代码例程。

从指定文件流中读取n个size大小的数据,存储到buf中
fread(buf,size,nmemb,fp);
1->数据量足够
2->只有5个字节
fread(buf,1,10,fp);
1->返回值 10->10字节
2->5 ->5字节

fread(buf,10,1,fp);
1->返回值 1->10字节
2- 不够1个字节 返回为0 ->???喵喵喵?

向文件流中写入数据

fwrite();

打印函数
printf();
更倾向于用 fprintf
指向一个指针的流
其他文件打印/输出流

sprintf()、snprintf()、scanf()、fscanf()、sscanf();

跳转文件流到指定字符位置
fseek();
相当于rewind的封装。可以帮助完成空洞文件。

返回字符长度
ftell();

返回到文件开头位置
rewind();

刷新缓冲区
fflush();
缓冲区的作用:大多数情况下是好事,合并系统调用

行缓冲:换行的时候刷新,满了的时候刷新,强制刷新(标准输出是这样的,因为这是终端设备)
全缓冲:满了的时候刷新,强制刷新(默认,只要不是中断设备)
无缓冲:如stderr,需要立即输出的内容

用于设定文件流的缓冲区
setvbuf

打开文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
    FILE *fp;
    fp = fopen("tmp","w");
    if (fp == NULL)
    {
        /*case 1:*/
        fprintf(stderr,"fopen() failed! errno = %d \n ",errno);
        /*case 2:常用*/
        perror("fopen()");
        /*case 3:常用*/
        fprintf(stderr,"fopen():%s\n ",strerror(errno));
        exit(1);
    }
    puts("OK!");
    fclose(fp);
    exit(0);
}
最大打开文件个数
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
    int count;
    FILE *fp = NULL;
    while(1)
    {
    		/*以读的形式打开文件*/
        fp = fopen("tmp","w");
        if (fp == NULL)
        { 
            perror("fopen()");
            break;
        }
        count++;
    }
    /*打印出最大能够打印出的文件个数*/
    printf("count = %d\n",count);
    exit(0);
}
统计文件字符个数
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main(int argc,char **argv)
{
    FILE *fp,*fpd;
    int count = 0;
    /*判断参数是否足够,不足则报错*/
    if (argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    } 
     /*以读的形式打开文件*/
    fp =  fopen(argv[1],"r");
    if (fp == NULL)
    {
        perror("fpoen()");
        exit(1);
    }
    /*跳转到文件尾巴*/
    fseek(fp,0,SEEK_END);
    /*打印出文件字符个数*/
    printf("%ld\n",ftell(fp));
    fclose(fp);
    exit(0);
}
自制copy函数
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main(int argc,char **argv)
{
    FILE *fps,*fpd;
    int ch;
	/*判断参数是否足够,不足则报错*/
    if (argc < 3)
    {
        fprintf(stderr,"Usage : %s <source_filename> <dest_filename>\n",argv[0]);
        exit(1);
    }
    fps = fopen(argv[1],"r");
    if (fps == NULL)
    {
    	/*释放fps*/
        fclose(fps);
        perror("fopen()");
        exit(1);
    }    
    fpd = fopen(argv[2],"w");
    if (fpd == NULL)
    {
        perror("fopen()");
        exit(1);
    }  
    while (1)
    {
    		/*获取文件字符*/
        ch = fgetc(fps);
        /*如果到达文件尾*/
        if (ch == EOF)
           break;
        /*打印字符到文件fpd中*/
        fputc(ch,fpd);
    }
/*释放文件指针fpd、fps*/
    fclose(fpd);
    fclose(fps);
    exit(0);
}
自制fgets获取字符串函数
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define BUFSIZE 1024

int main(int argc,char **argv)
{
    FILE *fps,*fpd;
    char buf[BUFSIZE];
    /*判断参数是否足够,不足则报错*/
    if (argc < 3)
    {
        fprintf(stderr,"Usage : %s <source_filename> <dest_filename>\n",argv[0]);
        exit(1);
    }  
    
    fps = fopen(argv[1],"r");
    if (fps == NULL)
    {
    		/*指针为空,直接释放*/
        fclose(fps);
        perror("fopen()");
        exit(1);
    }  
    
    fpd = fopen(argv[2],"w");
    if (fpd == NULL)
    {
        perror("fopen()");
        exit(1);
    }
    
    while (fgets(buf,BUFSIZE,fps)!= NULL)
    {
       fputs(buf,fpd);
    }
    fclose(fpd);
    fclose(fps);
    exit(0);
}

自制fread
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define BUFSIZE 1024

int main(int argc,char **argv)
{
    FILE *fps,*fpd;
    char buf[BUFSIZE];
    int n;

    if (argc < 3)
    {
        fprintf(stderr,"Usage : %s <source_filename> <dest_filename>\n",argv[0]);
        exit(1);
    }
    
    fps = fopen(argv[1],"r");
    if (fps == NULL)
    {
        fclose(fps);
        perror("fopen()");
        exit(1);
    }
    
    fpd = fopen(argv[2],"w");
    if (fpd == NULL)
    {
        perror("fopen()");
        exit(1);
    }
    
    while ((n = fread(buf,1,BUFSIZE,fps))>0)
    {
       fwrite(buf,1,n,fpd);
    }

    fclose(fpd);

    fclose(fps);
    
    exit(0);
}

获取文件字符长度
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main(int argc,char **argv)
{
    FILE *fp,*fpd;
    char *linebuf;
    int count = 0;
    size_t linsize;

    if (argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    
    fp =  fopen(argv[1],"r");
    if (fp == NULL)
    {
        perror("fpoen()");
        exit(1);
    }
    
    while (1)
    {
        if(getline(&linebuf,&linsize,fp) < 0)
        break;
        printf("%ld\n",strlen(linebuf));
        printf("%d\n",linsize);   
    }  
    fseek(fp,0,SEEK_END);
    printf("%ld\n",ftell(fp));
    fclose(fp);
    exit(0);
}

系统调用IO

P135:重要
文件描述符的概念(通俗来讲,整形数找到指针,指针找到结构体,通过结构体来找到正确的文件。文件描述符通常使用当前范围内最小的)。
fd文件描述符是文件IO中贯穿始终的类型。

文件IO 的操作:

open(), close(), read(), write(), lseek():相当于是flseek和ftell的综合,返回值是字节的距离。

f***都是依赖文件I/O
文件描述符的本质是一个整型数组下标,int类型。

标准IO和系统调用IO的映射:

r -> O_RDONLY
r+ -> O_RDWR
w -> O_WRONLY|O_CREAT|O_TRUNC
w+ -> O_RDWR|O_CREAT|O_TRUNC

将文件IO与标准IO进行区别

举例:传达室老大爷跑邮局。标准IO有缓冲操作。文件IO没有缓冲操作。
区别:响应速度和吞吐量。文件IO响应速度快,标准IO 的吞吐量大。

面试:如何是一个程序变快?两面回答。
用户体验是吞吐量。最好用标准IO。
提醒:标准IO与文件IO不可混用。
函数fileno(),可以实现标准IO转换成文件IO。
函数 fdopen(),可以实现把一个已经打开的文件描述符封装成一个FILE*使用。

IO的效率问题

习题:将mycopy.c程序进行更改,将BUFSIZE的值放大,并且观察进程的时间,注意性能最佳拐点出现时的BUFSIZE值,以及何时程序会出问题。

文件共享

多个任务共同操作一个任务或者协同完成任务。

面试:删除一个文件的第十行。
补充函数 :truncate //截断文件多长。
/******************************************/
1-> open r ->fd1->lseek 11

1-> open r+(读写) ->fd2->lseek 10

while()
{
1->fd1->read
2->fd2-> write
}

/******************************************/
process1 ->open ->r
process2 ->open ->r+

p1->read ->p2->write

原子操作:不可分割的操作。
原子:不可分割的最小单位。
原子操作的作用:解决竞争和冲突。

程序中的重定向是怎么实现的:dup,dup2

同步:
sync(全局催促)
解除设备挂载
fsync
同步一个文件的 buff或者cache。
fdatasync
只刷数据,不刷亚数据(文件最后的修改时间,文件属性)。

管家函数:
fcntl() ; 文件描述符所变的魔术,都来自fantl();

ioctl(); 设备相关的内容

/dev/fd/目录:虚目录,显示当前文件描述符的信息。

复制的实现
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFSIZE 1024


int main(int argc,char **argv)
{
    int sfd,dfd;
    int ch,len,ret,pos = 0;
    char buf[BUFSIZE];

    if (argc < 3)
    {
        fprintf(stderr,"Usage : %s <source_filename> <dest_filename>\n",argv[0]);
        exit(1);
    }
    
    sfd = open(argv[1],O_RDONLY);
    if (sfd < 0)
    {
        perror("open()");
        exit(1);
    }

    dfd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600);
    if (dfd < 0)
    {
        close(sfd);
        perror("open()");
        exit(1);
    }
    
    while (1)
    {
        /*len read 10 byte*/
       len = read(sfd,buf,BUFSIZE);
        if (len<0)
        {
            perror("read()");
            break;
        }
        if (len == 0)
        {
            break;
        }

        pos = 0;

        /*判断len是否全部填入*/
        while (len > 0)
        {
            /*from buf read len byte to dfd*/
            ret = write(dfd,buf+pos,len);
            if (ret < 0 )
            {
                perror("write()");
                exit(1);
            }
            /*计算下一个写入的位置*/
            pos += ret;
            /*计算剩下的没填入的字节数*/
            len -= ret;
        }    
    }

    close(dfd);

    close(sfd);
    
    exit(0);
}
重定向
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFSIZE 1024
#define FNAME "/tmp/out"

int main(int argc,char **argv)
{
    /*完成重定向操作*/
    int fd;
    
    fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
    if (fd < 0 )
    {
        perror("open()");
        exit(1);
    }

    close(1);
    /*文件描述符复制了一份,产生一个新的没用过的最小描述符,和之前指向同一个结构体*/
    // dup(fd);
    // close(fd);
    
    /*原子操作*/
    dup2(fd,1);
    if (fd != 1)
    {
        close(fd);
    }
    /************************************/
    puts("hello!");
    exit(0);
}
读取目录readdir的使用—P150
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glob.h>
#include <dirent.h>

#define PAT "/etc"

int main(int argc,char **argv)
{
    DIR *dp;
    struct dirent *cur;

    dp = opendir(PAT);
    if (dp == NULL)
    {
        perror("opendir()");
        exit(1);
    }

    while ((cur = readdir(dp)) != NULL)
        puts(cur->d_name);
        
    closedir(dp);

    exit(0);
}

stat函数详解

函数原型:int stat(const char *path, struct stat *buf)

返回值:成功返回0,失败返回-1;

​ 参数:文件路径(名),struct stat 类型的结构体

struct stat
{
    dev_t     st_dev;     /* ID of device containing file */文件使用的设备号
    ino_t     st_ino;     /* inode number */    索引节点号 
    mode_t    st_mode;    /* protection */  文件对应的模式,文件,目录等
    nlink_t   st_nlink;   /* number of hard links */    文件的硬连接数  
    uid_t     st_uid;     /* user ID of owner */    所有者用户识别号
    gid_t     st_gid;     /* group ID of owner */   组识别号  
    dev_t     st_rdev;    /* device ID (if special file) */ 设备文件的设备号
    off_t     st_size;    /* total size, in bytes */ 以字节为单位的文件容量   
    blksize_t st_blksize; /* blocksize for file system I/O */ 包含该文件的磁盘块的大小   
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */ 该文件所占的磁盘块  
    time_t    st_atime;   /* time of last access */ 最后一次访问该文件的时间   
    time_t    st_mtime;   /* time of last modification */ /最后一次修改该文件的时间   
    time_t    st_ctime;   /* time of last status change */ 最后一次改变该文件状态的时间   
};

stat结构体中的st_mode 则定义了下列数种情况:

    S_IFMT   0170000    文件类型的位遮罩
    S_IFSOCK 0140000    套接字
    S_IFLNK 0120000     符号连接
    S_IFREG 0100000     一般文件
    S_IFBLK 0060000     区块装置
    S_IFDIR 0040000     目录
    S_IFCHR 0020000     字符装置
    S_IFIFO 0010000     先进先出
​
    S_ISUID 04000     文件的(set user-id on execution)位
    S_ISGID 02000     文件的(set group-id on execution)位
    S_ISVTX 01000     文件的sticky位
​
    S_IRUSR(S_IREAD) 00400     文件所有者具可读取权限
    S_IWUSR(S_IWRITE)00200     文件所有者具可写入权限
    S_IXUSR(S_IEXEC) 00100     文件所有者具可执行权限
​
    S_IRGRP 00040             用户组具可读取权限
    S_IWGRP 00020             用户组具可写入权限
    S_IXGRP 00010             用户组具可执行权限
​
    S_IROTH 00004             其他用户具可读取权限
    S_IWOTH 00002             其他用户具可写入权限
    S_IXOTH 00001             其他用户具可执行权限
​
    上述的文件类型在POSIX中定义了检查这些类型的宏定义:
    S_ISLNK (st_mode)    判断是否为符号连接
    S_ISREG (st_mode)    是否为一般文件
    S_ISDIR (st_mode)    是否为目录
    S_ISCHR (st_mode)    是否为字符装置文件
    S_ISBLK (s3e)        是否为先进先出
    S_ISSOCK (st_mode)   是否为socket
    若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名,在linux中,最典型的就是这个/tmp目录啦。
​

st_mode 的结构

st_mode 主要包含了 3 部分信息:

1、 15-12 位保存文件类型

2、 11-9 位保存执行文件时设置的信息

3、 8-0 位保存文件访问权限

示例:mydu查看文件大小

模拟du命令查看当前目录下的文件大小

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glob.h>
#include <dirent.h>

#define  PATHSIZE 1024

static int path_noloop(const char *path)
{
    char *pos;
    //找到最后一次出现‘/’的字符后的串,最右边的串
    pos =  strrchr(path,'/');
    if (pos == NULL)
        exit(1);
    //解析隐藏文件
    if(strcmp(pos+1,".") == 0||strcmp(pos+1,"..")==0)
        return 0;
    return 1;
}


static int64_t mydu(const char *path)
{
    int64_t sum;
    static struct stat statres; 
    static char nextpath[PATHSIZE];
    glob_t globres;

    //解析传入路径,将解析结果保存在statres中。
    if(lstat(path,&statres)<0)
    {
        perror("lstat()");
        exit(1);
    }

    //判断是否是目录文件,非目录返回
    if(!S_ISDIR(statres.st_mode))
        return statres.st_blocks;
    

    /*解析非隐藏的文件,到globres当中*/
    strncpy(nextpath,path,PATHSIZE);//path传入nextpath
    strncat(nextpath,"/*",PATHSIZE);//追加新的路径
    glob(nextpath,0,NULL,&globres);
    
    /*解析隐藏的文件,到globres当中*/
    strncpy(nextpath,path,PATHSIZE);
    strncat(nextpath,"/.*",PATHSIZE);
    glob(nextpath,GLOB_APPEND,NULL,&globres);//追加,把这次解析的结果(隐藏的结果),追加到结果中

    sum = 0;
    for (int i = 0; i < globres.gl_pathc; i++)
    {
        //解析的path不是循环
        if (path_noloop(globres.gl_pathv[i]))
        {
            //递归调用
            sum += mydu(globres.gl_pathv[i]);
        }
        
    }
    sum += statres.st_blocks;

    globfree(&globres);

    return sum;

}


int main(int argc,char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    printf("%ld\n",mydu(argv[1])/2);
    exit(0);
}
}
username 的使用
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glob.h>
#include <dirent.h>
#include <pwd.h>

int main(int argc,char **argv)
{
    struct passwd *pwdline;
      if (argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }  
    pwdline = getpwuid(atoi(argv[1]));
    puts(pwdline->pw_name);
    exit(0);
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值