Linux下的文件操作——基于文件描述符的文件操作(2)
文件描述符的复制
MMAP文件映射
ftruncate修改文件大小
文件描述符的复制
系统调用函数dup和dup2可以实现文件描述符的复制,经常用来重定向进程的stdin(0), stdout(1), stderr(2)。
dup返回新的文件描述符(没有使用的文件描述符的最小编号)。这个新的描述符是旧文件描述符
的拷贝。这意味着两个描述符共享同一个数据结构。
dup2允许调用者用一个有效描述符(oldfd)和目标描述符(newfd),函数成功返回时,目标描述符将变成旧描述符的复制品,此时两个文件描述符现在都指向同一个文件,并且是函数第一个参数(也就是oldfd)指向的文件。
注意:文件描述符的复制是指用另外一个文件描述符指向同一个打开的文件,它完全不同于直接给文件描述符变量赋值!
int fd2=fd; //赋值,当释放掉一个,另一个已经不能操作了
函数原型:
#include <uistd.h>
int dup(int oldfd)
int dup2(int oldfd, int newfd)
先看一个简单的例子:
#include <func.h>
//dup是引用计数原理
int main(int argc, char *argv[])
{
ARGS_CHECK(argc, 2);
int fd, fd1;
fd = open(argv[1], O_RDWR);
ERROR_CHECK(fd, -1, "open");
printf("fd = %d\n",fd);
fd1 = dup(fd);
printf("fd1 = %d\n",fd1);
char buf[128] = {0};
int ret;
ret = read(fd, buf, 5);
printf("ret = %d, buf = %s\n", ret, buf);
close(fd);
memset(buf, 0, sizeof(buf));
ret = read(fd1, buf, 5);
printf("ret = %d, buf = %s\n", ret, buf);
close(fd1);
return 0;
}

从上述代码段中可以看出fd和fd1指向同一个文件,并不是两个相同的文件,因为每个文件都有各自的ptr偏移指针,从打印结果中看出,第一次read后读出了“hello”,第二次read后读出了“world”。第一次close(fd)以后文件并没有关闭。
PS:内核的文件打开引用计数为0时,文件正式关闭!
再来看个重定向 stdout 例子:
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,2);
int fd,fd1;
fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
printf("\n");
close(1);
fd1=dup(fd);
printf("fd1=%d\n",fd1);
close(fd);//关闭对应文件
printf("you can't see me\n");
return 0;
}

该程序首先打开了一个文件,返回一个文件描述符,因为默认的就打开了0,1,2表示标准输入,标准输出,标准错误输出。而用close(1),则表示关闭标准输出,此时这个文件描述符就空着了。后面又用dup,此时dup(fd),则会复制一个文件描述符到当前未打开的最小描述符,此时这个描述符为1,即fd1 =1,后面关闭fd自身,然后在用标准输出的时候,发现标准输出重定向到你指定的文件了。那么printf所输出的内容也就直接输出到文件了。
而打开两个文件的情况和文件描述符的复制又是不同的:
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,2);
int fd,fd1;
fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
fd1=open(argv[1],O_RDWR);
char buf[128]={0};
int ret;
ret=read(fd,buf,5);
ERROR_CHECK(ret,-1,"read");
printf("ret=%d,buf=%s\n",ret,buf);
memset(buf,0,sizeof(buf));
ret=read(fd1,buf,5);
printf("ret=%d,buf=%s\n",ret,buf);
return 0;
}

