内容来源:李慧琴老师视频课程笔记整理 + 《Unix高级系统编程》读书笔记(记录记录自己不太清楚的内容)
宗旨:一切最终不以代码展示呈现的方式都是耍流氓
3~16除9章
建议:在Linux中使用普通用户.
CFLAGS+=-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall
标准I/O:
Windows:
二进制流:换行符 《----》 ‘\n’
文本流:换行符 《----》‘\r’''\n'
Linux
换行符: 《----》 '\n'
3,5,14
intput&output是一切实现的基础
—读写一个字符(返回EOF)
按字符输入:fgetc() ,getc(),getchar()
按字符输出:fputc() ,putc(),putchar()
—读写一行(返回NULL)
按行输出:puts(),fputs()
按行输入:gets() ,fgets() 到文件末尾返回NULL,遇到’\n’或者输入size-1个字符返回,总是包含’\n’
----读写若干对象:
fread(),fwrite()
fgetc读写效率低,fgets:可以使用buf[strlen(buf)-1]来判断读写一行
typedef struct iobuf{
int cnt; /*剩余的字节数*/
char *ptr; /*下一个字符的位置*/
char *base; /*缓冲区的位置*/
int flag; /*文件访问模式*/
int fd; /*文件描述符*/
}FILE;
FILE *fopen(const char *path, const char *mode);
mode:
r 打开文本文件,用于读。流被定位于文件的开始(第一个有效字节)。
r+ 打开文本文件,用于读写。流被定位于文件的开始。
----r必须存在
w 将文件长度截断为零,或者创建文本文件,用于写。流被定位于文件的开始。
w+ 打开文件,用于读写。如果文件不存在就创建它,否则将截断它。流被定位于文件的开始。
a 打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。流被定位于文件的末尾。
a+ 打开文件,用于追加
(在文件尾写)。如果文件不存在就创建它。读文件的初始位置是文件的开始,但是输出总是被追加到文件的末尾。
注意区分最后一个字节(当前读的位置)和文件尾写(最后一个有效字节的下一个字符)
加b和不加b:Linux只有流的概念,Windows下有流和文本的概念
\\error:返回NULL
注:errno 是记录系统的最后一次错误代码
Linux中error的各种宏所在的位置/usr/include/asm-generic
作用
errno 是 error number 的缩写,意味系统调用错误码。
如果系统调用返回成功,errno 有可能但不一定会置0;而系统调用出错时,errno 必定会被设为对应的错误编号。因此,强迫症患者可以在调用系统调用之前,手动将 errno 置0。不过,如果系统调用返回成功,谁会闲着没事去看 errno 呢?
相关函数
一般来说,我们是不是需要了解每个 errno 的编号是什么意思的。我们会使用两个C库函数帮我们翻译 errno 的意思就可以了。
他们分别是:
#include
char *strerror(int errnum); //将错误码以字符串的信息显示出来
#include
void perror(const char *msg); //打印系统错误信息
perror 封装了错误码errno,将error表示的错误信息重定向到stderr上,其输出格式为:message:errno表示的错误信息.'
多线程
在没有多线程以前,errno 其实就是一个全局变量。这是很好理解的,不过已经是过去式了。
因为每个线程都有可能会调用系统调用,那么后来的错误,就会把前面的错误覆盖掉。
如果我们不能保证 errno 的可靠性,那 errno 还有什么意义?
所以,它变了。
在多线程编程时,需要包含头文件 #include <errno.h>
errno 不再是一个全局变量了,现在它已经变成了一个宏
虽然 errno 变了,但它依然是一个左值(左值就是可以写成 “左值 = 右值” 的形式)。
extern int *__errno_location(void);
#define errno (*__errno_location())
首先,声明了来自外部的函数 __errno_location();
注意哦,这个函数的返回值是一个函数指针,这个指针指向线程自己的 errno 的位置
,通过对这个指针解引用,就可以访问线程唯一的 errno。
所以,errno 被定义为 (*__errno_location())。
原文链接:https://blog.csdn.net/tissar/article/details/87996113
errno的演示案例:-----主要演示:perror(char *),strerror(int ):
#include
#include
#include
#include
int main()
{
FILE *fp;
fp = fopen("temp","r");
if(fp == NULL)
{
fprintf(stdout,"fsiled info = %s\n",strerror(errno));
fprintf(stdout,"——————————————————————————————");
perror("fopen(): -------------------"); //
fprintf(stdout,"——————————————————————————————");
fprintf(stdout,"fsiled info = %s\n",strerror(errno));
fprintf(stderr,"fopen() failed! errno=%d\n",errno);
fprintf(stdout,"fsiled info = %s\n",strerror(errno));
exit(1);
}
puts("ok");
exit(0);
}
/*
fsiled info = No such file or directory
__________________________
fopen():---------- : No such file or directory
__________________________
fopen() failed! errno=2
fsiled info = No such file or directory
*/
fopen()返回的是FILE*:位于堆上
4. fclose()
fclose(fp);
最多打开文件的数目案例
#include
#include
#include
#include
int main()
{
FILE *fp;
int count = 0;
while(1)
{
fp = fopen("tmp","r");
if(fp == NULL)
{
perror("fopen():---------- ");
break;
}
count++;
}
printf("max = %d\n",count);
exit(0);
}
/*
fopen():---------- : Too many open files
max = 1021
*/
分析:一个文件默认打开stdin,,stdout,stderror三个,加上1021所以打开的文件的做多个数是1024
一个命令:ulimit
:用来限制每个用户可使用的资源 ,所以一个文件可以打开的文件的个数可以修改
yangpipi@yangcentos7 io]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7800
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
int getchar(void);
getchar() is equivalent to getc(stdin).
int getc(FILE *stream);
getc() is equivalent to fgetc()
int fgetc(FILE *stream);
int putchar(int c);
putchar©; is equivalent to putc(c,stdout).int putc(int c, FILE *stream);
putc() is equivalent to fputc()int fputc(int c, FILE *stream);
#include
#include
#include
//注意argc是传入的参数的个数(包含函数名),argv[0]是函数名
int main(int argc,int ** argv)
{
FILE * fw,* fr; //小心FILE * fw,fr
int text; //不要定义为char因为char是未定义的有无符号
fr = fopen(argv[1],"r");
if(NULL == fr) //以后所有判断形式参照括号,这个把我搞残了
{
perror("fopen(r)");
exit(1);
}
fw = fopen(argv[2],"w");
if(NULL == fw)
{
perror("fopen(w)");
exit(1);
}
while(1)
{
text = fgetc(fr);
if(text == EOF)
{
printf("copy finish!\n");
break;
}
fputc(text,fw);
}
fclose(fr);
fclose(fw);
exit(0);
}
上面的案例输出警告:
mycopy.c: 在函数‘main’中:
mycopy.c:11:2: 警告:传递‘fopen’的第 1 个参数时在不兼容的指针类型间转换 [默认启用]
fr = fopen(argv[1],"r");
^
In file included from mycopy.c:1:0:
/usr/include/stdio.h:272:14: 附注:需要类型‘const char * __restrict__’,但实参的类型为‘int *’
extern FILE *fopen (const char *__restrict __filename,
^
mycopy.c:17:2: 警告:传递‘fopen’的第 1 个参数时在不兼容的指针类型间转换 [默认启用]
fw = fopen(argv[2],"w");
^
In file included from mycopy.c:1:0:
/usr/include/stdio.h:272:14: 附注:需要类型‘const char * __restrict__’,但实参的类型为‘int *’
extern FILE *fopen (const char *__restrict __filename,
#include
#include
#include
int main(int argc,int **argv)
{
FILE *fp = NULL;
int count = 0;
if(2>argc)
{
perror("permeter is enough!");
exit(1);
}
fp = fopen(argv[1],"r");
while(EOF!=fgetc(fp))
{
count++;
}
printf("letter is %d\n",count);
exit(0);
}
char *gets(char *s);
//这里面没有设置输入字符的个数,----不安全char *fgets(char *s, int size, FILE *stream);
abcd
#define SIZE 5
fgets()
读取上面的字符两次;
第一次:
a b c d '\0'
第二次:
'\n' '\0'
9. fputs()
int puts(const char *s);
int fputs(const char *s, FILE *stream);
10. fwrite()
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
11.fread()
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
想读的字节个数:size*nmemb
对于fread和fwrite1
建议只是用单字节操作:
原因:
1=>当要读的文件只有5个字节时
fread(buf,1,10,fp)(每次读取1个字节,读取十次)
>返回5读取5个字节
>fread(buf,10,1,fp)(每次读取10个字节,读取一次)
>返回0读取??字节
#include
#include
int main()
{
char buf[100];
sprintf(buf,"%d-%d-%d",2021,5,13);
printf("%s\n",buf);
exit(0);
}
int sprintf(char *str, const char *format, ...);
int fprintf(FILE *stream, const char *format, …);`int atoi(const char *nptr);
文件位置指针
14. void rewind(FILE *stream);
==== fseek(fp,0L,SEEK_SET)
;将文件指针放到文件首.
15. fseek()
int fseek(FILE *stream, long offset, int whence);
whenceca参数:SEEK_SET, SEEK_CUR, or SEEK_END
具体应用可实现空洞文件:一个文件的大小为4K,此时使用lseek函数将文件的头移动到地址6k处,再用write函数将文件写入,这样的导致4000-6000之间产生了空洞,因为这一部分并没有写入任何的数据,这一部分区域就称为文件的空洞,此文文件就叫做空洞文件
//使用fseek和ftell来实现文件大小的测量.
#include
#include
#include
int main(int argc,int **argv)
{
FILE *fp = NULL;
long count = 0;
if(2>argc)
{
perror("permeter is enough!");
exit(1);
}
fp = fopen(argv[1],"r");
fseek(fp,0,SEEK_END);
count = ftell(fp);
printf("letter is %ld\n",count);
exit(0);
}
函数设置文件指针stream的位置
16. ftell()
long ftell(FILE *stream);
ftell函数用来返回当前文件指针的位置。定义在stdio.h头文件中。
由于受制于long类型大小,导致字节数收到限制.,上面函数的替换:(但是上面的使用与C89和C99移植性高这只适用于POXIC标准)
int fseeko(FILE *stream, off_t offset, int whence);
off_t ftello(FILE *stream);
但是注意On many architectures both off_t
and long are 32-bit types, but compilation with
#define _FILE_OFFSET_BITS 64
will turn off_t into a 64-bit type.
每次编译应该这样:gcc a.c -o a -D _FILE_OFFSET_BITS=64
或者使用makefile文件
vim makefile
CFLAGES+=-D_FILE_OFFSET_BITS =64
注意:D选项是用来在使用gcc/g++编译的时候定义宏的
[yangpipi@yangcentos7 linux_c]$ make a
cc -D_FILE_OFFSET_BITS=64 a.c -o a
int fflush(FILE *stream);
#include
#include
int main()
{
int i;
printf("Before while()");//打印不出来;
while(1);
printf("After while()");
exit(0);
}
fflush
为NULL:刷新所有打开的流;
stdout:
要求填满整个缓存区后才进行I/O 系统调用操作。对于磁盘文件通常
使用全缓存访问。 可以调用ffresh
进行刷新缓存.
涉及一个终端时(例如标准输入和标准输出),使用行缓存。
行缓存满自动输出
碰到换行符自动输出
setvbuf
可以设置缓存类型:函数 setvbuf 可以用在任何打开的流上,改变它的缓冲。参数 mode 必须是下列三个宏之一:
int main()
{
printf("[%s:%d]:before:while()\n",__FUNCTION__,__LINE__);
//上面如果没有/n,则什么也输不出。
while(1); //放置sleep(2);也会出现这种情况。
printf("[%s:%d]:after:while()",__FUNCTION__,__LINE__);
}
printf()函数的缓冲区大小为1024个字节,当超出缓冲区大小时缓冲区就会被刷新。
1.进程结束。
2.遇到\n。
3.缓冲区满。
4.手动刷新缓冲区fflush(stdout)。
5.调用exit(0);但是还可以调用_exit(0),不刷新缓冲区。
标准错误流stderr 通常是不带缓存区的,这使得错误信息能够尽快地
显示出来。
无论声明缓存在程序执行之后都会输出
int main()
{
printf("[%s:%d]:before:while()\n",__FUNCTION__,__LINE__);
//上面如果没有/n,则什么也输不出。
while(1); //放置sleep(2);也会出现这种情况。
printf("[%s:%d]:after:while()",__FUNCTION__,__LINE__);
}
printf()函数的缓冲区大小为1024个字节,当超出缓冲区大小时缓冲区就会被刷新。
1.进程结束。
2.遇到\n。
3.缓冲区满。
4.手动刷新缓冲区fflush(stdout)。
5.调用exit(0);但是还可以调用_exit(0),不刷新缓冲区。
20.getline
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
#define _GNU_SOURCE
#include
#include
所以在makefile文件中添加:-D_GNU_SOURCE
CFLAGS+=-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
案例:
#include
#include
#include
int main(int argc,char **argv)
{
FILE *fp = NULL;
size_t linesize;
char *linebuf = NULL;
if(argc < 2)
{
fprintf(stderr,"%s:参数不够\n",argv[0]);
exit(1);
}
fp = fopen(argv[1],"r");
if(fp==NULL)
{
perror("fp is NULL");
exit(1);
}
puts("参数无误|\n");
while(1)
{ puts("进入循环\n");
if(getline(&linebuf,&linesize,fp)<0)
break;
printf("%s\n",linebuf);
printf("%d\n",linesize);
}
exit(0);
}
提示:按照上面的程序会报段错误,只要将linesize初始化为0即可
在gcc编译器中,对标准库进行了扩展,加入了一个getline函数。该函数的定义以下:
#include ssize t getline(char**lineptr,size tn,FILEstream);其中1ineptr指向一个动态分配的内存区域。机是所分配内存的长度。
假如1ineptr是NULL的话,getline函数会自动进行动态内存的分配忽略机的大小,因此使用这个函数很注意的就使用要注意自己进行内存的释放。
假如1ineptr分配了内存,但在使用过程中发觉所分配的内存不足的话,getline函数会调用realloc函数来重新进行内存的分配,同时更新】ineptr和机。
注意1ineptr指向的是一个动态分配的内存,由malloc,calloc或realloc分配的,不能是静态分配的数组。
#include
#include
#include
int main(int argc,char **argv)
{
FILE *fp = NULL;
size_t linesize;
char *linebuf = NULL;
if(argc < 2)
{
fprintf(stderr,"%s:参数不够\n",argv[0]);
exit(1);
}
fp = fopen(argv[1],"r");
if(fp==NULL)
{
perror("fp is NULL");
exit(1);
}
puts("参数无误|\n");
while(1)
{ puts("进入循环\n");
if(getline(&linebuf,&linesize,fp)<0)
break;
printf("%s\n",linebuf);
printf("%d\n",linesize);
}
//free(linebuf); 不建议这样,如果实现使用new就完了
fclose(fp);
exit(0);
}
所以自己实现getline
#include
#include
#include
// #include
// void cleanup(char** pointer) {
// free(*pointer);
// *pointer = NULL;
// }
// 自行实现getline函数
ssize_t mygetline(char** line, size_t *n, FILE *fp)
{
char *buf = *line;
//c来存储字符,i来记录字符串长度
ssize_t c =0;
ssize_t i =0;
//buf为空或n为0时动态分配空间
if(buf == NULL || *n==0)
{
*line = malloc(10);
buf = *line;
*n = 10;
}
while((c = fgetc(fp)) != '\n')
{
if(c == EOF)
return -1;
//留2个空间给'\n'和'\0'
if(i < *n - 2)
*(buf + i++) = c;
else
{
//空间不足,需要扩展空间,重新进行分配
*n = *n + 10;
buf = realloc(buf, *n);
*(buf + i++) = c;
}
}
*(buf + i++)='\n';
*(buf + i)='\0';
return i;
}
int main(int argc, char **argv)
{
// mtrace();
FILE *fp;
char *linebuf = NULL;
size_t linesize = 0;
if(argc < 2)
{
fprintf(stderr, "Usage:%s \n" , argv[0]);
exit(1);
}
fp = fopen(argv[1], "r");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
while(1)
{
if(mygetline(&linebuf, &linesize, fp) < 0) // if(getline(&linebuf, &linesize, fp) < 0)
break;
printf("%lu\n", strlen(linebuf));
printf("%lu\n", linesize);
}
fclose(fp);
free(linebuf);
exit(0);
}
EOF是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束
char *tmpnam(char *s);
FILE *tmpfile(void);
临时文件关键点如下:
1.如何保证不冲突。
2.及时销毁 :造成内存吃紧,也会导致问题1增大冲突的可能性。
功能:create a name for a temporary file创建一个临时文件
char *tmpnam(char *s);
返回值:返回一个指向临时文件的指针
缺陷:不是原子操作(产生名字,再创建文件)
建议不使用这个函数:bugs:ever use this function. Use mkstemp(3) or tmpfile(3) instead.
功能:tmpfile - create a temporary file创建一个临时文件
FILE * tmpfile(void);
description:以二进制的读写方式打开一个临时文件,当文件关闭后自动删除---匿名文件(没名字:ls命令看不到)
返回值:返回一个文件指针
引用:http://t.zoukankan.com/muzihuan-p-5262538.html
不同的系统提供的sysio不一样
每打开一个文件都会创建一个结构体,这个结构体的指针会放在一个数组中,系统会将对应的下标返回给用户即描输字 所以之前讲到的ulimit -a可以修改打开一个文件的最多个数,实质上实在修改数组的大小
每个结构体内部都有一个计数器,统计有一个指针指向他(也就是上面的数组中的几个元素指向他,所以当数组中同时有两个元素指向同一个结构体时,free掉其中的一个元素,只是将指向的结构体中的计数器减一.)
stdout,stdin,stderr
任何一种操作系统中,程序在开始读写一个文件的内容之前,必须首先在程序与文件之间建立连接或通信通道,这一过程称为打开文件。打开一个文件的目的可以是为了读或者为了写,也可以是即读又写。
UNIX系统中有两种机制用于描述程序与文件的这种连接:
(1)文件描述符
(2)流
文件描述符 和 流 相同点:
6. 都是用来表示用户程序与被操作的文件之间的连接,并在此连接的 基础上对文件进行读写等访问。
7. 都能表示与普通文件,与设备(如终端),与管道或者套接字的连接,用户打开一个文件,要么返回文件描述符,要么返回一个流。
8. 都包含了一大类的I/O库函数
文件描述符 和 流 不同点:
文件描述符表示为int类型的对象。例如标准输入对应文件描述符0,标准输出对应文件描述符1。
而流则表示为指向结构FILE的指针FILE* ,因此流也称为“文件指针”
如果需要对特定设备进行控制操作,必须使用文件描述符方式,没有函数能对流进行这类操作。
如果需要按照特殊的方式进行I/O操作(例如非阻塞的方式),必须使用文件描述符方式,也没有函数能对流进行这类操作。
文件描述符 和 流的关系:
流给用户程序提供了更高一级的(功能更强大,使用更简化)的I/O接口,它处在文件描述符方式的上层,也就是说,流函数是通过文件描述符函数来实现的。
流 相对于 文件描述符的优点:
执行实际输入输出操作的流函数集合比文件描述符函数要丰富很多,而功能也灵活,强大不少。
文件描述符函数只提供简单的传送字符块的函数
流函数提供格式化I/O,字符I/O,面向行的I/O等大量函数
流函数有利于程序的移植,任何基于ANSI C的系统都支持流,文件描述符的支持则较弱
————————————————
版权声明:本文为CSDN博主「shangtang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shangtang1/article/details/78665018
打开文件
#include 系统/功能
#include
#include
//只要使用内核函数进行文件操作,就把这三个头文件都包含上
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
返回: 若成功为文件描述符,若出错为-1,为什么返回-1,因为数组下标没有负数.
功能: 打开或创建一个文件
pathname:要打开或者创建的文件路径
flags:用来说明此函数的多个选择项
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
mode:新建文件的访问权限,对于open函数而言,仅当创建新文件
时才使用第三个参数。
上面的头文件加sys
的原因:如果头文件在/usr/include
下的直接#include<头文件名>
即可,但是在/usr/include
下的目录下的就要深入目录下了#incldue
用下列一个或多个常数进行或运算构成flags参数(这些
常数定义在<fcntl.h>头文件中)
O_RDONLY 以只读方式打开文件 ******************
O_WRONLY 以只写方式打开文件 ******************
O_RDWR 以读写方式打开文件 ******************
O_APPEND 以追加模式打开文件,每次写时都加到文件的尾端, ******************
但在网络文件系统进行操作时却没有保证。
O_CREAT 如果指定的文件不存在,则按照mode参数指定的文件
权限来创建文件。 ******************
O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错
。这可测试一个文件是否存在。但在网络文件系统进行操作时却没
有保证。 ******************
O_DIRECTORY 如果参数pathname不是一个目录,则open出错
O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将 ******************
其长度截短为0。清空文件.
O_NONBLOCK 如果pathname指的是一个FIFO、一个块特殊文件
或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续
的I/O操作设置非阻塞方式。可见只能对特定类型的文件设置非阻塞功能,并不是所以文件
与标注IO读写操作的映射:
O_RDONLY ===== r
O_WRONLY ===== r+
O_WRONLY|O_CREAT|O_TRUNC ===== w
O_RDWR|O_CREAT|O_TRUNC ===== w+
关闭文件
#include
int close(int fd);
返回: 若成功为0,若出错为-1
功能: 关闭一个打开的文件
参数
fd:已打开文件的文件描述符
当一个进程终止时,它所有的打开文件都由内核自动
关闭。
读取文件
#include
ssize_t read(int fd, void *buf, size_t count);
返回: 读到的字节数,若已到文件尾为0,若出错为-1
功能: 从打开文件中读数据
参数
fd:读取文件的文件描述符
buf:存放读取数据的缓存
count:要求读取一次数据的字节数
有多种情况可使实际读到的字节数少于要求读字节数
读普通文件时,在读到要求字节数之前已到达了文件尾端。
当从终端设备读时,通常一次最多读一行。
当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
某些面向记录的设备,例如磁带,一次最多返回一个记录。
进程由于信号造成中断
读操作从文件的当前位移量处开始,在成功返回之前,该位移量
增加实际读得的字节数。
写入文件
#include
ssize_t write(int fd, const void *buf, size_t count);
返回: 若成功为已写的字节数,若出错为-1 返回为0时不一定是出错了
功能: 向打开的文件中写数据
参数
fd:写入文件的文件描述符
buf:存放待写数据的缓存
count :要求写入一次数据的字节数
其返回值通常与参数count的值不同,否则表示出错
write出错的一个常见原因是:磁盘已写满,或者超过了对一个给
定进程的文件长度限制。
对于普通文件,写操作从文件的当前位移量处开始。如果在打开
该文件时,指定了O_APPEND选择项,则在每次写操作之前,将
文件位移量设置在文件的当前结尾处。在一次成功写之后,该文
件位移量增加实际写的字节数。
案例:
#include
#define BUFFER_LEN 1024
void copy(int fd1, int fd2)
{
char buffer[BUFFER_LEN];
ssize_t nreads;
fd1 = open(,O_RDONLY);
fd2 = open(,O_WRONLY|O_CREAT|O_TRUNC,0600);
while((nreads = read(fd1, buffer, BUFFER_LEN)) != 0){
if(nreads < 0){
fprintf(stderr, "read error: %s\n", strerror(errno));
}else{
if(write(fd2, buffer, nreads) != nreads){
fprintf(stderr, "read error: %s\n", strerror(errno));
}
}
}
}
#include
#include
#include
#include
#include
#define BUFSIZE 1024
int main(int argc,char **argv)
{
int sfd,dfd;
int len,ret;
int pos;
char buf[BUFSIZE];
if(argc<3)
{
fprintf(stderr,"Usage....\n");
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("dfd open()");
exit(1);
}
while(1)
{
len = read(sfd,buf,BUFSIZE);
if(len<0)
{
perror("read()");
break;
}
if(len==0)
break;
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);
}
文件定位
#include
#include
off_t lseek(int fd, off_t offset, int whence);
返回: 若成功则返回新的文件位移量(绝对偏移量)---相对文件的开头,若出错为-1
功能: 定位一个已打开的文件
//c标准中的
int fseek(FILE *stream,long offset,int whence);
fd:已打开文件的文件描述符
offset:位移量,不一定是文件开头可以是文件的任意位置.(与返回值有所差异)
whence:定位的位置
SEEK_SET:将该文件的位移量设置为距文件开始处offset个字节。
SEEK_CUR:将该文件的位移量设置为其当前值加offset,offset可为正
或负。
SEEK_END:将该文件的位移量设置为文件长度加offset,offset可为正
或负。
空洞文件:
lseek也可用来确定所涉及的文件是否可以设置位移量
。如果文件描述符引用的是一个管道或FIFO(这两种文件不支持lseek),则lseek
返回-1,并将errno设置为EPIPE。
每个打开文件都有一个与其相关联的"当前文件偏移量"
。它是一个非负整数,用以度量从文件开始处计算的
字节数。通常,读、写操作都从当前文件偏移量处开
始,并使偏移量增加所读或写的字节数。按系统默认
,当打开一个文件时,除非指定O_APPEND选择项,
否则该位移量被设置为0。
只有打开的文件,才有文件偏移量的概念,所以可知文件偏移量是不记录在文件属性中的
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
int fileno(FILE *stream)
FILE *fdopen(int fildes, const char *mode);
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 512
int main(int argc,char ** argv)
{
int fr,fw;
int n;
char buf[BUFSIZE];
char buf_read[BUFSIZE];
fprintf(stdout,"打开的文件是:%s--%s\n",argv[1],argv[2]);
if(argc < 2){
fprintf(stdout,"%s ->参数不够:\n",argv[0]);
exit(1);
}
if((fr = open(argv[1],O_RDONLY))<0){
fprintf(stderr,"读文件打开失败\n");
exit(1);
}
//O_RDWR,O_WRONLY,O_RDONLY
if((fw = open(argv[2],O_RDWR|O_CREAT,O_TRUNC,0600)) < 0){
printf("fw = %d\n",fw);
fprintf(stderr,"写文件打开失败\n");
close(fr);//当fw文件打开失败时那么fr已经打开了
exit(1);
}
while((n = read(fr,buf,BUFSIZE))>0){
write(fw,buf,n);
}
printf("一下内容开始读:\n");
printf("fr = %d,fw = %d\n",lseek(fr,0,SEEK_CUR),lseek(fw,0,SEEK_CUR));
lseek(fw,0,SEEK_SET);
printf("fr = %d,fw = %d\n",lseek(fr,0,SEEK_CUR),lseek(fw,0,SEEK_CUR));
while((n = read(fw,buf_read,BUFSIZE))>0){
//write(STDOUT_FILENO,buf,n);
fprintf(stdout,"%s ",buf_read);
}
printf("%d----\n",n);
close(fw);
close(fr);
exit(0);
}
#include
#include
#include
int main()
{
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
putchar('a');
write(1,"b",1);
exit(0);
}
//输出结果:bbbbaaaa
strace
strace ./a.out
write(1, "b", 1b) = 1
write(1, "b", 1b) = 1
write(1, "b", 1b) = 1
write(1, "b", 1b) = 1
write(1, "aaaa", 4aaaa) = 4
#time 可以查看执行时间
[yangpipi@yangcentos7 sysIO]$ time ./stdio_complex_sysctlio
bbbbaaaa
real 0m0.003s
user 0m0.000s
sys 0m0.003s
作用:解决竞争和冲突
tmpname不原子
#include
int dup(int oldfd);
int dup2(int oldfd, int newfd);
返回: 成功返回新文件描述符,出错返回-1
功能: 文件描述符的复制
就是把oldfd的指向复制一份到newfd(原来newfd指向的文件被关闭),如此一来,
再向newfd写输入就如同向oldfd写输入一样了。
参数
oldfd:原先的文件描述符
newfd:新的文件描述符
由dup返回的新文件描述符一定是当前可用文件描述符
中的最小数值。
用dup2则可以用newfd参数指定新描述符的数值。如
果newfd已经打开,则先将其关闭。如若oldfd等于
newfd,则dup2返回newfd,而不关闭它。
在进程间通信时可用来改变进程的标准输入和标准输
出设备
案例引入:
//将标准输出的内容输出其他地方
#include
#include
#include
#include
#include
#include
#define FNAME "/tmp/out"
int main()
{
int fd;
close(1);//关闭标准输出
if((fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600))<0){
perror("文件打开失败:\n");
exit(1);
}
puts("hello");
}
#include
#include
#include
#include
#include
#include
#define FNAME "/tmp/out"
int main()
{
int fd;
if((fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600))<0){
perror("文件打开失败:\n");
exit(1);
}
close(1);
dup(fd);//将fd复制到当前可用最小的文件描述符下即1下
close(fd);
puts("hello");
}
/**
存在的问题:
1. 源文件和目标文件时同一个文件。
2. 在并发下,有一个兄弟进程正在使用该文件但是你却关闭了他
本质是
close(1);
dup(fd);//将fd复制到当前可用最小的文件描述符下即1下
不原子
那么dup2解决了上面的问题
/
#include
#include
#include
#include
#include
#include
#define FNAME "/tmp/out"
int main()
{
int fd;
if((fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600))<0){
perror("文件打开失败:\n");
exit(1);
}
dup2(fd,1)
if(fd!=1)
close(fd);
puts("hello");
//记住最后还原标准输出
}
//将标准输出重定向到文件,并且向该文件写入内容,然后恢复标准输出
int main()
{
int fd,tmpfd,savefd;
if((fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600))<0){
perror("文件打开失败:\n");
exit(1);
}
dup2(STDOUT_FILENO,savefd);// /* 保存标准输出 */
printf("savefd:%d\n",savefd);
dup2(fd,STDOUT_FILENO);
if(fd!=1){
close(fd);
}
puts("11111111yyyyy11111111111\n");
dup2(savefd,STDOUT_FILENO); /* 恢复stdout */
printf("我已经恢复了\n");
exit(0);
}
void sync(void);
int fsync(int fd);
3. 同步内存中所有已修改的文件数据到储存设备。
int fdatasync(int fd);
4. fdatasync 函数类似于 fsync, 但它只影响到文件的数据。而 fsync 会更新文件的数据和属性(比如说文件的更新时间等文件属性)。
5. 根据这三个函数的特征,一般来说当你的应用无需延迟 异步,数据的实时性要求没那么高,最终只要数据一致性就可以时,调用 sync 就可以来。而 当对数据的实时性要求比较高时,可采用 fsync 同步更新数据。
int fcntl(int fd, int cmd, ... /* arg */ );
int ioctl(int d, int request, ...);
设备相关的内容
1.虚目录,显示的是当前进程的文件描述符信息.
//如下图所示显示的就是ls的文件描述符信息
[yangpipi@yangcentos7 sysIO]$ ls -l /dev/fd/
总用量 0
lrwx------. 1 yangpipi yangpipi 64 6月 20 23:55 0 -> /dev/pts/1
lrwx------. 1 yangpipi yangpipi 64 6月 20 23:55 1 -> /dev/pts/1
lrwx------. 1 yangpipi yangpipi 64 6月 20 23:55 2 -> /dev/pts/1
lr-x------. 1 yangpipi yangpipi 64 6月 20 23:55 3 -> /proc/68723/fd
4,6,7
《深入理解计算机系统》
类ls的实现,myls
Linux中的七种文件和七种宏
普通文件(regular file) S_ISREG()
目录文件(directory file) S_ISDIR()
块特殊文件(block special file) S_ISBLK()
字符特殊文件(chatacter special file) S_ISCHR()
FIFO(named pipe) S_ISFIFO()
套接字(socket) S_ISSOCK()
符号链接(symbolic link) S_ISLNK()
9种文件访问权限位
用户权限
S_IRUSR, S_IWUSR, S_IXUSR
组权限
S_IRGRP, S_IWGRP, S_IXGRP
其它权限
S_IROTH, S_IWOTH, S_IXOTH
文件权限通过按位或方式构造
、、ls filename命令就是读取这里的信息
struct stat{
mode_t st_mode; /*file type & permissionq权限信息*/
ino_t st_ino; /*i-node number `ls -i看出的`*/
dev_t st_dev; /*device number (file system) 文件保存的设备*/
dev_t st_rdev; /*device number for special files*/
nlink_t st_nlink; /*number of links硬链接数*/
uid_t st_uid; /*user ID of owner*/
gid_t st_gid; /*group ID of owner*/
off_t st_size; /*size in bytes*/
time_t st_atime; /*time of last access*/
time_t st_mtime; /*time of last modification*/
time_t st_ctime; /*time of last file status change*/
blksize_t st_blksize; /*best I/O block size*/
blkcnt_t st_blocks; /*number of disk blocks allocated*/
};
根据目前所学有三种方式
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 512
int main(int argc, char ** argv)
{
int fr = 0;
int size_file = 0;
int size_per;
int buff[BUFFSIZE];
if(argc < 2){
fprintf(stderr,"传入的参数不够,,,\n");
exit(1);
}
if((fr=open(argv[1],O_RDONLY))<0){
fprintf(stderr,"不能打开文件: %s\n",strerror(errno));
exit(1);
}
while((size_per=read(fr,buff,BUFFSIZE))>0){
size_file+=size_per;
printf("%d---\n",size_per);
}
fprintf(stdout,"%s 大小为 %d\n",argv[1],size_file);
exit(0);
}
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char ** argv)
{
int fr;
int pos;
if(argc<2){
fprintf(stderr,"参数不够,%s",strerror(errno));
exit(1);
}
if((fr=open(argv[1],O_RDONLY)) < 0){
fprintf(stderr,"文件打开失败:%s\n",strerror(errno));
exit(1);
}
lseek(fr,0,SEEK_CUR);
pos = lseek(fr,0,SEEK_END);
fprintf(stdout,"文件大小为: %d\n",pos);
exit(0);
}
st-size
由于off_t会变化所以应该使用makefile
-D_FILE_OFFET_BITSET=64 `#include
#include
#include
#include
#include
#include
#include
static int flen(const char *fname)
{ // static修饰函数表示禁止外部扩展
int fr;
struct stat statres;
if(stat(fname,&statres) < 0){
perror("stat()\n");
exit(1);
}
return statres.st_size;
}
int main(int argc,char ** argv)
{
if(argc < 2){
fprintf(stderr,"参数不够~~~");
exit(1);
}
printf("文件大小为:%d\n",flen(argv[1]));
exit(0);
}
#include
#include
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
返回: 若成功则为0,若出错则为-1
功能: 返回一个与pathname或fd指定的文件属性信息,存储在结构体buf中
参数
pathname:文件路径名字
buf:struct stat结构体指针
lstat函数类似于stat,但是当命名的文件是一个符号连
接时,lstat返回该符号连接的有关信息,而不是由该
符号连接引用的文件的信息
st_mode
中,它是一个16位的位图int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
粘住位
t位
如果在一个目录上出现“t”位,这就意味着该目录中的文件只有其属主才可以删除
文件系统:FAT,UFS
硬链接和符号链接
link()-------------ln
unlink()
remove()-----------rm
rename() ------mv
硬链接与目录项是同义词,且建立硬链接有限制:不能给分区建立,不能给目录建立。符号链接优点:可跨分区,可以给目录建立
10
10,11
8
13
15,16