IO:
标准io(std)(5)
文件io(sys)(3)
高级io(adv)(14)(阻塞的,非阻塞的)
并发:
多进程+信号(10,8)
多线程(11,12)
IPC:
PIPE管道(15)
XSI:msg,sem,shm(15)
SOCKET(套接字跨网络的,不在一台主机上的):dgram,stream(16)
杂:4,6,7,9,13
IO:inpute & output 是一切实现的基础
stdio=>标准io 贯穿始终的类型:FILE* <- fopen()
常见标准IO函数
fopen
fclose
fread
fwrite
fseek
fflush
强制把IO缓存区的数据写入到页缓存区
文件IO五大模式
- 阻塞模式
- 非阻塞模式
- IO多路复用
- 异步IO
- 信号驱动IO
标准IO移植性比较好,可以在不同内核下的平台上使用
标准化函数依赖于系统调用
描述:使用给定的模式 mode 打开 pathname 所指向的文件(打开文件)
头文件:
#include
函数原型:
FILE *fopen(const char *pathname, const char *mode);
//FILE *fdopen(int fd, const char *mode);
//FILE *freopen(const char *pathname, const char *mode, FILE *stream);
用法:
FILE *fp;
fp = fopen("tmp.log","w");
fp = fopen("/home/user/tmp.log","w");
模式:
参数 | 含义 |
---|---|
r | 以只读形式打开文件,流被被定位到文件起始位置 |
r+ | 以读和写形式打开文件,流被定位到文件起始位置 |
w | 清空文件或创建文件去写,流被定位到文件起始位置 |
w+ | 以读和写的形式打开文件,文件有则清空,无则创建,流被定位到文件起始位置 |
a | 以写入文件末尾的方式打开文件,流被定位到文件末尾位置 |
a+ | 以读的方式,流被定位到文件起始位置,以写的方式,流被定位到文件末尾位置 |
注意:
r/r+:要读的文件必须是已经存在的
以传入的字符串开头为模式,不管字符串后边是什么
例:"rwa+" => 以r开头 => 只读 "read" => 以r开头 => 只读
文件末尾的位置,指针指向的是文件中最后一个元素下一个位置的地址空间
返回值:
成功:返回FILE 指针
失败:空指针
返回值为指针类型:存放在堆或静态区上,不存放在栈上
注意:
不可能存放在栈上,栈存放局部变量,局部变量的生命周期只在函数内有效
存放在静态区时要在一个操作完全执行完再进行另一个操作,否则会覆盖,丢失数据
存放在堆上会有内存的申请和释放
描述:通过系统调用设置的,在错误事件中的某些库函数表明了什么发生了错误,涵盖了系统所有可能的出错情况
头文件:
#include
存放路径:
/usr/include/asm-generic/errno.h
/usr/include/asm-generic/errno-base.h
描述:把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 s,后跟一个冒号,然后是一个空格。只会输出最近一次的错误提示。
头文件:
#include
#include
函数原型:
void perror(const char *s);
用法:
perror("fopen()");
描述:打印errno的错误号所描述的内容
头文件:
#include
函数原型:
char *strerror(int errnum);
用法:
printf("fopen():%s\n",strerror(errno));
参数 | 含义 |
---|---|
-a | 显示全部命令 |
-n | 更改打开文件最大数量 |
open file (-n) 1024
ulimit -n 2000
open file (-n) 2000
拓展
用man手册查看:setlimit,setrlimit
在系统创建进程中,建进程时的三个默认的文件描述符(stdin(标准输入) stdout(标准输出) stderr(标准出错)) Linux进程默认情况下会有3个缺省打开的文件描述符
三个进程都是FILE *类型。
描述:关闭文件
头文件:
#include
函数原型:
int fclose(FILE *stream);
用法:
fclose(fp);
返回值:
成功:返回0
失败:返回EOF(系统宏定义为-1)
#include
#include
#include
#include
int main()
{
FILE *fp;
fp = fopen("tmp.log","w");
// fp = fopen("/etc/shadow","r");
if(fp == NULL)
{
// printf("fopen failed.errno = %d\n",errno);
// perror("fopen()");
printf("fopen():%s\n",strerror(errno));
exit(1);
}
puts("OK!");
fclose(fp);
exit(0);
}
文件的位置指针像眼睛一样,顺次向后操作,读完这个位置,移动到下个位置
描述:从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动 (从文件读一个字符)
头文件:
#include
函数原型:
int fgetc(FILE *stream);
返回值:
成功:该函数以无符号 char 强制转换为 int 的形式返回读取的字符
失败:返回EOF(系统宏定义为-1)
描述:把一个指定的字符,输出到目标文件中去 (往文件写一个字符)
头文件:
#include
函数原型:
int fputc(int c, FILE *stream);
返回值:
成功:返回非负数
失败:如果到达文件末尾或发生读错误,则返回 EOF(-1)。
将一个文件内容拷贝到另一个文件中去
功能:由终端输入待操作的两个文件,实现以单个字符为单位进行文件拷贝
#include
#include
#include
#include
int main(int argc,char *argv[])
{
FILE *fps,*fpd;
int ch;
if(argc < 3)
{
fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
exit(1);
}
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps);
perror("fopen()");
exit(1);
}
while((ch = fgetc(fps)) != EOF)
fputc(ch,fpd);
fclose(fpd);
fclose(fps);
exit(0);
}
描述:可以来自打印机…不只是来自于键盘输入
头文件:
#include
函数原型:
int fscanf(FILE *stream, const char *format, ...);
返回值:
成功:该函数返回成功匹配和赋值的个数
失败:如果到达文件末尾或发生读错误,则返回 EOF
描述:发送格式化输出到流 stream 中。
头文件:
#include
函数原型:
int fprintf(FILE *stream, const char *format, ...)
返回值:
成功:如果成功,则返回写入的字符总数
失败:返回一个负数
描述:从指定的文件(流 stream )读取一行,并把它存储在 s 所指向的字符串内
头文件:
#include
函数原型:
char *fgets(char *s, int size, FILE *stream);
fgets函数有两种情况可以导致其读取行为正常结束
1.读到了size-1个字节
2.读到了’\n’
返回值:
成功:返回空间的起始位置
失败:返回空指针
描述:给我一个字符串的起始位置,把字符串写入到指定的流 stream 中,但不包括空字符。
头文件:
#include
函数原型:
int fputs(const char *s, FILE *stream);
返回值:
成功:返回一个非负值
失败:返回EOF(系统宏定义为-1)
#include
#include
#include
#include
/*
fgets函数有两种情况可以导致其读取行为正常结束:
1读到了size-1个字节
2读到了'\n'
*/
#define BUFSIZE 5
int main()
{
FILE *fp;
char buf[BUFSIZE];
fp = fopen("tmp.log","r");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
fgets(buf,BUFSIZE,fp);
puts(buf);
fgets(buf,BUFSIZE,fp);
puts(buf);
fclose(fp);
exit(0);
}
将一个文件内容拷贝到另一个文件中去
功能:由终端输入待操作的两个文件,实现以字符串为单位进行文件拷贝
#include
#include
#include
#include
#define BUFSIZE 128
int main(int argc,char *argv[])
{
FILE *fps,*fpd;
char buf[BUFSIZE];
if(argc < 3)
{
fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
exit(1);
}
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps);
perror("fopen()");
exit(1);
}
while(fgets(buf,BUFSIZE,fps) != NULL)
fputs(buf,fpd);
fclose(fpd);
fclose(fps);
exit(0);
}
头文件:
#include
描述:从指定的文件流stream中取数据,取nmemb个对象,一个对象size大小,读到指针所指向的ptr数组中去
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
描述:从指针ptr所指向的数组中取数据,一个对象size个大小,取nmemb个对象,写到文件指针(流stream)所指向的文件中去
函数原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
返回值:
== 成功读入或写出的对象个数==
注意:
fread(buf,1,32,fp);//推荐
数据量充足 = 32
文件中只有5个字节 = 5
fread(buf,32,1,fp);//不推荐
数据量充足 = 1
文件中只有5个字节 = 0
将一个文件内容拷贝到另一个文件中去
功能:由终端输入待操作的两个文件,实现以单字节为单位进行文件拷贝
#include
#include
#include
#include
#define BUFSIZE 1024
int main(int argc,char *argv[])
{
FILE *fps,*fpd;
char buf[BUFSIZE];
int n;
if(argc < 3)
{
fprintf(stderr,"Usage:%s srcfile destfile\n",argv[0]);
exit(1);
}
fps = fopen(argv[1],"r");
if(fps == NULL)
{
perror("fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if(fpd == NULL)
{
fclose(fps);
perror("fopen()");
exit(1);
}
while((n = fread(buf,1,BUFSIZE,fps)) > 0)
fwrite(buf,1,n,fpd);
fclose(fpd);
fclose(fps);
exit(0);
}
头文件:
#include
描述:设置文件指针位置偏移offset个字节
函数原型:
int fseek(FILE *stream, long offset, int whence);
常量 | 描述 |
---|---|
SEEK_SET | 文件的开头 |
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件的末尾 |
返回值:
成功:0
失败:-1,并且设置errno
描述:设置文件位置指针到文件开始处
描述:反馈文件位置指针在哪,以字节为单位开始数
函数原型:
long ftell(FILE *stream);
返回值:
成功:该函数返回位置标识符的当前值(整型数)
失败:则返回 -1L,全局变量 errno 被设置为一个正值。
功能:文件位置指针
#include
#include
#include
#include
int main()
{
FILE *fp;
fp = fopen("tmp.log","r+");
if(fp == NULL)
{
printf("fopen():%s\n",strerror(errno));
exit(1);
}
fseek(fp,4,SEEK_SET);
fputc('X',fp);
fclose(fp);
exit(0);
}
描述:刷新流 stream 的输出缓冲区
头文件:
#include
函数原型:
int fflush(FILE *stream);
返回值:
成功:0
失败:EOF
缓冲区:暂存空间,大多数情况下,缓冲区的存在是件好事,作用合并系统调用
行缓冲:stdout,换行时,满了时,强制刷新
全缓冲:默认,(只要不是终端设备,全采用全缓冲模式)满了时,强制刷新
无缓冲:stderr,需要立即输出
缓冲区的作用及分类
功能:五秒之后一口气输出 xxxxx
#include
#include
#include
#include
#include
/*
缓冲区:暂存空间,大多数情况下,缓冲区的存在是件好事,作用合并系统调用
行缓冲:stdout,换行时,满了时,强制刷新
全缓冲:默认,(只要不是终端设备,全采用全缓冲模式)满了时,强制刷新
无缓冲:stderr,需要立即输出
*/
int main()
{
int i;
for(i = 0 ; i < 5; i++)
{
putchar('x');
sleep(1);
}
putchar('\n');
/*
int i= 1;
printf("Befor while()");
fflush(stdout);
while(1);
printf("After while()");
*/
exit(0);
}
描述:用于读取一行 字符直到换行符,包括换行符(如果此行内容超出给定长度,自动重新申请内存)
手册:man getline
头文件:
#include
函数原型:
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
lineptr – 一块可写的数组起始位置
n – 用来回调新申请后的空间大小
stream – 文件流
返回值:
成功:读到的字节个数
失败:-1,设置errno
代码演示:
功能:以行为单位,读取给定文件,并输出文件中每行的字符个数
#include
#include
#include
#include
ssize_t mygetline(char **lineptr, size_t *n, FILE *stream)
{
char *buf = *lineptr;
char *cur;
if(buf == NULL && *n == 0)
{
*n = 120;
buf = malloc(*n);
}
while(1)
{
*lineptr = fgets(buf,*n,stream);
if(*lineptr == NULL)
return EOF;
if(*n-1 == strlen(buf))
{
*n = *n+120;
buf = realloc(buf,*n);
fseek(stream,-strlen(buf),SEEK_CUR);
}
else
break;
}
}
int main(int argc,char *argv[])
{
FILE *fp;
char *linebuf = NULL;
size_t linesize = 0;
if(argc < 2)
{
fprintf(stderr,"Usage:%s file\n",argv[0]);
exit(1);
}
fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
while(1)
{
if(getline(&linebuf, &linesize, fp) < 0)
break;
printf("linesize = %ld\n",linesize);
printf("strlen(linebuf) = %ld\n",strlen(linebuf));
printf("linebuf = %s\n",linebuf);
}
fclose(fp);
exit(0);
}
sysio=>文件io,系统调用io
文件描述符(fd)使用策略:优先使用当前可用范围内最小的fd
Linux中fd为什么返回值为3(从3开始存)呢??
答:在系统创建进程中,建进程时的三个默认的文件描述符 Linux进程默认情况下会有3个缺省打开的文件描述符…所以会返回3.
头文件:
#include
#include
#include
函数原型:
当文件存在时
int open(const char* pathname,int flags);
//文件中有O_CREAT时
int open(const char *pathname, int flags, mode_t mode);
当文件不存在时
int open (const char* pathname,int flags,int perms)
参数:
┌─────────────┬───────────────────────────────┐
│fopen() mode │ open() flags │
├─────────────┼───────────────────────────────┤
│ r │ O_RDONLY │
├─────────────┼───────────────────────────────┤
│ w │ O_RDONLY | O_CREAT | O_TRUNC │
├─────────────┼───────────────────────────────┤
│ a │ O_WRONLY | O_CREAT | O_APPEND │
├─────────────┼───────────────────────────────┤
│ r+ │ O_RDWR │
├─────────────┼───────────────────────────────┤
│ w+ │ O_RDWR | O_CREAT | O_TRUNC │
├─────────────┼───────────────────────────────┤
│ a+ │ O_RDWR | O_CREAT | O_APPEND │
└─────────────┴───────────────────────────────┘
flags | 含义 |
---|---|
O_RDONLY | 只读 |
O_WRONLY | 只写 |
O_RDWR | 可读可写 |
O_CREAT | 无则创建 |
O_TRUNC | 有则清空 |
O_APPEND | 追加末尾 |
O_NONBLOCK | 非阻塞方式打开(默认阻塞方式) |
O_NOFOLLOW | 打开符号链接本身(默认打开真实文件) |
返回值:
成功:文件描述符
失败:-1
头文件:
#include
函数原型:
int close(int fd)
返回值:
成功:0
失败:-1
描述:从fd所指向的文件中读count个字节到buff缓存区
头文件:
#include
函数原型:
ssize_t read(int fd,void *buff,size_t count)
返回值:
成功:
失败:
描述:从buff缓存区读取count个字节写入到fd文件操作符所指向的文件中
头文件:
同read函数
函数原型:
ssize_t write(int fd,void *buff,size_t count)
返回值:
成功:
失败:
1、打开要复制的文件
fd1 = open(argv[1],O_RDONLY);
2、创建新的文件
fd2 = open(argv[2],O_WRONLY|O_CREAT,0666);
3、把源文件内容读到缓冲区,把缓冲区内容写入新文件
4、循坏执行第三步,直到读取的字节数量为0,退出循坏。
while(1)
{
read_size = read(fd1,buf,512);
if(read_size == 0)
break;
write(fd2,buf,read_size);
}
5、关闭打开的文件
close(fd1);
close(fd2);
代码内容
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 1024
int main(int argc,char *argv[])
{
int sfd,dfd;
char buf[BUFSIZE];
ssize_t len,ret,pos;
if(argc < 3)
{
fprintf(stderr,"Usage:%s srcfile destfile\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)
{
perror("fopen()");
exit(1);
}
while(1)
{
len = read(sfd,buf,BUFSIZE);
if(len == 0)
break;
if(len < 0)
{
perror("read()");
exit(1);
}
//len > 0
pos = 0;
while(len > 0)
{
ret = write(dfd,buf+pos,len);
if(ret < 0)
{
perror("write()");
exit(1);
}
pos += ret;
len -= ret;
}
}
close(dfd);
close(sfd);
exit(0);
}
}
描述:设置文件读写位置
头文件:
#include
#include
函数原型:
off_t lseek(int fd, off_t offset, int whence);
常量 | 描述 |
---|---|
SEEK_SET | 文件的开头 |
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件的末尾 |
返回值:
成功:该函数返回位置标识符的当前值(整型数)
失败:-1,并且设置errno
代码演示:
功能:加深缓冲区的理解,强调在同一个程序中对同一个文件尽量不要混用sysio和stdio
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
exit(0);
}
描述:用于将一个文件描述符 oldfd 复制到另一个 newfd
头文件:
#include
函数原型:
int dup(int oldfd);
int dup2(int oldfd, int newfd);
oldfd – 要被复制的文件描述符
newfd – 复制的文件描述符(先关闭newfd文件描述符本身,再将oldfd复制给newfd)
原子操作:一条语句执行时一气呵成,中间不会被分割打断干扰
返回值:
如果oldfd与newfd两个文件描述符数值相同,返回newfd。
示例代码:
功能:文件描述符的复制(原子操作)
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
fd = open("/tmp/out",O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd < 0)
{
perror("open()");
exit(1);
}
// close(1);
// dup(fd);
dup2(fd,1);
if(fd != 1)
close(fd);
//---------------------------------------------
puts("Hello!");
exit(0);
}
UNIX环境高级编程 APUE
UNIX网络编程(2卷) UNP
“圣经(易学难用)”
网络:网络基础,UNP,TCP/IP详解(3)
阅读UNIX环境高级编程章节 5.4,5.8,5.13 , 3.11
面试:三种不同方法获取文件不同长度
- while fgetc()
- fseek(END),ftell()
- stat
标准IO与系统调用IO区别
标准IO:移植性好
系统调用IO:没有缓存区
面试:如何使程序运行速度变快
1.响应速度变快 :多用系统调用IO,吞吐量低
2.吞吐量大:标准IO,提高程序吞吐量,响应速度较慢(一半以上几率追求吞吐量)