首先每个进程中文件描述符相互分离,即进程1中文件描述符从0、1、2开始,进程2中也从0、1、2开始,两者没有联系,其次打开两次,fd=3 , fd1=4,说明分别有两个指针指向文件地址空间(区别于dup,dup是共享同一个文件指针ptr)。于是就可以读出两个hellw。
dup2(int fdold,int fdnew)也是进行描述符的复制,只不过采用此种复制,新的描述符由用户用参数fdnew显示指定,而不是象dup一样由内核帮你选定(内核选定的是随机的)。对于dup2,如果fdnew已经指向一个已经打开的文件,内核会首先关闭掉fdnew所指向的原来的文件。此时再针对于fdnew文件描述符操作的文件,则采用的是fdold的文件描述符。如果成功dup2的返回值于fdnew相同,否则为-1.
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,2);
int fd,fd1;
fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
printf("\n");
dup2(STDOUT_FILENO,20);
fd1=dup2(fd,STDOUT_FILENO);
printf("fd1=%d\n",fd1);
close(fd);
printf("you can't see me\n");
dup2(20,1);
printf("you can see me\n");
return 0;
}
第一个dup2的作用使STDOUT_FILENO定位为20,返回值是第二个参数。而printf是依靠标准输出符打印的,此时标准输出符已经偏移,于是第一条打印看不见,第二条再修改回1后打印才能看见。
MMAP文件映射
头文件 <sys/mman.h>
- 函数原型
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);
- 参数说明:
start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。
length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理
prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
PROT_EXEC //页内容可以被执行
PROT_READ //页内容可以被读取
PROT_WRITE //页可以被写入
PROT_NONE //页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
(一般用这个)MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
off_t offset:被映射对象内容的起点。 4K对齐
系统调用
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或System V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
1、mmap()系统调用形式如下:
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
参数fd为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。prot 参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。flags由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。offset参数一般设为0,表示从文件头开始映射。参数addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。这里不再详细介绍mmap()的参数,读者可参考mmap()手册页获得进一步的信息。2、系统调用mmap()用于共享内存的两种方式:
(1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下:
fd=open(name, flag, mode);
if(fd<0)
...
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 通过mmap()实现共享内存的通信方式有许多特点和要注意的地方,我们将在范例中进行具体说明。
(2)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。
对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可3、系统调用munmap()
int munmap( void * addr, size_t len )
该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。4、系统调用msync()
int msync ( void * addr , size_t len, int flags)
一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。
举例:
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,2);
int fd;
fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
printf("fd=%d\n",fd);
char *p;
struct stat buf; //为了获取文件长度
fstat(fd,&buf); //对于已经打开的文件用fstat
p=(char*)mmap(NULL,buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED ,fd,0);
ERROR_CHECK(p,-1,"mmap");
strcpy(p,"HELLO");
munmap(p,buf.st_size);
return 0;
}
mmap比read和write效率更高,原因是read和write是通过系统调用,先将数据存储到内存的内核区再从内核区搬移到堆栈区域,而mmap通过DMA机制直接将数据拷贝到堆区,被称为“零拷贝”(无内核态到用户态的拷贝)
ftruncate修改文件大小
#include <func.h>
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,2);
int fd;
fd=open(argv[1],O_RDWR|O_CREAT,0666);
ERROR_CHECK(fd,-1,"open");
printf("fd=%d\n",fd);
ftruncate(fd,4096);
char *p;
struct stat buf;
fstat(fd,&buf);
p=(char*)mmap(NULL,buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED ,fd,0);
ERROR_CHECK(p,(char*)-1,"mmap");
memcpy(p,"HELLO",5);
munmap(p,buf.st_size);
return 0;
}
Linux下的文件操作——基于文件描述符的文件操作(2)的更多相关文章
- linux一切皆文件之tcp socket描述符(三)
一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件(比如:块设备,socket套接字,pipe队列) 2.操作这些不同的类型就像操作文件一样,比如增删改查等 二.环境准备 ...
- Linux下如何查看tomcat是否安装、启动、文件路径、进程ID
Linux下如何查看tomcat是否安装.启动.文件路径.进程ID 在Linux系统下,Tomcat使用命令的操作! 检测是否有安装了Tomcat: rpm -qa|grep tomcat 查看Tom ...
- linux下保存下位机输出的串口信息为文件
linux下保存下位机输出的串口信息为文件 1.stty -F /dev/ttyUSB0 raw (转换成raw模式) 2.stty -F /dev/ttyUSB0 speed 115200 (设置波 ...
- Linux下使用matlab在后台默默的运行.m文件(无界面形式)
Linux下使用matlab在后台默默的运行.m文件(无界面形式)本主在Ubuntu18.04LTS上已经安装了matlab直接运行Matlab$ matlab会启动 matlab,出现启动界面但想要 ...
- Linux--文件描述符、文件指针、索引节点
Linux -- 文件描述符 文件描述符 Fd 当进程打开文件或创建新文件时,内核会返回一个文件描述符(非负整数),用来指向被打开的文件,所有执行I/O操作的系统调用(read.write)都会通过文 ...
- 文件描述符、文件表项、V节点表项的一些总结
转自 http://blog.csdn.net/gzzheyi/article/details/7739556 表格可以参见APUE 第三版 P61. 文件描述符(进程级别): 1).在每个进程表中 ...
- Linux下的文件操作——基于文件描述符的文件操作(1)
概要: 打开.创建和关闭文件 读写文件 文件定位 获取文件信息 打开.创建和关闭文件 函数原型: #include <sys/types.h> //头文件 #include <sys ...
- linux下如何挂接(mount)光盘镜像文件、移动硬盘、U盘、Windows网络共享和NFS网络共享
首先,介绍一下挂接(mount)命令的使用方法,mount命令参数非常多,这里主要讲一下今天我们要用到的. 命令格式: mount [-t vfstype] [-o options ...
- linux下tar gz bz2 tgz z等众多压缩文件的压缩与解压方法
Linux下最常用的打包程序就是tar了,使用tar程序打出来的包我们常称为tar包,tar包文件的命令通常都是以.tar结尾的.生成tar包后,就可以用其它的程序来进 行压缩了,所以首先就来讲讲ta ...
随机推荐
- MySQL数据库-表内容操作
1.表内容增加 insert into 表 (列名,列名...) values (值,值,值...); 添加表内容添加一条数据 insert into 表 (列名,列名...) values (值,值 ...
- 【shell编程】之基础知识了解shell
一.什么是shell Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了 ...
- MongoDB 副本集 pymongo使用
搭建没有仲裁节点的副本集,推荐使用 2.清空node2的db文件夹 和 log 文件夹 rm -rf /var/lib/mongod/* rm -rf /var/log/mongod/* 3.修改no ...
- jenkins 集成 keycloak 认证
keycloak 是很不错的sso 工具,当然也有Jenkins 的插件,我们可以使用jenkins 插件,方便用户账户的管理 环境准别 docker-compose version: "3 ...
- 03关于C的数组指针
一.数组与指针有什么关系 (1)地址 数组名就是一个地址,指向一个数组的开端. 从实验结果我们可以看出前三个地址都是一样的,都是数组的开始地址,而最后一个是数组的第二个元素,因为是整型数据,地址加4. ...
- Promise实例的catch方法
//Promise.prototype.catch方法是.then(null,rejection)的别名, //用于指定发生错误时的回调函数 //then方法指定的回调函数如果运行时抛出错误,也会被c ...
- java 实现websocket的三种方式
Java中实现websocket常见有以下三种方式: 使用tomcat的websocket实现,需要tomcat 7.x,JEE7的支持. 使用spring的websocket,spring与webs ...
- eclipse卡死在search for main types 20 files to index
run as application时,提示search for main types 20 files to index (*/*/*.jar)某个maven依赖jar出了问题,找不到main方法 ...
- javaweb下载中的一个问题
如果你发现,response头以及contentType都已经设置没错,但出现浏览器将下载的文件内容显示到新窗口 那么解决方案就是在请求的时候不要产生新的窗口
- ML平台_微博深度学习平台架构和实践
( 转载至: http://www.36dsj.com/archives/98977) 随着人工神经网络算法的成熟.GPU计算能力的提升,深度学习在众多领域都取得了重大突破.本文介绍了微博引入深度学 ...