UNIX环境高级编程(第三版) UNIX网络编程 TCP/IP详解(卷一) 深入理解计算机系统 APUE
一:I/O一切实现的基础(input&output)
man手册:
第一章:Linux基础
第二章:
表示预处理(以#开头的内容,在预处理阶段完成)
gcc -E test.c
第三章:文件调用
第七章:机制(socket、tcp、epoll)
1.stdio(标准I/O)
stdio:FILE类型贯穿始终
①FILE *fopen(const char *path,const char *mode)
path指文件路径
mode指操作类型
如果函数运行成功,返回一个FILE指针,如果失败返回一个空指针并且设置errno,(errno是一个全局变量,用于全局的错误代码,必须实时打印)
注:
end of file 是指文件最后一个字节的下一个字节
在mode选项中,若在windows环境下编程,需要在加‘b’指定为二进制流,否则默认为文件流,在linux环境下只有一种流定义
在使用molloc函数时,如果报错显示等号两边类型不匹配,可能是没有包含malloc函数的头文件
errno的具体错误的含义在/usr/include/asm-generic/errno-base.h中可以查看
函数①的return值返回的FILE指针指向的区域是在(堆F 栈F 静态区T)有互逆操作则在堆上,没有互逆操作在其他地方
函数返回值和局部变量在栈上,动态分配的空间在堆上,全局变量和静态变量在静态区
/*如果在栈上,tmp在栈帧中,函数结束,栈被释放,tmp地址不在程序掌控范围内
全局变量有弊端,不会定义为全局变量*/
FILE *fopen(const char *path,const char *mode)
{
FILE tmp;
tmp. = ;
.....
return &tmp;
}
/*如果在静态区上,重复调用,不会生成新的空间。第一个文件和第二个文件会填充在同一片空间(覆盖)*/
FILE *fopen(const char *path,const char *mode)
{
FILE tmp;
tmp. = ;
.....
return &tmp;
}
/*如果在堆上*/
FILE *fopen(const char *path,const char *mode)
{
FILE *tmp == NULL;
tmp = malloc(sizeof(FILE));
tmp-> = ;
.....
return tmp;
}
//perror - print a system error message自动关联全局变量errno
fp = fopen("tmp","r");
if(fp == NULL)
{
// fprintf(stderr,"fopen() failed! errno = %d\n",errno);
perror("fopen()");
exit(1);
}
//resule: fopen(): No such file or directory
/*strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l -
return string describing error number*/
fprintf(stderr,"fopen():%s\n",strerror(errno));
//result fopen():No such file or directory
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("tmp","r");
if(fp == NULL)
{
// fprintf(stderr,"fopen() failed! errno = %d\n",errno);
// perror("fopen()");
fprintf(stderr,"fopen():%s\n",strerror(errno));
exit(1);
}
puts("OK!");
fclose(fp);
exit(0);
}
②int fclose(FILE *fp)
关闭文件fp,成功返回0,失败返回-1(宏EOF)并且设置errno
一个进程默认打开三个流 stdin stdout stderr
注:
资源就有上限, 默认最多可以打开1024个文件,默认前三个为stdin、stdout、stderr,
查看上限设置:ulimit -a
设置TAB键缩进,vim ~/.vim 中打开vimrc
ll查看文件夹下所有文件的属性; ls -l (文件名) 查看文件属性
文件属性计算公式:0 666 & ~umask(0 002) 八进制数,umask值越大,权限越低
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
int count = 0;
FILE *fp = NULL;
while(1)
{
fp = fopen("/home/wjy/Desktop/code/wjy/io/stdio/tmp","w");
if(fp == NULL)
{
perror("fopen()");
break;
}
count++;
}
printf("count = %d\n",count);
// puts("OK!");
// fclose(fp);
exit(0);
}
③int fgetc(FILE *stream)
int getc(FILE *stream) 定义为函数,fgetc被定义为宏,两者是一样的
int getchar(void)相当于fgetc(stdin)
成功则返回读到的字符的int形式或者EOF,失败返回error
内核使用宏,宏不占用调用时间,只占用编译时间。函数不占用编译时间,只占用整个进程的调用时间。(内核当中的实现是节约一点一滴的时间)
应用编程开发以稳定安全为主,建议使用函数
通过命令行将第一个文件复制到第二个
#include <stdio.h>
#include <stdlib.h>
/*
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ ./mycpy /etc/services /tmp/out
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ diff /etc/services /tmp/out
copy files from A to B
*/
int main(int argc,char **argv)
{
FILE *fps,*fpd;
int ch;
if(argc < 3)
{
fprintf(stderr,"Usage:%s <src_file> <dest_file>\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(1)
{
ch = fgetc(fps);
if(ch == EOF)
break;
fputc(ch,fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
diff /etc/services /tmp/out比较两个文件,没有提示表示文件相同
④int fputc(int c,FILE *stream)
int putc(int c,FILE *stream)被定义为函数,fputc定义为宏
int putchar(int c)相当于putc(c,stdout)
计算文件字符数
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
FILE *fp;
int count = 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(fgetc(fp) != EOF)
{
count++;
}
printf("count = %d\n",count);
fclose(fp);
exit(0);
}
⑤char *fgets(char *s,int size,FILE *stream)
在stream中读取字节到s中,最多读取size个字节;读到文件尾返回NULL,出错返回errno,正确时返回s
函数正常结束的情况:读了(size-1)个字节(剩余一个字节补全尾0)/读到'\n'
/*
#define SIZE 5
char buf[SIZE];
fgets(buf,SIZE,stream);
1.size-1
2.'\n'
ab=>a b '\n' \0
abcd
1->a b c d '\0'
2->'\n' \0
读两次
*/
#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 1024
/*
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ ./mycpy_fgets /etc/services /tmp/out
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ diff /etc/services /tmp/out
copy files from A to B
*/
int main(int argc,char **argv)
{
FILE *fps,*fpd;
char buf[BUFSIZE];
if(argc < 3)
{
fprintf(stderr,"Usage:%s <src_file> <dest_file>\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);
}
⑥int fputs(const char *s,FILE *stream)
将字符串输出到stream中,返回非负整数,如果返回EOF(-1)则出错
⑦size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream)
从stream中读字节到ptr中,读nmemb个对象每个对象size的大小
返回成功读到的对象的个数,如果读到的不足一个对象返回0或者errno
⑧size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream)
将ptr中的字节写入到stream中,写入nmemb个对象,每个对象size个字节
返回成功写到的对象的个数,如果写到的不足一个对象返回0或者不足对象的所含字符个数
#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 1024
/*
#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 1024
/*
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ ./mycpy_fread /etc/services /tmp/out
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ diff /etc/services /tmp/out
copy files from A to B
*/
int main(int argc,char **argv)
{
FILE *fps,*fpd;
char buf[BUFSIZE];
if(argc < 3)
{
fprintf(stderr,"Usage:%s <src_file> <dest_file>\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);
}
⑨int fprint(FILE *stream,const char *format,...)
将...以format的形式输出到stream里
int sprintf(char *str,const char *format,...)输出到字符串str里(可以实现与atoi相反的效果,将任何形式转换成str字符串形式) 不会检查是否溢出
#include <stdio.h>
#include <stdlib.h>
int main()
{
int year = 2014,month = 5,day = 13;
char buf[1024];
sprintf(buf,"%d-%d-%d",year,month,day);
puts(buf);//puts会自动追加换行
// char str[] = "123456";
// printf("%d\n",atoi(str));
exit(0);
}
int snprintf(char *str,size_t size,const char *format,...)输出到字符串中去,这块str空间大小为size,仅容纳size-1个,有尾0
eg:
重定向输出:echo 1 > 文件名
查看文件新增内容: cat 文件名
int atoi(const char *nptr)将字符串转化成整型数 ‘1223’--->1223 拿到字母或'\0'为止'123a1'-->'123'
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[] = "123456";
printf("%d\n",atoi(str));
exit(0);
}
一般写入或读取时都是一行一行为终止,所以在return时'\n'会进行特殊处理
⑩int fsanf(FILE *stream,const char *format,...)
int sanf(const char *format,...)缺点是看不见进来的输入有多大
int sscanf(const char *str,const char *format,...)将缓冲区上的信息输入到st
⑪int fseek(FILE *stream,long offset,int whence)
定位 文件位置指针,offset是偏移量(+向前,-向后 ),whence是偏移相对位置包括(SEEK_SET,SEEK_CUR,SEEK_END) 返回值为整数
/*文件位置指针,读写都发生在当前位置
fp = fopen();
fputc(fp) *10//文件位置指针向后偏移,指向第11个字符的位置
可以通过以下两个函数调整位置
//fclose();
//fopen();
fgetc() *10//向后移动,读取10个未知内容
*/
/*文件位置指针,读写都发生在当前位置
fp = fopen();
fputc(fp) *10//文件位置指针向后偏移,指向第11个字符的位置
fseek(fp,0,SEEK_SET);//文件首
fseek(fp,-10,SEEK_CUR);//从当前位置向前偏移10个字节
fgetc() *10//向后移动,读取10个未知内容
*/
long ftell(FILE *stream)
在使用ftell或者fseek时,如果long是32位,则文件大小最多为4G才能有效查找
通过fseek和flen函数查看文件长度
#include <stdio.h>
#include <stdlib.h>
/*
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ make flen
cc flen.c -o flen
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ ls -l flen.c
-rw-rw-r-- 1 wjy wjy 605 4月 22 15:49 flen.c
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ ./flen flen.c
605
*/
int main(int argc,char **argv)
{
FILE *fp;
int count = 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);
}
fseek(fp,0,SEEK_END);
printf("%ld\n",ftell(fp));
/*
fp = fopen(argv[1],"r+");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
while(fgetc(fp) != EOF)
{
count++;
}
printf("count = %d\n",count);
*/
fclose(fp);
exit(0);
}
void rewind(FILE *stream)
文件位置指针定位到文件首
空洞文件(文件内字符都为ASCLL码0)直接使用fseek将位置定位在文件后2G占住空间
切片后用多线程锁住,别人无法使用
eg:
如果在使用函数时需要加入宏定义时,可以直接在编译命令加:gcc a.c -o a -D_FILE_OFFSET_BITS=64,或者在makefile中加入:CFLAGS+=-D_FILE_OFFSET_BITS=64(注意-D是命令)
查看文件属性:ls -l 文件名
在一些(部分)体系中,通常把off_t和long定义为32位整型值
如果编译时 加入 #define _FILE_OFFSET_BITS 64 则off-t定义为64位
~/Desktop/code/wjy/io/stdio$ gcc a.c -o a -D_FILE_OFFSET_BITS=64
off_t 重新定义一个数据类型
int fseeko(FILE *stream, off_t offset, int whence);
off_t ftello(FILE *stream);
⑫int fflush(FILE *stream)
如果stream是NULL,那么会刷新所有的输出流(直接刷新缓冲区,例如让尚在缓冲区的输出直接输出,不用等待程序运行结束)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
printf("Before while()\n");//必须加'\n',否则while(1)只会在缓冲区第一行运行,导致无法输出
/*
printf("Before while()");
fflush(NULL);
*/
while(1);
printf("After while()\n");
exit(0);
}
eg:
缓冲区的作用:大多数情况下是好事,合并系统调用,见setvbuf函数
行缓冲:换行时候刷新,满了的时候刷新,强制刷新(fflush)(标准输出是这样的,因为是终端设备)
全缓冲:满了的时候刷新,强制刷新(默认,只要不是终端设备)
无缓冲:如stderr,需要立即输出的内容
setvbuf调整缓冲模式
vim中,光标停在函数,shift+k可以直接跳转到man手册
⑬ssize_t getline(char **lineptr,size_t *n,FILE *stream)
传入一个字符串地址和一个整型数地址,可以读到一整行的数,返回读到的字符个数,包括分隔符如'\n'、空格,但是不包括'/0'。失败返回-1(此函数只是不断在申请内存,并没有释放内存的操作,存在内存泄漏问题)
lineptr是缓冲区,存储读取的数据,n是缓冲区的大小(须分别初始化为NULL和0)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ make getline
cc -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE getline.c -o getline
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/stdio$ ./getline makefile
44
120
*/
int main(int argc,char **argv)
{
FILE *fp;
char *linebuf = NULL;
size_t linesize;
if(argc < 2)
{
fprintf(stderr,"Usage...\n");
exit(1);
}
linebuf = NULL;
linesize = 0;
fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
while (1)
{
if(getline(&linebuf,&linesize,fp) < 0)
break;
printf("%ld\n",strlen(linebuf));
printf("%ld\n",linesize);
}
fclose(fp);
exit(0);
}
⑭临时文件
1、如何不冲突 2、及时销毁
tmpnam:创建一个函数名,然后调用其他函数打开文件,没有实现原子操作,可能导致冲突
FILE *tmpfile(void):创建一个无名文件,以二进制w+r形式打开,可以避免冲突,并且将FILE使用fclose之后,由于FILE的可指向路径计数器count为0的时候这快空间自动释放,可以做到及时销毁。即使忘记关闭,若函数执行return 0 ,终止时会释放所有的内存空间,临时文件也会销毁。
eg:
打开所有隐藏文件(匿名文件,不会冲突):ls -a
2、系统调用IO(文件IO)
①文件描述符的概念(整型数int,数组下标,文件描述符优先使用当前可用范围内最小的)
fd是在文件IO中贯穿始终的类型
在一个进程中,会分配单独的一块指针数组,数组中每个元素指向一个文件位置指针相关结构体执指向文件结构体,文件描述符就是数组的下标;一般数组的前三个位置分别是stdin,stdout,stderr;
默认分配新的文件描述符时,首先分配可用的下标最小的数组元素。
eg:一般情况下,一个进程空间分配1024个文件描述符
②文件IO的操作:open,close,read,write,lseek
int open(const char *pathname,int flags)
flags包含:O_RDONLY,O_WRONLY,O_RDWR等,如果用到O_CREAT,则需给文件权限
r | O_RDONLY | 要求文件存在 |
r+ | O_RDWR | 要求文件存在 |
w | O_WRONLY|O_CREAT|O_TRUNC | 文件不存在则需创建,且文件有内容则需清空 |
w+ | O_RDWR|O_TRUNC|O_CREAT | 有则清空,无则创建 |
a | O_WEONLY|O_CREAT|O_APPEND | |
a+ | O_RDWR|O_CREAT|O_APPEND |
int open(const char *pathname,int flags,mode_t mode)
int creat(const char *pathname,mode_t mode)
ssize_t read(int fd,void *buf,size_t count)
ssize_t write(int fd,const void *buf,size_t count)
off_t lseek(int fd,off_t offset,int whence)
注:
CFLAGS+=-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall
//-Wall表示打印所有的警告
buff是用户态读写数据暂存区,cache是内核态读写加速机制
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSIZE 1024
/*
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ make mycpy_1
cc mycpy_1.c -o mycpy_1
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ ./mycpy_1 /etc/services /tmp/out
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ diff /etc/services /tmp/out
*/
int main(int argc,char **argv)
{
int sfd,dfd;
char buf[BUFSIZE];
int len,ret,pos;
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(sfd < 0)
{
close(sfd);
perror("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()");
close(dfd);
close(sfd);
exit(1);
}
pos += ret;
len -= ret;
}
}
close(dfd);
close(sfd);
exit(0);
}
eg:
阻塞IO:一直等待直到这个阶段执行结束;非阻塞IO:如果遇到问题可以跳过
在open中函数名相同但是函数的参数不同的现象:变参函数
如何识别重载和变参函数的形式:在输入多个参数时,系统不报错,则函数也不知道自己有几个参数,那么使用变参函数,如果系统报错,则函数的参数个数时固定的,使用重载
③文件IO与标准IO的区别:
区别:响应速度&吞吐量,标准IO设置缓冲区,在缓冲区满或其他紧急情况时与内核交互,所以响应速度慢,文件IO及时与内核交互,只要有一个任务就调用一次内核,响应速度快,从而吞吐量低;
面试:加快程序,可以从响应速度和吞吐量,两个方面考虑
提醒:文件IO与标准IO不可混用(有无缓冲区情况下pos指针位置不同)
FILE *fp;
fputc(fp) -> pos++
fputc(fp) -> pos++
存在缓冲区,并非直接修改磁盘位置
转换函数:FILE *fdopen(int fd,const char *mode)把一个成功打开的文件描述符fd,封装到FILE *中使用
int fileno(FILE *stream):标准IO转文件IO
eg:
查看文件的系统调用路径:strace ./文件名
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ strace ./ab
execve("./ab", ["./ab"], 0x7ffc09202f70 /* 57 vars */) = 0
brk(NULL) = 0x63deb7888000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fff3f8cc900) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7e82f2453000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=61375, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 61375, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7e82f2444000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\226 \25\252\235\23<l\274\3731\3540\5\226\327"..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2220400, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2264656, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7e82f2200000
mprotect(0x7e82f2228000, 2023424, PROT_NONE) = 0
mmap(0x7e82f2228000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7e82f2228000
mmap(0x7e82f23bd000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7e82f23bd000
mmap(0x7e82f2416000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x215000) = 0x7e82f2416000
mmap(0x7e82f241c000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7e82f241c000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7e82f2441000
arch_prctl(ARCH_SET_FS, 0x7e82f2441740) = 0
set_tid_address(0x7e82f2441a10) = 7217
set_robust_list(0x7e82f2441a20, 24) = 0
rseq(0x7e82f24420e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7e82f2416000, 16384, PROT_READ) = 0
mprotect(0x63deb73d0000, 4096, PROT_READ) = 0
mprotect(0x7e82f248d000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7e82f2444000, 61375) = 0
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}, AT_EMPTY_PATH) = 0
getrandom("\x96\xe7\x83\x98\x94\x31\xc7\xbd", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x63deb7888000
brk(0x63deb78a9000) = 0x63deb78a9000
write(1, "b", 1b) = 1
write(1, "b", 1b) = 1
write(1, "b", 1b) = 1
write(1, "aaa", 3aaa) = 3
exit_group(0) = ?
+++ exited with 0 +++
查看命令执行时间:time 命令
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ make mycpy_1
cc mycpy_1.c -o mycpy_1
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ ./mycpy_1 /etc/services /tmp/out
wjy@wjy-virtual-machine:~/Desktop/code/wjy/io/sys$ time ./mycpy_1 /etc/services /tmp/out
real 0m0.012s //理论值为比usr+sys多一点,由于调度等待时间。(用户关注)
user 0m0.006s //在user层面的执行时间(设计者关注)
sys 0m0.006s //当前命令在系统调用/内核层面的执行时间(设计者关注)
real:real=user+sys+调度等待(无法因为主观因素改变)
user:程序在user层面消耗的时间
sys:程序在进行时在系统调用层面花费的时间
④文件共享
多个程序同时操作同一个文件或者协同完成任务
尝试面试题:删除一个文件的第十行,
补充知识点(计算某一行长度):
int truncate(const char *path,off_t length);把一个未打开的文件截断为**长度
int ftruncate(int fd,off_t length)把一个已打开的文件截断为**长度
while()
{
lseek 11 +read + lseek 10 +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
注:操作系统里原子操作是防止进程并行导致的可能错误,如死锁和同时访问临界区
⑥程序中的重定向
int dup(int oldfd) 使用当前可用范围内最小的描述符代替原有fd,作为新的描述符
int dup2(int oldfd,int newfd)
拷贝一个文件描述符到指针数组的最小下标位置,其中dup2是(dup+close)的原子操作。
更改输出位置:输出到终端,改为输出到文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FNAME "/tmp/out"
int main()
{
int fd;
close(1);
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if (fd < 0)
{
perror("open():");
exit(1);
}
/******************************************/
puts("hello!");
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FNAME "/tmp/out"
int main()
{
int fd;
// close(1);
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if (fd < 0)
{
perror("open():");
exit(1);
}
/*
close(1);/*漏洞:开始打开的fd如果就是1,会出错 多进程并发,其他进程同时操作文件描述符1(非原子操作)*/
dup(fd);
*/
dup2(fd,1);//不存在漏洞,如果fd = 1,不进行操作。当不能贸然关闭fd
if(fd != 1)
close(fd);
/******************************************/
puts("hello!");
exit(0);
}
⑦同步
void sync(void)全局催促(例,关机:解除设备挂载),把设备当中正在cache或buffer的数据刷新一下,然后解除设备挂载,int fsync(int fd),int fdatasync(int fd):同步一个文件,但只刷数据不刷亚数据
数据与亚数据:数据是指正常的数据内容,(一个文件当中的有效内容),亚数据是指文件的运行的修改时间时间、大小等属性
⑧fcnti():文件描述符相关的函数几乎都来自于该函数
ioctl():设备相关的内容都与此函数相关
/dev/fd/目录:虚目录,显示当前进程的文件描述符的信息
三、文件系统
完成一个类似ls的实现,如myls
ls:cmd --长格式 -短格式 非选项的传参
ls --all 打印所有文件,包括隐藏文件 == ls -a
ls -i 打印所有文件,包括inode信息
ls -n 打印所有文件,包括用户名
stat+文件名 打印文件属性信息
touch -- -a 表示非选项的传参,创建一个文件名(-a) == touch ./-b
1、目录和文件
①获取文件属性
int stat(const char *pathname, struct stat *statbuf):通过文件路径获取属性信息,填入buf空间,面对符号链接文件时获取的时所指向目标文件的属性,失败返回-1
int fstat(int fd, struct stat *statbuf):通过文件描述符获取属性
int lstat(const char *pathname, struct stat *statbuf):面对符号链接文件时获取的时符号链接文件的属性
struct stat {
dev_t st_dev; /* ID of device containing file包含当前文件的设备ID号 */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode 权限信息*/
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; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated 当前文件占了多少个512大小的块*/
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access 最后一次读的时间*/
struct timespec st_mtim; /* Time of last modification 最后一次写的时间*/
struct timespec st_ctim; /* Time of last status change 最后一次亚数据修改的时间*/
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
eg:
创建一个-b名字的文件可以用命令:touch -- -b或者touch ./-b(--表示当前命令结束)
删除时可以用命令:rm -- -b或rm ./-b(./表示路径)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static off_t flen(const char *fname)
{
struct stat statres;
if(stat(fname,&statres) < 0)
{
perror("stat()");
exit(1);
}
return statres.st_size;
}
int main(int argc,char **argv)
{
if(argc < 2)
{
fprintf(stderr,"Usage...\n");
exit(1);
}
printf("%ld\n",flen(argv[1]));
exit(0);
}
②文件访问权限
umask
文件权限的更改/管理
粘住位
文件系统:FAT,UFS
硬链接,符号链接
utime
目录的创建和销毁
更改当前工作路径
分析目录/读取目录内容