一、昨日回顾
1、在C语言中得到文件的当前目录使用命令getcwd(NULL,0)
2、如何得到当前的文件名?DIR*dir=opendir(目录名)
3、在C语言中改变当前目录chdir("/tmp")
4、超级块信息dirent/文件节点信息inode/文件内容
5、改变文件大小使用函数ftruncate(fd,size)
二、上午的内容
1、VIM-plus安装
(1)安装,使用群里下载好的安装包,复制到Ubuntu下,使用命令tar -xvf 进行解压缩,然后使用命令./install.sh进行安装
(2)安装了vim-plus后怎么关闭
改变./vimrc文件的文件名,mv vimrc backup_vimrc
2、X-shell死机后,文件怎么办?
再次重启机器后,再次打开该文件,可以看到该文件的backup,这时可以选择是否要恢复文件
3、mmap的其他功能?
(1)mmap(viod *addr,size_t length,int prot,int flags,int fd,pff_t offset)
(2)使用munmap命令解除映射,要解除就要全部解除
(3)练习mmap的例子
(4)练习munmmap的例子
(5)Linux的一个物理页的大小是4k
(6)touch file1创建一个大小为0的空文件
使用strcpy(p,"how")实际拷贝的是4个字符
使用Strncpy(p,"how")实际拷贝的是3个字符
(7)映射内存的例子
4、lseek
使用文件定位函数lseek,可以制造文件空洞,文件空洞服务于mmap
5、获取文件信息
可以通过各种宏去获取一些文件信息,在第12页,例如S_ISDIR、S_ISREG、S_ISLNK等函数
6、fopen与open的关系
Fopen(argv[1],"ab+");----此命令使用与fp
Open(argv[1],O_RDER);---此命令适用于fd
判断打开成功还是失败?使用ERROR_CHECK
7、如何通过fp得到fd?
使用fileno函数,将fp放进去,就可以得到对应的文件描述符
例如:int fd=fileno(fp);
8、fread的效率高还是read效率高?二者的区别在哪里?
要看具体的使用场景
9、fdopen函数:fdopen(int fd,const char *type)
有些特殊文件不能用io打开,因此我们需要先使用open函数获取文件的文件描述符fd,这也是fdopen的第一个参数,第二个参数是常量,不同的类型有不同的意义
10、要注意fp和fd的区别
如果是通过fd生成的fp,那么直接用fclose关闭fp就可以,如果先关闭了fd,那么fp就成了一个空指针
11、第13页,文件描述符的复制
复制使用系统调用dup进行
int fd,fd1;
fd=open(argv[1],O_RDWR);
fd1=dup(fd);
(1)直接int fd,fd1,令fd1=fd
①可以实现复制吗?答案是不可以
②怎样就可以复制了?将fd1=fd,改为fd1=dup(fd);
(2)fd是文件描述符
(3)通过dup可以实现重定向标准输出
(4)想要两个文件描述符,不一样,但是指向一个文件,怎么操作?使用两次open命令,打开两次该文件即可
练习例子:
(5)为什么使用dup复制文件描述符后,为什么两个文件描述符指向了同一个文件?
实际两个文件描述符中的ptr指针指向的是一个地址
(6)例子2:使用函数dup2函数可以制定文件描述符的位置,也就是说可以将文件描述符复制到任何一个位置
Dup2(源描述符,目标描述符)
例子代码:
三、下午的内容
1、管道:什么是管道文件?
创建管道文件命令mkfifo 1.pipe
注意,不可以使用vim打开管道,管道是用来通信的
设置一个读端口,设置一个写端口,也就是启动两个进程,就可以实现简单的管道通信
(1)实现简单的一句话通信
①设置读端read,int fdr=open(argv[1],O_RDONLY);
②设置写端口write
③使用makefile_more的makefile,之后使用命令:
(2)
①通信方式分3种:全双工、半双工、单工;管道属于半双工;
②管道通信相当于两个进程共享了一段缓冲区
③如何查看reader进程被卡住的状态?使用ps -elf|grep raed,即可查看进程的状态
(3)设置可以发送多个数据
①read.c和writer.c,设置可以发送多个数据
②当管道里没有数据的时候,reader是不会读的,read读取会阻塞
③后续操作截图:
(4)怎么写一个即时聊天工具呢?使用两条管道,就可以实现全双工
①再创建一个管道使用命令mkfifio 2.pipe
②要注意!可能会发生死锁问题!
要防止出现死锁问题!
双方要保持相同的手法
Chat1打开1号管道,以读的方式,chat2打开1号管道,以写的方式
Chat1打开2号管道,以写的方式,chat2打开2号管道,以读的方式
③chat1.c和chat2.c文件
④两个程序第一次打开的管道必须是同一个管道
⑤重新make一下
(5)打开了管道之后,如何写即时聊天?
①大致思路
②Chat1.c
③Chat2.c的内容
④执行截图:
⑤但是此时设置的聊天还是有一个bug,就是1个进程不能同时发送多条数据,有两个地方会出现卡住,一个是管道,一个是标准输入,如何解决?使用I/O多路转接模型(在第17页)
(6)修复单向关闭后,会出现大量空白的bug
①首先在func.h文件中,加入
#include<sys/select.h>
#include<sys/time.h>
func.h文件在Usr/include/func.h
②timeout是设置一个等待阈值;rdset是传入传出参数,
③Chat2.c的内容
④
(7)程序还有一个bug,就是在某一个进程结束管道后,另一个进程会突然冒出大量空白界面,如何解决?
原因?管道本来是有读端口和写端口,怎么判断另外一端口还在不在?
对于管道来讲,如果写端关闭,读端会直接返回0,而对于内核来讲,状态会被标识为可读;因为一直显示可读,就会一直转入该函数,但是可以读取到的都是0,所以就会疯狂狂打印
如何改造一下,使之不会疯狂打印?
read.c加一句
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,3);
int fdr=open(argv[1],O_RDONLY);//1号管道
int fdw=open(argv[2],O_WRONLY);//2号管道
printf("I am chat1 fdr=%d,fdw=%d\n",fdr,fdw);
char buf[128]={0};
fd_set rdset;
int ret;
struct timeval timeout;
while(1)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO,&rdset);
FD_SET(fdr,&rdset);
bzero(&timeout,sizeof(timeout));
timeout.tv_sec=3;
ret=select(fdr+1,&rdset,NULL,NULL,&timeout);//rdset是传入传出参数
if(ret>0)
{
if(FD_ISSET(STDIN_FILENO,&rdset))
{
memset(buf,0,sizeof(buf));
ret=read(STDIN_FILENO,buf,sizeof(buf));
if(0==ret)
{
printf("broken up\n");
break;
}
write(fdw,buf,strlen(buf)-1);
}
if(FD_ISSET(fdr,&rdset))
{
memset(buf,0,sizeof(buf));
ret=read(fdr,buf,sizeof(buf));
if(0==ret)
{
printf("byebye\n");
break;
}
printf("%s\n",buf);
}
}else{
printf("timeout,come back,do other thing\n");
}
}
close(fdr);
close(fdw);
return 0;
}
write.c同理
对于Ctrl+C强制断开的情况,加一句话
Chat1.c中
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,3);
int fdw=open(argv[1],O_WRONLY);//1号管道
int fdr=open(argv[2],O_RDONLY);//2号管道
printf("I am chat2 fdr=%d fdw=%d\n",fdr,fdw);
char buf[128]={0};
fd_set rdset;
int ret;
while(1)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO,&rdset);
FD_SET(fdr,&rdset);
ret=select(fdr+1,&rdset,NULL,NULL,NULL);//rdset是传入传出参数
if(FD_ISSET(STDIN_FILENO,&rdset))
{
memset(buf,0,sizeof(buf));
read(STDIN_FILENO,buf,sizeof(buf));
write(fdw,buf,strlen(buf)-1);
}
if(FD_ISSET(fdr,&rdset))
{
memset(buf,0,sizeof(buf));
ret=read(fdr,buf,sizeof(buf));
if(0==ret)
{
printf("byebye\n");
break;
}
printf("%s\n",buf);
}
}
return 0;
}
2、I/O多路转接机制-select
(1)select最大可以监视1024个进程,因为fd_set中只可以监视1024;
(2)如何设置超时参数?再定义一个超时参数,每3秒就会打印一句话
额外:bzero命令可以清空内容,相当于C语言的memset,需要在func.h中增加#include<strings.h>;#include<string.h>
Chat1.c
看一下龙哥最新的chat1.c文件
(3)写事件,一个不读,另一个一直写,就会写满,怎么办?
创建一个新文件,叫做read.c,在另一个文件夹write_full中
写文件的内容
启动两个进程后,写进程会疯狂的往管道里写数据,睡眠函数除了sleep函数,还有usleep函数,微秒级的睡眠
读端口一断开,写端口会崩溃
读文件的内容:见具体的代码
(4)
一个文件既可以掌握读端口,也可以掌握写端口,具体例子见第18页
该例子在第18页
使用2条管道,既用于监视写事件,又可以监视读事件
创建了新文件在select_write文件夹下
文件write.c
清空文件可以使用FD_ZERO