IO的效率问题:
习题:mycp(read版本的)里面的BUFSIZE值为1和10的区别,10和100的区别,当BUFSIZE达到一个值,这个时候如果在增大BUFSIZE就会出现性能下降。观察进程所消耗的时间注意性能最佳拐点出现时的BUFSIZE值,以及何时程序会出问题。
文件共享:
多个任务共同操作一个文件或是协同完成任务。
写程序删除一个文件的第十行。
文件的存放有点像数组的存放
数组当中我们删除一个元素,实际上是把删除的元素覆盖掉,将删除元素后面的元素逐个向前移动。这个数组最终还是那么大的空间,但是会多出一块没有用到。
文件也是类似的,要删除一个文件的第十行,我们要找到第九行的换行符之后的内容,就是第十行的行首再找到第十一行的行首从第十一行开始一个一个向前覆盖,或者一块一块的向前覆盖写。
思路1:
lseek定位到第十一行的行首,然后读内容,然后再去定位,定位到第十行的行首,然后去覆盖写。整个内容是要包含在循环结构中的。
while()
{
lseek 11+read+lseek 10+write
}
最后这个文件会多出来一行长度,就是第十行的长度,最终的文件要进行截短,把多的一块截掉。
思路2:
在当前进程空间里面,打开文件两次
1->open() r ->fd1->lseek 1
2->open r+ ->fd2->lseek 10
while()
{
1->fd1->read
2->fd2->write
}
思路3:
换成两个进程(也可以是线程):
process1->open->r
process2->open ->r+
p1->read->p2->write
截断的函数:
truncate():
ftruncate():
--------------------- truncate a file to a specified length
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);//把一个未打开的文件截断到多长
int ftruncate(int fd, off_t length);//把一个已经打开的文件截断到多长
原子操作:
不可分割的操作
原子:不可分割的最小单位
原子操作的作用:就是解决竞争和冲突
tmpname就不是一个原子操作
多用于多进程和多线程并发的时候
重定向:
程序中的重定向是怎么实现的(dup,dup2)
利用下面的方法可以将hello!输出到指定的文件上
标准的输出是1,对1做一个重定向
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out"
int main()
{
int fd;
close(1);//关闭1号
fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);//文件描述符是优先使用当前可用范围内最小的,
//所以打开的文件会占据1号,标准输出的位置就被这个文件占据了,下面的输出就会输出到这个文件上
if(fd < 0)
{
perror("open():");
exit(1);
}
/************************************/
puts("hello!");
exit(0);
}
利用dup函数
dup, dup2()函数:
------------------------- duplicate a file descriptor
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
The dup() system call creates a copy of the file descriptor oldfd,using the lowest-numbered unused descriptor for the new descriptor.//使用当前可用范围内最小的文件描述符作为新的描述符,拷贝一个副本放到当前可用范围内最小的位置上去。
如果原来有个4号文件描述符,0~5都被用上了,执行dup,把这个4号复制一份,当前可用范围内最小的就是6号,所以会把4号的一个副本放到6号,4号和6号指向的是同一个结构体。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out"
int main()
{
int fd;
fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd < 0)
{
perror("open():");
exit(1);
}
close(1);
dup(fd);
close(fd);
/************************************/
puts("hello!");
exit(0);
}
上面代码的默认1是标准输出,但是有的时候改变等等1就不一定是标准输出,如果没有1号,就会出错。如果是多进程或是多线程的时候,当我们close(1)时,还没来得及dup,有可能有别的进程或是线程先使用了1号,就会让puts输出到别的文件上去。
就是下面的两句不是原子操作
close
dup
dup2:
dup2就可以实现原子化操作
dup2可以理解为close和dup的原子操作
所以上面的代码那两句话,可以使用dup2代替dup2(fd,1);
*If oldfd is not a valid file descriptor, then the call fails, and
newfd is not closed.
* If oldfd is a valid file descriptor, and newfd has the same value as
oldfd, then dup2() does nothing, and returns newfd.
如果fd本来就是1,那么dup2就不做什么,还是保持原状,带式代码下面,又有一个close,就相当于我们自己关了,所以还是会出错。所以在最后的close前加一个条件
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "/tmp/out"
int main()
{
int fd;
fd=open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd < 0)
{
perror("open():");
exit(1);
}
dup2(fd,1);
if(fd!=1)
close(fd);
/************************************/
puts("hello!");
exit(0);
}
最后最好复原一下
同步(sync,fsync,fdatasync):
sync();
---------- commit buffer cache to disk(同步buffer和cache,内核层面的)
也可以叫做全局催促,什么时候会用到全局催促,解除设备挂载(关机)的时候。
比如说当前的内核在即将关机的时候,需要把当前的正在cache当中的或者buffer还没有来得及同步的数据刷新一下。这个时候就可以用sync
然后下一步解除设备挂载
fsync;
#include <unistd.h>
int fsync(int fd);//同步一个文件的buffer或者是cache
int fdatasync(int fd);//同步一个文件,只刷数据不刷亚数据
数据:文件当中的有效内容
亚数据:文件最后的修改时间,文件的属性等等这些内容。
fcntl();
文件描述符所变的魔术几乎都来源于该函数(管家级的函数
fcntl - manipulate file descriptor(管理文件描述符
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
cmd不同那么传的参数也就不同
返回值:
For a successful call, the return value depends on the operation:
F_DUPFD The new descriptor.
F_GETFD Value of file descriptor flags.
F_GETFL Value of file status flags.
F_GETLEASE
Type of lease held on file descriptor.
F_GETOWN Value of descriptor owner.
F_GETSIG Value of signal sent when read or write becomes possible, or
zero for traditional SIGIO behavior.
F_GETPIPE_SZ, F_SETPIPE_SZ
The pipe capacity.
F_GET_SEALS
A bit mask identifying the seals that have been set for the
inode referred to by fd.
All other commands
On error, -1 is returned, and errno is set appropriately.
ioctl();
设备相关的内容都归这个函数管
ioctl - control device
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
虚目录:
/dev/fd目录
显示的是当前进程的文件描述符信息