I/O总结

IO:input and output

I/O:输入/输出

回顾:计算机由几部分构成

处理器,输入/输出设备,存储器,总线

处理器:运算器、控制器、寄存器

存储器:内存、外存

输入输出设备:

输入设备:键盘,鼠标,MIC,摄像头等
输出设备:显示器,喇叭等

总线:

地址总线
数据总线

哈弗结构:数据和指令使用不同的总线传输,不需要编解码,效率高

冯诺依曼结构:数据和指令使用相同的总线传输,需要复杂的编解码方法,效率低

Linux下:用户如何操作磁盘文件,想要读取指定的文件内容

Linux Kernel的结构和构成:

内存管理单元(MMU):

  1. 内存分配和管理

-》虚拟地址
-》线性地址
-》物理地址

2. 进程管理单元

APP
程序——》“任务”(单进程、多进程、单进程多进程)
死的 活的

3. 文件系统

——》文件系统是操作系统提供用户和计算机交互的结构
——》组织管理文件

4. 网络模块

提供网络协议栈等功能

5. 驱动模块

提供设备驱动

Linux文件系统的结构:

2. 系统调用

2.1 什么是系统调用

操作系统留给用户操作计算机稀有资源的接口(一系列函数)

2.2 为什么要使用系统调用函数

  1. 保护稀有资源

  1. 提供用户访问稀有资源的接口

3. 防止用户访问稀有资源的竞态导致稀有资源被破坏

2.3 哪些都是系统调用

文件IO相关函数

进程线程中学的绝大多数函数都是

网络编程中学的绝大多数函数都是

2.4 系统调用的实现过程

上述过程牵连几个问题:

  1. 中断上下文的切换

2. 函数可重入性的问题

从上图可知,系统调用很耗时

补充:

  1. 中断上下文切换的过程

——》发起中断请求

——》保存上文(用户态的运行状态)

——》切换下文(加载Kernel的指令)

——》Kernel执行

2. 函数可重入性

当函数执行过程中被中断,当中断结束后,函数是否能按照之前的状态接着运行,这样的问题,叫做可重入问题。

如果可以,称为可重入函数,该函数具备可重入性。

3. 所有的系统调用都是可重入的。

2.5 如何解决系统调用耗资源的问题

——》系统调用本身是没办法优化额

——》但是可以减少系统调用的次数

——》次数减少,但是活不能少干

所以引入缓冲机制,建立用户态缓冲区,进而减少系统调用的次数

3. IO的分类

根据封装来源不同,分为标准IO和文件IO

标准IO:标准C库提供的IO操作接口,称为标准IO

文件IO:类UNIX操作系统中,操作系统提供的POSIX系统调用(IO操作)的接口。

POSIX:可移植操作系统(类UNIX的代称)接口

3.1文件IO和标准IO的区别


4. 标准IO

4.1 标准IO的特点

因为有缓冲机制, 减少了系统调用的次数,进而缩小的系统开销。

4.1.2 有缓冲机制

4.1.2.1 如何验证缓冲区的存在

使用_exit()函数,结束程序,不会清空IO的缓冲区

4.1.2.2缓冲区存在,但是有几种类型

全缓冲:其余的默认全缓冲
行缓冲:stdin、stdout
无缓冲:stderr
注意:每个进程都有自己的stdin、stdout、stderr

4.1.2.3如何刷新缓冲区

  1. 缓冲区满时,自动刷新缓冲区

  1. 调用exit()函数程序退出

  1. 函数return的时候

  1. 调用fclose

  1. 调用fflush

  1. 对于行缓冲,遇到'\n'会刷新

4.1.2.4缓冲区的大小是多少,能不能该,如何改

  1. 行缓冲:1024Bytes
  1. 全缓冲的大小:4096Bytes
标准IO缓冲区的大小是可以修改的,通过setvbuf可以修改

5. 标准IO如何操作文件的

标准IO通过一个文件流指针的对象来操作文件

文件流:file stream/stdio stream

标准IO提供了三种较为特殊的文件流:

stdin:标准输入流(终端输入)

stdout:标准输出流(终端输出)

stderr:标准错误(程序运行的错误信息)

5.1 如何获取文件流指针

我们使用fopen函数来获取文件流指针

5.2 如何释放文件流指针

我们使用fclose函数来释放文件流指针

5.3 如何操作文件流指针

——》从文件流中读取数据

fread、fputs

——》往文件流中写入数据

fwrite、fgets

——》文件流指针的重定位

fseek、ftell

5.4 实现一个cat命令

./mycat file

  1. 通过main函数传参,获取想要读取的文件名

int main(int argc,char *argv[])

{

return 0;

}

这样写有个功能:通过执行指令可以传递参数,参数用空格隔开。
参数的类型都是字符串类型

例如:./calc 1+3 =

指令 参数1 参数2 参数3 参数 4

“./calc” "1" "+" "#" "="

argc:int,说的是命令行参数的个数,包括指令在内。

char *argv【】:

argv:是一个数组,数组的每个元素都是char *类型的。

数组中存储的是每一个参数的首地址。

argv[0]:指令

argv[1]:参数1

char *:

  1. char类型变量的指针

char b = 15;

char *p = &b;

2. char类型数组的首地址

char arr【】 = {1,2,3,4,5};

char *p = arr;

int i = 0;

for(i = 0;i< sizeof(arr)/sizeof(arr[0]);i++)

{

printf("%hhd",p[i]);

}

注意:要给指针一个操作范围。

3. 字符串的首地址

char str[] = "mytest";

char *p = str;

printf("%s\n",p);

2. 打开

3. 读并输出

4. 关闭

5.5获取本地时间,并将本地时间写入一个指定的文件

  1. main函数传参获取文件名

  1. 打开文件(w)

  1. 获取本地时间

time(time_t *ptim):获取1970:1:1:0:0:0到现在的秒数

char *pstr = ctime(ptim);

  1. fwrite

  1. fclose

5.6获取指定文件的大小

  1. 以只读形式打开指定文件

  1. fseek跳转到文件末尾

  1. ftell获取文件流指针的偏移量

  1. fclose

函数接口:

/*需要包含的头文件*/

#include <unistd.h>

/*

*函数名:_exit

*函数功能:退出/终止正在执行的进程,不会flush标准的流(不会清空标准IO的缓冲区)

*函数参数:

*int status:表示退出的状态

*函数返回值:void

*/

void _exit(int status);

fopen函数:

/*需要包含的头文件*/

#include <stdio.h>

/*

*函数名:fopen

*函数功能:打开一个文件,获取文件的文件流指针

*函数参数:

* const char *pathname:带路径的文件名

* const char *mode:打开方式

*函数返回值:FILE *:成功返回文件流指针,失败返回NULL。

*/

FILE *fopen(const char *pathname,const char *mode);

fclose函数:

/*需要包含的头文件*/

#include <stdio.h>

/*

*函数名:fclose

*函数功能:关闭文件描述符,并刷新缓冲区

*函数参数:FILE *stream:文件流指针

*函数返回值:int:成功0,失败返回EOF(-1)和错误码

*/

int fclose(FILE *stream);

fread函数

/*需要包含的头文件*/

#include <stdio.h>

/*

*函数名:fread

*函数功能:从文件流指针中读取数据

*函数参数:

*void *ptr:存储读取来的数据的内存首地址

*size_t size:每一个读取单元的大小,单位是字节

*size_t nmemb:想要读取的单元个数

*FILE *stream:文件流指针

*函数返回值:size_t:成功返回读到的单元个数,失败返回short nmemb count,或者是0

*/

size_t fread(void *ptr,size_t size,size_t nmemb, FILE *stream);

注意:想要读取的字节数:nmemb *size,当size == 1时,表示nmemb就是想要读取的字节数

/*

函数名:fwrite

*函数功能:往文件流指针中写入数据

*函数参数:

*void *ptr:存储想要写入的数据的内存首地址

*size_t size:每一个写入单元的大小,单位是字节

*size_t nmemb:想要写入的单元个数

*FILE *stream:文件流指针

*函数返回值:size_t:成功返回写入的单元个数,失败返回short nmemb count,或者是0

*/

size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream);

time函数:

/*需要包含的头文件*/

#include <time.h>

/*

*函数名:time

*函数功能:获取时间单位为秒(即从1970-01-01 00:00:00 +0000(UTC)到现在的秒数)

*函数参数:time_t *tloc:可以给NULL,可以给一个变量的指针。

*给NULL,不影响什么

*给了一个time_t变量的地址,会将获取来的秒数存入该变量。

*函数返回值:time_t:成功返回秒数,失败返回(time_t)-1。

*/

time_t time(time_t *tloc);

ctime函数:

/*需要包含的头文件*/

#include <time.h>

/*

*函数名:ctime

*函数功能:将秒数转换为字符串类型的时间

*函数参数:const time_t *timep:指向time_t类型的指针

*函数返回值:char *:字符串首地址,失败返回NULL

*/

char *ctime(const time_t *timep);

fseek函数:

/*需要包含的头文件*/

#include <stdio.h>

/*

*函数名:fseek

*函数功能:修改文件流指针指向的位置

*函数的参数:

*FILE *stream:文件流指针

*long offset:偏移量

*int whence:基准

*函数返回值:int:成功返回0,失败返回-1

*/

int fseek(FILE *stream,long offset,int whence);

ftell函数:

/*需要包含的头文件*/

#include <stdio.h>

/*

*函数名:ftell

*函数功能:获取当前文件流指针的位置

*函数参数:

*FILE *stream:文件流指针

*函数返回值:long:成功返回偏移量(当前位置,到文件开头的偏移量),失败返回-1

*/

long ftell(FILE *stream);

day2


  1. 文件IO


文件IO是类UNIX操作系统提供的POSIX系统调用。

文件IO的操作对象:文件描述符

文件描述符:是一个≥0的整数,可以通过这个数来操作文件

0:stdin

1:stdout

2:stderr


3.1针对文件描述符展开的操作

——》如何获取文件描述符

open

creat(不用)

——》如何关闭文件描述符

close

——》如何从文件描述符中读取数据

read

——》如何往文件描述符中写入数据

write

——文件读写位置重定位

lseek


3.2文件权限掩码

mask:为文件权限掩码,进程可以通过umask函数修改文件权限掩码。默认文件权限掩码为0002

creat(),open(),mkdir()都会用到mask,即创建文件,文件夹时都有文件权限掩码。

文件权限掩码的使用:

最终的文件权限= ~文件权限掩码&文件权限(mode)

例如:mode:777

最终得到的权限:0777& ~0002

111 111 111 777

000 000 010 002

111 111 101 775(~002)


4. 库


4.1 如何实现代码复用

封装函数接口(API)就能够实现代码复用,但是,此时还需要源文件的拷贝,然后联编。

4.2 如何实现代码复用,且不公开源代码

库可以实现不公开源代码,但是能复用代码。

4.3如何实现代码复用和拼接

没有库的情况下,是可以使用目标文件来实现代码共享的。

4.4 代码重定向

代码的拼接就是代码重定向

4.5可重定向文件(ELF文件)

链接的过程就是做代码拼接

目标文件

可执行文件

4.6gcc编译流程中的链接

——》预处理
处理预处理指令的
#include
#ifndef
#define
#ifdef
#if
#endif
#else
#elif

条件编译:

#ifdef:宏名
代码段

#endif

编译的时候:gcc xxx.c -D宏名

屏蔽代码:

#if 0
被屏蔽的代码
#endif

——》编译

将.c/.i文件转为汇编命令

——》汇编

.c/.i/.s 汇编为二进制指令(生成的文件.o目标文件)

——》链接

链接目标文件或者库
gcc xxx.c/.o -l库名 -L库的路径

4.7 什么是库

库,是编译好的,不可执行的,二进制代码,专门用来拼接的代码。

4.8库的分类

我们根据库的链接阶段不同分为静态库和动态库。

链接阶段:

编译时链接——》静态链接——》使用静态库

运行时链接——》动态链接——》动态库

静态库:是编译时链接的库,一般在Linux下,以“.a”为文件后缀
动态库:也叫共享库,是运行时链接的库,一般在Linux下,以".so"为文件后缀

在windows下,静态库以".lib"为后缀,动态库以“.dll”为后缀。

4.9静态库和动态库的区别

4.10 库的制作

4.10.1 Linux下创建与使用静态库

Linux静态库命名规则

Linux静态库命名规范,必须是“lib[you library name].a”:lib为前缀,中间是静态库的名字,扩展名为.a。

创建静态库(.a)

通过上面的流程可以知道,Linux创建静态库过程如下:

  1. 首先将代码文件编译成目标文件.o(MYSORT.o)

gcc -c mysort.c MYSORT.o(注意带参数-c,否则直接编译为可执行文件)

  1. 通过ar工具将目标文件打包成.a静态库文件

ar -crv libmysort.a MYSORT.o

生成静态库libmysort.a

4.10.2 Linux下创建与使用动态库

创建动态库(.so)

1. 编写代码

2. 将代表生成目标文件,此时要加上编译器选项-fPIC

-fPIC创建与地址无关的编译程序(pic, position independent code),

是为了能够在多个应用程序间共享。

gcc -fPIC -c mysort.c

3. 然后生成动态库,此时要加链接器选项 -shared

gcc -shared -o libmysort.so mysort.o

-shared指定生成动态链接库。

注意:上述两个过程也可以合二为一

gcc -shared -fPIC -c mysort.c -o libmysort.so

4.11 库的链接

gcc xxx.c -L+库所在的路径 -l库名(不要前面的lib和后面的.so)

静态库的搜索路径(-L选项),指定静态库名(不需要lib前缀和.a后缀,-l选项)。

-L:表示要链接库所在目录

-l:制定链接时需要的库,编译器查找链接时有隐含的命名规则,即在给出名字前面加上lib,后面加上.a或.so来确定库的名称。


  1. 文件夹操作


  1. 创建文件夹

#include <sys/stat.h>

#include <sys/types.h>

int mkdir(const char *pathname,mode_t mode);

  1. 删除空文件夹

#include <unistd.h>

int rmdir(const char *pathname);

  1. 获取文件夹的信息

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

struct dirent

{

ino_t d_ino; /*Inode number*/

off_t d_off /*Not an offset;see below*/

unsigned short d_reclen; /*Length of this recor*/

unsigned char d_type; /*Type of file; not supported

by all filesystem types */

char d_name[256]; /*Null-terminated filenamed*/

};

d_type:8种类型,见manual手册

  1. 打开文件夹

#include <sys/types.h>

#include <dirent.h>

DIR *opendir(const char *name);

  1. 关闭文件夹

#include <sys/types.h>

#include <dirent.h>

int closedir(DIR *dirp);


文件夹操作案例


#include <stdio.h>

#include <dirent.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

int readFileList(char *path);

int main(int argc, char *argv[])

{

readFileList(argv[1];

return 0;

}

int readFileList(char *path)

{

if(NULL == path)

{

puts("this path is no exist");

return -1;

}

DIR *dir;

struct dirent *ptr;

char base[1000];

dir = opendir(path);

if(NULL == dir)

{

puts("opendir error.");

return -2;

}

while((ptr = readdir(dir)) != NULL)

{


函数接口


feof函数:

/*需要包含的头文件*/

*函数名:feof

*函数功能:检测文件流指针是否指向文件末尾

*函数参数:FILE *stream:文件流指针

*函数返回值:int:如果到文件末尾返回值非0(真),没到,返回0(假)

int feof(FILE *stream);

fflush函数:

/*需要包含的头文件*/

*函数名:fflush

*函数功能:刷新文件流指针的缓冲区

*函数参数:FILE *stream:文件流指针,如果为NULL,清空所有打开的文件流指针的缓冲区

*函数返回值:int:成功返回0,失败返回EOF(-1)和错误码。

int fflush(FILE *stream);

open函数:

/*需要包含的头文件*/

#Include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

/*

*函数名:open

*函数功能:打开文件(文件存在的情况下)

*函数参数:

  • const char *pathname:带路径的文件名

  • int flags:打开的方式

*函数返回值:int:成功返回打开的文件的文件描述符,失败返回-1和错误码

*/

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

/*

*函数名:open

*函数功能:打开文件(文件不存在的情况下可以创建)

*函数参数:

  • const char *pathname:带路径的文件名

  • int flags:打开的方式

  • mode_t mode:文件权限

*函数返回值:int:成功返回打开的文件的文件描述符,失败返回-1和错误码

*/

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

flags:是一组flag

O_RDONLY:只读

O_WRONLY:只写

O_RDWR:可读可写

O_CREAT:文件不存在创建

O_TRUNCC:截断

O_NOCTTY:摆脱终端,Linux下串口通信

O_APPEND:追加

O_NONBLOCK or O_NDELAY:非阻塞

mode_t: 8进制的文件权限

例如:0777 0664

close函数:

/*需要包含的头文件*/

#include <unistd.h>

/*

*函数名:close

*函数功能:关闭文件描述符

*函数参数:int fd :文件描述符

*函数返回值:int:成功返回0,失败返回-1和错误码

*/

int close(int fd);

read函数:

/*需要包含的头文件*/

#include <unistd.h>

/*

*函数名:read

*函数功能:从文件描述符中读取数据

*函数参数:

*int fd:文件描述符

*void *buf:存放读到数据的内存首地址

*size_t count:想要读取的字节数

*函数返回值:int:成功返回读到的字节数,失败返回-1和错误码,当返回值为0,表示读到文件末尾

*/

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

write函数:

/*需要包含的头文件*/

#include <unistd.h>

/*

*函数名:write

*函数功能:往文件描述符中写入数据

*函数参数:

*int fd:文件描述符

*void *buf:存放想要写入数据的内存首地址

*size_t count:想要写入的字节数

*函数返回值:int:成功返回写入的字节数,失败返回-1和错误码

*/

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

lseek函数:

/*需要包含的头文件*/

#include <sys/types.h>

#include <unistd.h>

/*

*函数名:lseek

*函数功能:文件读写指针重定位

*函数参数:

*int fd:文件描述符

*off_t offset:相对于基准的偏移量

*int whence:基准(SEEK_SET,SEEK_CUR,SEEK_END)

*函数返回值:int:成功返回文件头移动后位置的偏移量,失败返回-1和错误码

*/

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值