- 浏览: 1658325 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (405)
- C/C++ (16)
- Linux (60)
- Algorithm (41)
- ACM (8)
- Ruby (39)
- Ruby on Rails (6)
- FP (2)
- Java SE (39)
- Java EE (6)
- Spring (11)
- Hibernate (1)
- Struts (1)
- Ajax (5)
- php (2)
- Data/Web Mining (20)
- Search Engine (19)
- NLP (2)
- Machine Learning (23)
- R (0)
- Database (10)
- Data Structure (6)
- Design Pattern (16)
- Hadoop (2)
- Browser (0)
- Firefox plugin/XPCOM (8)
- Eclise development (5)
- Architecture (1)
- Server (1)
- Cache (6)
- Code Generation (3)
- Open Source Tool (5)
- Develope Tools (5)
- 读书笔记 (7)
- 备忘 (4)
- 情感 (4)
- Others (20)
- python (0)
最新评论
-
532870393:
请问下,这本书是基于Hadoop1还是Hadoop2?
Hadoop in Action简单笔记(一) -
dongbiying:
不懂呀。。
十大常用数据结构 -
bing_it:
...
使用Spring MVC HandlerExceptionResolver处理异常 -
一别梦心:
按照上面的执行,文件确实是更新了,但是还是找不到kernel, ...
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法 -
dsjt:
楼主spring 什么版本,我的3.1 ,xml中配置 < ...
使用Spring MVC HandlerExceptionResolver处理异常
高级文件I/O
1、Linux提供的高级I/O操作:
1)Scatter/gatter I/O:可以通过单个系统调用从多个buffer写到文件或者读到多个buffer中。
2)Epoll:是poll()和select()的改进版。
3)Memory-mapped I/O:将一个文件映像到内存,通过内存的操作来操作文件,操作更高效简单。
4)File advice:允许进程向内核提供使用的场景来优化I/O效率。
4)异步I/O:允许进程发出I/O请求后不等待操作完成返回。
2、Scatter/gatter I/O:
通过单个系统调用从单个流读取到或写入多个buffer数据。
优点:对于有些操作更自然、更高效、具有原子性。
1)readv:
从文件描述符fd中读去count个段到多个buffers iov中:
2)writev:
从多个buffers中写入入最多count个段到由fd描述符标示的文件中。
readv、writev与read、write的行为一直,除了读到多个buffers中或者从多个buffers写
iovec结构:
按照iovec[0],iovec[1],...,iovec[count-1]的次序依次读写。
返回值:成功,返回读写的字节数,这个字节数是所有iovec结构中iov_len的总和。
失败返回-1,并设置好errno。
iov_len的总和不能超过SSIZE_MAX,count需要大于0,小于IOV_MAX(<limits.h>中),在linux为1024,
否则返回-1,errno设置为EINVAL,
例子:writev:
readv例子:
3、The Event Poll Interface
poll()和select()需要一些描述符列表进行监控,内核需要遍历每一个描述符进行监控,这样如果
描述符列表比较大,就会产生性能问题。
epoll解决了这个问题,它将监控的注册和实际的监控进行了解耦,一个系统调用用来初始化epoll的
环境,另一个用来添加或者删除描述符,第三个进行实际的事件等待。
1)创建一个新的Epoll实例:
epoll_create:
创建了一个epoll实例,返回一个文件描述符,这个文件描述符不和一个实际的文件相关,只是被后面的系统调用
所用到。size是提供给内核要监控多少个文件描述符的一个线索,并不是最大值,提供一个大约的值可以提高性能。
失败了返回-1,并设置errno。EINVAL表示size参数不是正数,ENFILE表示达到了打开文件的最大值,ENOMEM内存不足。
例子:
2) 控制epoll:
epoll_ctl可以从epoll的环境中添加和删除文件描述符。
epoll_event结构:
op指定了与fd相关要执行的操作,event参数进一步描述了操作的行为。
op值:
EPOLL_CTL_ADD:添加一个监控的文件描述符到有epfd关联的epoll中。
EPOLL_CTL_DEL:从epfd关联的epoll中删除一个监控的文件描述符。
EPOLL_CTL_MOD: 修改有fd表示的文件描述符。
epoll_event结构中:
events可以是以下值的OR:
EPOLLERR: 文件发生错误,这个事件始终监控,即使没有指定。
EPOLLET:使得监控文件的edge-triggered行为可用,和Level-triggered events对应。
默认是Level-triggered
EPOLLHUP:文件hangup发生。这个事件始终监控,即使没有指定。
EPOLLIN:文件可以无需阻塞的进行读操作。
EPOLLONESHOT:当一个事件被生成和读取,这个文件自动不再被监控,需要重新通过EPOLL_CTL_MOD设置监控。
EPOLLOUT:文件可以无需阻塞的进行写操作。
EPOLLPRI:存在紧急的out-of-band数据需要读。
成功返回0,失败返回-1,并设置好errno,
EBADF:epfd不是一个合法的epoll实例。
EEXIST:EPOLL_CTL_ADD操作,fd已经存在。
ENOENT:EPOLL_CTL_ADD,EPOLL_CTL_DEL操作,不存在相关的fd。
EINVAL:epfd不是epoll的一个实例。
ENOMEM:没有足够的内存。
EPERM:fd不支持epoll
例子:
添加
修改:
删除:
3)使用epoll等待事件:
epoll_wait:
epoll_wait等待与epfd相关联的文件描述符的事件直到timeout微妙。
timeout是0,则立即返回,timeout是-1等待直到一个事件发生。
成功,events指针指向了epoll_event结构,描述了每一个事件,最大到maxevents个事件。
失败返回-1,并设置errno:
EBADF:epfd不合法的文件描述符。
EFAULT:进程没有对有events指向的内存的写访问权限。
EINTR:进程被信号中断。
EINVAL:epfd是个不合法的epoll实例。
例子:
4)边缘出发(Edge-Triggered Events) VS 条件触发(Level-Triggered Events):
在epoll_ctl中,如果epoll_event结构中的events参数中设置为EPOLLET,则监控为Edge-Triggered,
否则是Level-Triggered,默认是Level-Triggered。Level-Triggered和poll、select的行为一致,这是
很多开发者期望的方式。使用非阻塞编程可以使用Edge-Triggered。
不同点:
考虑下面使用pipe通讯的生产者消费者例子:
1)生产者写1kb到管道中。
2)消费者使用epoll_wait,等待管道数据到来。
如果使用Level-Triggered,epoll_wait会立即返回,因为已经有数据到来。
如果使用Edge-Triggered,epoll_wait可能会阻塞,如果没有写操作继续的话,因为Edge-Triggered只有在监控的文件的事件发生的时候分发事件。
而2)步是在生产者写之后去epoll_wait的。
4、将文件映像到内存:
内核提供了一个接口,可以将文件映像到内存的buffer中,这样当我们从buffer中获取数据时,相应的文件数据就会被读取,类似,当我们存储数据
到内存中时,相应的数据自动写入文件。
1)mmap:
addr:建议内核将文件映射到的地址,这只是一个hint,为了可移植性,一般设置为0,调用返回mapping开始的实际地址。
fd:要映像文件的描述符。
prot描述了内存映像的保护权限,可以使用OR连接以下选项:
PROT_READ:区域可读。
PROT_WRITE:区域可写
PROT_EXEC:区域可执行
PROT_NONE:区域不可访问,很少有用。
prot不能和打开文件的模式冲突。比如打开了一个只读文件,prot不可以指定为PROT_WRITE。
flags:描述了内存映像的方式,可以使用OR连接以下选项:
MAP_FIXED: addr是必须的,并且不是一个hint,如果内核无法在该地址映像,则失败。
具有不可移植性,不推荐使用。
MAP_PRIVATE:映像不共享,文件被映像为copy-on-write,任何在内存中的改变,都不会反映在文件或者其他进程的mapping中。
MAP_SHARED:映像和其他进程共享映射的同一个文件,写入buffer等效于写入文件,读取映像写操作同时反映在其他进程中。
MAP_SHARE和MAP_PRIVATE其中之一必须被指定。
addr和off的值需要被指定为系统虚拟页的整数倍,可以通过使用_SC_PAGESIZE或_SC_PAGE_SIZE作为sysconf参数获得页的大小。
如果文件的大小12字节,但系统页的大小是512字节,那么系统调用会映射512字节,其他的部分被填充为0,我们可以修改另外的
500个字节,但是并不反应在文件中,所有我们不能使用mmap来append文件,我们需要首先增大文件到指定的值。
sysconf:
获得页的大小:
linux提供了getpagesize():
并不是所有的Unix系统都提供这个方法,POSIX标准已经将它抛弃掉了。
mmap()成功返回mapping的地址,失败返回MAP_FAILED,并设置errno:
EACESS:fd不是普通文件或者fd的模式和prot、flags冲突。
EAGAIN:文件被文件锁锁定。
EBADF:文件描述符不合法。
EINVAL:addr、len或者off不合法
ENFILE:达到了系统最大打开文件数
ENODEV:文件所在的文件系统不支持mmap。
ENOMEM:进程没有足够的内存。
EOVERFLOW:addr+len大于地址空间
EPERM:PROT_EXEC被指定,但是文件系统被mount成noexec。
两个相关的信号:
SIGBUS:进程访问一个已经不合法的映射,例如,文件映射之后被truncated
SIGSEGV:试图往被映射成只读的区域写。
2)munmap:
删除有mmap()创建的映像:
munmap:
mummap删除了从addr地址开始,是page对齐的,连续的lenzijie的映像,一旦被删除,内存中的区域
就不在有效,访问会产生SIGSEGV信号。
munmap通常传入由mmap返回的addr以及设置的len长度。
成功返回0,失败返回-1,并设置errno:EINVAL,表示有一个或者更多的参数不正确。
内存映射例子:
3)mmap的优缺点:
优点:
1、从内存映像文件中读写,避免了read、write多余的拷贝。
2、从内存映像文件中读写,避免了多余的系统调用和用户-内核模式的切换
3、可以多个进程共享内存映像文件。
4、seeking内存映像只需要指针操作,避免系统调用lseek。
缺点:
1、内存映像需要时整数倍页大小,如果文件较小,会浪费内存。
2、内存映像需要在放在进程地址空间,大的内存映像可能导致地址空间碎片,找不到足够大的空余连续区域供其它用。
3、内核需要维护更多的和内存映像相关的数据结构。
4)重新设置映像大小:
mremap扩展或者缩小内存映像,从区域[addr,add+old_size]编程一个新的大小new_size
flags:
0:不可以移动来改变内存映像大小。
MREMAP_MAYMOVE:如果需要可以移动地址改变内存映像大小。
返回值:成功返回重新设置大小之后的内存映像的大小,失败返回MAP_FAILED,并设置errno:
EAGAIN:内存区域被锁定,无法改变大小。
EFAULT:给定区域的一些也有不合法的页或者重新映像存在问题。
EINAL:参数不合法。
ENOMEM:不移动则无法扩展,如果MREMAP_MAYMOVE没有被设置。
glibc经常使用mremap来实现高效的realloc():
5)改变映像的protection:
mprotect可以允许程序改变已经存在内存区域的permissions:
一些系统只能改变由mmap得到的内存映像的protection,Linux可一个操作任何一个内存区域。
成功返回0,失败返回-1,并设置errno为:
EACCESS:不可以被设置成prot的permissions,可能是文件打开时只读的,设置成可写
EINVAL:参数不合法。
ENOMEM:内核内存不足或者所给的内存区域不是进程的合法地址空间。
6)将文件和映像同步:
POSIX提供了和文件操作中fsync类似的将文件和内存映像同步的操作:
msync:
将内存映像flush到磁盘。没有msync,没有能够保证将mapping的脏数据写回磁盘,除非被unmapped。
当修改内存映像,进程直接修改在内核页cache中的文件页,内核可能不会很快将内核的页cache同步到
磁盘。
flags使用OR链接下面选项:
MS_ASYNC:异步的执行同步操作,msync立即返回,更新操作被调度。
MS_INVALIDATE:指定所有其他的内存映像cache副本无效。任何以后的操作都同步到磁盘中。
MS_SYNC:同步的执行同步操作,等将内容写入磁盘在返回。
成功返回0,失败返回-1,并设置errno:
EINVAL:MS_SYNC和MS_ASYNC同时被设置或者addr没有页对齐。
ENOMEM:所给的内存区域(或者部分)没有被映射。
7)给mapping建议:
Linux提供了madvice来让进程建议内核或者给内核提供线索来使用mapping,这样可以优化mapping的使用。
len如果为0,内核将建议施用于从addr开始的整个映像。
advice:可以是以下之一:
MADV_NORMAL:没有特别的建议。建议使用中等程度的预先读。
MADV_RANDOM:以随机访问的方式访问指定的区域。建议较少的预先读。
MADV_SEQUENTIAL:顺序访问指定区域。建议大量预先读
MADV_WILLNEED:将来要访问指定区域。初始化预先读,将指定的页读到内存。
MADV_DONTNEED:将来不再访问指定区域。内核释放与指定页关联的资源。后续的读会导致从文件中再度调入。
参考:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》
1、Linux提供的高级I/O操作:
1)Scatter/gatter I/O:可以通过单个系统调用从多个buffer写到文件或者读到多个buffer中。
2)Epoll:是poll()和select()的改进版。
3)Memory-mapped I/O:将一个文件映像到内存,通过内存的操作来操作文件,操作更高效简单。
4)File advice:允许进程向内核提供使用的场景来优化I/O效率。
4)异步I/O:允许进程发出I/O请求后不等待操作完成返回。
2、Scatter/gatter I/O:
通过单个系统调用从单个流读取到或写入多个buffer数据。
优点:对于有些操作更自然、更高效、具有原子性。
1)readv:
从文件描述符fd中读去count个段到多个buffers iov中:
#include <sys/uio.h> ssize_t readv(int fd,const struct iovec *iov, int count);
2)writev:
从多个buffers中写入入最多count个段到由fd描述符标示的文件中。
#include <sys/uio.h> ssize_t writev(int fd,const struct iovec *iov, int count);
readv、writev与read、write的行为一直,除了读到多个buffers中或者从多个buffers写
iovec结构:
#include <sys/uio.h> struct iovec{ void *iov_base; /* pointer to the start of buffer */ size_t iov_len; /* size of buffer in bytes */ }
按照iovec[0],iovec[1],...,iovec[count-1]的次序依次读写。
返回值:成功,返回读写的字节数,这个字节数是所有iovec结构中iov_len的总和。
失败返回-1,并设置好errno。
iov_len的总和不能超过SSIZE_MAX,count需要大于0,小于IOV_MAX(<limits.h>中),在linux为1024,
否则返回-1,errno设置为EINVAL,
例子:writev:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <sys/uio.h> int main(){ struct iovec iov[3]; ssize_t nr; int fd,i; char *buf[] = { "The term buccaneer comes from the word boucan.\n", "A boucan is a wooden frame used for cooking meat.\n", "Buccaneer is the West Indies name for a pirate.\n" }; fd = open("buccaneer.txt",O_WRONLY | O_CREAT | O_TRUNC); if( fd == -1 ){ perror("open"); return 1; } for(i = 0; i < 3; i++){ iov[i].iov_base = buf[i]; iov[i].iov_len = strlen(buf[i]); } nr = writev(fd,iov,3); if(nr == -1){ perror("writev"); return 1; } printf("wrote %d bytes\n",nr); if(close(fd)){ perror("close"); return 1; } return 0; }
readv例子:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/uio.h> int main(){ char foo[48],bar[51],baz[49]; struct iovec iov[3]; ssize_t nr; int fd,i; fd = open("buccaneer.txt",O_RDONLY); if(fd == -1){ perror("open"); return 1; } iov[0].iov_base = foo; iov[0].iov_len = sizeof(foo); iov[1].iov_base = bar; iov[1].iov_len = sizeof(bar); iov[2].iov_base = baz; iov[2].iov_len = sizeof(baz); nr = readv(fd,iov,3); if(nr == -1){ perror("readv"); return 1; } for(i = 0; i < 3; i++){ printf("%d: %s",i,(char *)iov[i].iov_base); } if(close(fd)){ perror("close"); return 1; } return 0; }
3、The Event Poll Interface
poll()和select()需要一些描述符列表进行监控,内核需要遍历每一个描述符进行监控,这样如果
描述符列表比较大,就会产生性能问题。
epoll解决了这个问题,它将监控的注册和实际的监控进行了解耦,一个系统调用用来初始化epoll的
环境,另一个用来添加或者删除描述符,第三个进行实际的事件等待。
1)创建一个新的Epoll实例:
epoll_create:
#include <sys/epoll.h> int epoll_create(int size);
创建了一个epoll实例,返回一个文件描述符,这个文件描述符不和一个实际的文件相关,只是被后面的系统调用
所用到。size是提供给内核要监控多少个文件描述符的一个线索,并不是最大值,提供一个大约的值可以提高性能。
失败了返回-1,并设置errno。EINVAL表示size参数不是正数,ENFILE表示达到了打开文件的最大值,ENOMEM内存不足。
例子:
int epfd; epfd = epoll_create(100); if(epfd < 0) perror("epoll_create");
2) 控制epoll:
epoll_ctl可以从epoll的环境中添加和删除文件描述符。
#include <sys/epoll.h> int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
epoll_event结构:
struct epoll_event{ __u32 events;/* events */ union{ void *ptr; int fd; __u32 u32; __u64 u64; }data; };
op指定了与fd相关要执行的操作,event参数进一步描述了操作的行为。
op值:
EPOLL_CTL_ADD:添加一个监控的文件描述符到有epfd关联的epoll中。
EPOLL_CTL_DEL:从epfd关联的epoll中删除一个监控的文件描述符。
EPOLL_CTL_MOD: 修改有fd表示的文件描述符。
epoll_event结构中:
events可以是以下值的OR:
EPOLLERR: 文件发生错误,这个事件始终监控,即使没有指定。
EPOLLET:使得监控文件的edge-triggered行为可用,和Level-triggered events对应。
默认是Level-triggered
EPOLLHUP:文件hangup发生。这个事件始终监控,即使没有指定。
EPOLLIN:文件可以无需阻塞的进行读操作。
EPOLLONESHOT:当一个事件被生成和读取,这个文件自动不再被监控,需要重新通过EPOLL_CTL_MOD设置监控。
EPOLLOUT:文件可以无需阻塞的进行写操作。
EPOLLPRI:存在紧急的out-of-band数据需要读。
成功返回0,失败返回-1,并设置好errno,
EBADF:epfd不是一个合法的epoll实例。
EEXIST:EPOLL_CTL_ADD操作,fd已经存在。
ENOENT:EPOLL_CTL_ADD,EPOLL_CTL_DEL操作,不存在相关的fd。
EINVAL:epfd不是epoll的一个实例。
ENOMEM:没有足够的内存。
EPERM:fd不支持epoll
例子:
添加
struct epoll_event event; int ret; event.data.fd = fd; event.events = EPOLLIN | EPOLLOUT; ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event); if(ret) perror("epoll_ctl");
修改:
struct epoll event event; int ret; event.data.fd = fd; event.events = EPOLLIN; ret = epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&event); if(ret) perror("epoll_ctl");
删除:
struct epoll_event event; int ret; ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&event); if(ret) perror("epoll_ctl");
3)使用epoll等待事件:
epoll_wait:
#include <sys/epoll.h> int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
epoll_wait等待与epfd相关联的文件描述符的事件直到timeout微妙。
timeout是0,则立即返回,timeout是-1等待直到一个事件发生。
成功,events指针指向了epoll_event结构,描述了每一个事件,最大到maxevents个事件。
失败返回-1,并设置errno:
EBADF:epfd不合法的文件描述符。
EFAULT:进程没有对有events指向的内存的写访问权限。
EINTR:进程被信号中断。
EINVAL:epfd是个不合法的epoll实例。
例子:
#define MAX_EVENTS 64 struct epoll_event *events; int nr_events,i,epfd; events = malloc(sizeof(struct epoll_event) * MAX_EVENTS); if(! events ){ perror("malloc"); return 1; } nr_events = epoll_wait(epfd,events,MAX_EVENTS,-1); if(nr_events < 0){ perror("epoll_wait"); free(events); return 1; } for(i = 0; i < nr_events;i++){ printf("event=%ld on fd=%d\n",events[i].events,events[i].data.fd); /* *现在可以使用events[i].data.fd进行无阻塞的操作了。 */ } free(events);
4)边缘出发(Edge-Triggered Events) VS 条件触发(Level-Triggered Events):
在epoll_ctl中,如果epoll_event结构中的events参数中设置为EPOLLET,则监控为Edge-Triggered,
否则是Level-Triggered,默认是Level-Triggered。Level-Triggered和poll、select的行为一致,这是
很多开发者期望的方式。使用非阻塞编程可以使用Edge-Triggered。
不同点:
考虑下面使用pipe通讯的生产者消费者例子:
1)生产者写1kb到管道中。
2)消费者使用epoll_wait,等待管道数据到来。
如果使用Level-Triggered,epoll_wait会立即返回,因为已经有数据到来。
如果使用Edge-Triggered,epoll_wait可能会阻塞,如果没有写操作继续的话,因为Edge-Triggered只有在监控的文件的事件发生的时候分发事件。
而2)步是在生产者写之后去epoll_wait的。
4、将文件映像到内存:
内核提供了一个接口,可以将文件映像到内存的buffer中,这样当我们从buffer中获取数据时,相应的文件数据就会被读取,类似,当我们存储数据
到内存中时,相应的数据自动写入文件。
1)mmap:
#include <sys/mmap.h> void *mmap(void *addr,size_t len,int prot, int flags,int fd,off_t offset);
addr:建议内核将文件映射到的地址,这只是一个hint,为了可移植性,一般设置为0,调用返回mapping开始的实际地址。
fd:要映像文件的描述符。
prot描述了内存映像的保护权限,可以使用OR连接以下选项:
PROT_READ:区域可读。
PROT_WRITE:区域可写
PROT_EXEC:区域可执行
PROT_NONE:区域不可访问,很少有用。
prot不能和打开文件的模式冲突。比如打开了一个只读文件,prot不可以指定为PROT_WRITE。
flags:描述了内存映像的方式,可以使用OR连接以下选项:
MAP_FIXED: addr是必须的,并且不是一个hint,如果内核无法在该地址映像,则失败。
具有不可移植性,不推荐使用。
MAP_PRIVATE:映像不共享,文件被映像为copy-on-write,任何在内存中的改变,都不会反映在文件或者其他进程的mapping中。
MAP_SHARED:映像和其他进程共享映射的同一个文件,写入buffer等效于写入文件,读取映像写操作同时反映在其他进程中。
MAP_SHARE和MAP_PRIVATE其中之一必须被指定。
addr和off的值需要被指定为系统虚拟页的整数倍,可以通过使用_SC_PAGESIZE或_SC_PAGE_SIZE作为sysconf参数获得页的大小。
如果文件的大小12字节,但系统页的大小是512字节,那么系统调用会映射512字节,其他的部分被填充为0,我们可以修改另外的
500个字节,但是并不反应在文件中,所有我们不能使用mmap来append文件,我们需要首先增大文件到指定的值。
sysconf:
#include <unistd.h> long sysconf(int name);
获得页的大小:
long page_size = sysconf (_SC_PAGESIZE);
linux提供了getpagesize():
#include <unistd.h> int getpagesize(void);
并不是所有的Unix系统都提供这个方法,POSIX标准已经将它抛弃掉了。
mmap()成功返回mapping的地址,失败返回MAP_FAILED,并设置errno:
EACESS:fd不是普通文件或者fd的模式和prot、flags冲突。
EAGAIN:文件被文件锁锁定。
EBADF:文件描述符不合法。
EINVAL:addr、len或者off不合法
ENFILE:达到了系统最大打开文件数
ENODEV:文件所在的文件系统不支持mmap。
ENOMEM:进程没有足够的内存。
EOVERFLOW:addr+len大于地址空间
EPERM:PROT_EXEC被指定,但是文件系统被mount成noexec。
两个相关的信号:
SIGBUS:进程访问一个已经不合法的映射,例如,文件映射之后被truncated
SIGSEGV:试图往被映射成只读的区域写。
2)munmap:
删除有mmap()创建的映像:
munmap:
#include <sys/mman.h> int mummap(void *addr, size_t len);
mummap删除了从addr地址开始,是page对齐的,连续的lenzijie的映像,一旦被删除,内存中的区域
就不在有效,访问会产生SIGSEGV信号。
munmap通常传入由mmap返回的addr以及设置的len长度。
成功返回0,失败返回-1,并设置errno:EINVAL,表示有一个或者更多的参数不正确。
if(munmap(addr,len) == -1) perror("munmap");
内存映射例子:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> int main(int argc,char *argv[]){ struct stat sb; off_t i; char *p; int fd; if(argc < 2){ fprintf(stderr,"usage: %s <file>\n",argv[0]); return 1; } fd = open(argv[1],O_RDONLY); if(fd == -1){ perror("open"); return 1; } if(fstat(fd,&sb) == -1){ perror("fstat"); return 1; } if(! S_ISREG(sb.st_mode)){ fprintf(stderr,"%s is not a regular file\n",argv[1]); return 1; } p = mmap(0,sb.st_size, PROT_READ,MAP_SHARED,fd,0); if(p == MAP_FAILED){ perror("mmap"); return 1; } if(close(fd) == -1){ perror("close"); return 1; } for(i = 0;i < sb.st_size; i++){ putchar(p[len]); } if(munmap(p,sb.st_size) == -1){ perror("mummap"); return 1; } return 0; }
3)mmap的优缺点:
优点:
1、从内存映像文件中读写,避免了read、write多余的拷贝。
2、从内存映像文件中读写,避免了多余的系统调用和用户-内核模式的切换
3、可以多个进程共享内存映像文件。
4、seeking内存映像只需要指针操作,避免系统调用lseek。
缺点:
1、内存映像需要时整数倍页大小,如果文件较小,会浪费内存。
2、内存映像需要在放在进程地址空间,大的内存映像可能导致地址空间碎片,找不到足够大的空余连续区域供其它用。
3、内核需要维护更多的和内存映像相关的数据结构。
4)重新设置映像大小:
#define _GNU_SOURCE #include <unistd.h> #include <sys/mman.h> void *mremap(void *addr, size_t old_size, size_t new_size, unsigned long flags);
mremap扩展或者缩小内存映像,从区域[addr,add+old_size]编程一个新的大小new_size
flags:
0:不可以移动来改变内存映像大小。
MREMAP_MAYMOVE:如果需要可以移动地址改变内存映像大小。
返回值:成功返回重新设置大小之后的内存映像的大小,失败返回MAP_FAILED,并设置errno:
EAGAIN:内存区域被锁定,无法改变大小。
EFAULT:给定区域的一些也有不合法的页或者重新映像存在问题。
EINAL:参数不合法。
ENOMEM:不移动则无法扩展,如果MREMAP_MAYMOVE没有被设置。
glibc经常使用mremap来实现高效的realloc():
void * realloc(void *addr, size_t len){ size_t old_size = look_up_mapping_size(addr); void *p; p = mremp(addr,old_size, len,MREMAP_MAYMOVE); if(p == MAP_FAILED) return NULL; return p; }
5)改变映像的protection:
mprotect可以允许程序改变已经存在内存区域的permissions:
#include <sys/mman.h> int mprotect(const void *addr, size_t len, int prot);
一些系统只能改变由mmap得到的内存映像的protection,Linux可一个操作任何一个内存区域。
成功返回0,失败返回-1,并设置errno为:
EACCESS:不可以被设置成prot的permissions,可能是文件打开时只读的,设置成可写
EINVAL:参数不合法。
ENOMEM:内核内存不足或者所给的内存区域不是进程的合法地址空间。
6)将文件和映像同步:
POSIX提供了和文件操作中fsync类似的将文件和内存映像同步的操作:
msync:
#include <sys/mman.h> int msync(void *addr, size_t len, int flags);
将内存映像flush到磁盘。没有msync,没有能够保证将mapping的脏数据写回磁盘,除非被unmapped。
当修改内存映像,进程直接修改在内核页cache中的文件页,内核可能不会很快将内核的页cache同步到
磁盘。
flags使用OR链接下面选项:
MS_ASYNC:异步的执行同步操作,msync立即返回,更新操作被调度。
MS_INVALIDATE:指定所有其他的内存映像cache副本无效。任何以后的操作都同步到磁盘中。
MS_SYNC:同步的执行同步操作,等将内容写入磁盘在返回。
if(msync(addr,len,MS_ASYNC) == -1) perror("msync");
成功返回0,失败返回-1,并设置errno:
EINVAL:MS_SYNC和MS_ASYNC同时被设置或者addr没有页对齐。
ENOMEM:所给的内存区域(或者部分)没有被映射。
7)给mapping建议:
Linux提供了madvice来让进程建议内核或者给内核提供线索来使用mapping,这样可以优化mapping的使用。
#include <sys/mman.h> int madvice(void *addr, size_t len, int advice);
len如果为0,内核将建议施用于从addr开始的整个映像。
advice:可以是以下之一:
MADV_NORMAL:没有特别的建议。建议使用中等程度的预先读。
MADV_RANDOM:以随机访问的方式访问指定的区域。建议较少的预先读。
MADV_SEQUENTIAL:顺序访问指定区域。建议大量预先读
MADV_WILLNEED:将来要访问指定区域。初始化预先读,将指定的页读到内存。
MADV_DONTNEED:将来不再访问指定区域。内核释放与指定页关联的资源。后续的读会导致从文件中再度调入。
int ret; ret = madvise(addr,len,MADV_SEQUENTIAL); if(ret < 0) perror("madvice");
参考:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》
发表评论
-
【zz】Vim的分屏
2012-04-26 11:13 1603分屏启动Vim 使用大写 ... -
bash的几种for循环
2012-04-26 11:13 18581、罗列式 for VARIABLE in 1 2 3 ... -
【zz】几个简单选项让你的Readline(Bash)更好用
2011-10-10 23:08 2429Reddit上的一位仁兄贴出了他的readline手册学习成果 ... -
pipe in -exec
2011-09-27 22:35 1433一个简单的例子: 将一堆的.txt文件,合并成一个文件,让每个 ... -
[zz]Unix调试的瑞士军刀:lsof
2011-08-31 10:33 1507原文见:http://heikezhi.com/2011/06 ... -
[zz]关于xargs,你可能不知道的
2011-08-29 23:05 2483如果你曾经花了许多时间在Unix命令行上,那么或许你已经见过x ... -
使用scala.sys.process包和系统交互
2011-07-19 00:00 6501在Java中我们可以使用Runtime.getRuntime( ... -
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法
2011-06-23 22:17 14482在笔记本安装Ubuntu11.04增强功能失败 引用 fuli ... -
awk getline
2011-06-02 23:58 5848awk getline开始一直用做按行读取文件。 getlin ... -
[zz]服务器性能评估
2011-04-29 14:17 4032工作这么久了,主 ... -
[zz]Top命令VIRT,RES,SHR,DATA的含义
2011-04-10 15:50 4133VIRT:virtual memory usage ... -
[zz]一些shell命令
2011-04-10 15:35 15191.显示消耗内存/CPU最多的10个进程 ps aux | ... -
Open VPN免密码配置
2011-03-03 22:55 3589公司VPN的帐号密码使用的是个超长的随机数,每次输入很麻烦,如 ... -
Linux下使用RTX腾讯通
2011-03-03 22:46 10795在公司为了使用RTX,专门安装了一个XP的虚拟机,但是这个也不 ... -
[zz]linux 常用命令总结:
2010-12-11 21:30 1700linux 常用命令总结: 一。 通用命令: 1. dat ... -
Ubuntu10.10解决Empathy无法链接MSN的问题
2010-10-21 16:36 37581.在Empathy中删除无法登录的MSN账户,并关闭Empa ... -
[zz]Vim的分屏功能
2010-10-21 13:09 1708分屏启动Vim 使用大写 ... -
Ubuntu10.10 64bit使用Eclipse插件subclipse问题
2010-10-20 20:32 1974升级到ubuntu10.10什么坑都碰到了,郁闷啊。发现sub ... -
scim输入法,Eclipse代码提示之后失去焦点的问题
2010-10-20 13:36 3095突然发现Eclipse,每次代码提示之后无法打字,感觉是文本域 ... -
Linux下使用谷歌输入法
2010-10-18 23:01 3149Linux的中文输入法一直太烂,scim终于出来对google ...
相关推荐
### Linux系统编程学习笔记 #### 一、IO **1.1 标准I/O (stdio)** - **fopen/fclose**: `fopen` 用于打开或创建一个文件,并返回一个指向该文件的 `FILE *` 类型的指针。`fclose` 用于关闭由 `FILE *` 指向的文件...
尚观Linux内核驱动开发笔记不仅涵盖了这些基础知识,还可能包含实践案例、常见问题解析以及高级技术探讨,例如异步I/O、内存管理优化、多线程同步等。通过学习和实践这些内容,开发者可以提升在Linux平台上的系统...
《高级Linux环境编程》或称为APUE(Advanced Programming in the UNIX ...这本书不仅涵盖了UNIX编程的核心概念,还包括了许多实用的编程技巧和解决方案,是一本非常适合在Linux环境下从事系统编程学习的参考书籍。
根据提供的文件信息,我们可以推断出这是一份关于Linux编程学习笔记的PDF资料。下面将对这份资料可能涉及的关键知识点进行详细的阐述。 ### Linux编程基础知识 #### 1. Linux操作系统概述 - **定义与特点**:Linux...
这部分涵盖了Linux系统编程中常见的文件和I/O操作函数,这些函数对于管理和操作文件至关重要。 ##### 1. `open` - **定义**:`#include<unistd.h>` `int open(const char *pathname, int flags);` - **作用**:...
本笔记主要涵盖了三个关键领域:文件系统操作、进程管理和高级IO(输入/输出)。 首先,让我们深入探讨文件系统操作。在Linux中,一切皆为文件,包括硬件设备。文件系统是组织这些文件的方式,它定义了如何存储、...
在进入UNIX系统编程的学习之前,我们首先需要理解UNIX的基本概念。UNIX是一种多用户、多任务的操作系统,由贝尔实验室在1960年代末开发。它以其简洁、强大的命令行接口和丰富的工具集而闻名,是许多现代操作系统设计...
Linux学习笔记是一个包含丰富内容的资源包,专为那些希望深入了解和掌握Linux操作系统的人们设计。Linux是一种开源、自由的类UNIX操作系统,被广泛应用于服务器、嵌入式设备以及个人计算机上。本笔记将带你踏上Linux...
5. **网络和I/O**:掌握网络编程的基本概念,如套接字编程,以及如何通过标准输入/输出和文件描述符进行I/O操作。 6. **系统调用与内核接口**:深入理解Linux内核,学习如何使用系统调用编写高效的程序。 压缩包中...
提供的压缩包文件包含一系列关于Linux系统编程的学习笔记,如线程、信号、文件I/O、进程管理、内存管理、进程间通信等主题,这些都是Linux初级学习的关键点。通过阅读这些笔记,可以深入理解Linux底层机制,为后续的...
这份“201808达内大数据Linux阶段学习课后笔记”涵盖了Linux基础到高级应用的诸多方面,旨在帮助学习者深入理解Linux系统及其在大数据环境中的应用。 笔记首先可能会介绍Linux的基础知识,包括Linux的历史、哲学...
这只是Linux学习笔记的冰山一角。Linux系统还包括包管理、网络配置、进程管理、权限控制、脚本编程等多个方面。熟练掌握这些基本命令和概念是成为Linux高手的第一步。随着经验的积累,你还可以学习更高级的主题,如...
通过以上内容,我们可以了解到Unix系统编程涉及到多个方面的知识,从操作系统的基本概念到具体的文件操作、进程管理等高级主题。掌握这些知识点对于深入理解Unix系统的工作原理以及编写高质量的应用程序至关重要。
### Linux设备驱动程序学习笔记知识点总结 #### 一、前言 - **学习背景**:作者自述已接触ARM-Linux九个月,并完成了基于不同硬件平台的系统移植工作,包括U-Boot、Linux内核以及特定硬件的驱动移植。这为后续深入...
在Linux操作系统中,掌握基本的命令和文件操作是至关重要的,因为它们构成了日常使用...随着经验的积累,你还可以探索更多高级功能,如文件权限的更细粒度控制、文件的权限变更、脚本编程等,进一步提升你的Linux技能。
标题"jz2440学习笔记"表明这是一个关于jz2440处理器的学习资源,通常jz2440是基于ARM体系架构的微控制器,常用于嵌入式系统的学习和开发。描述中提到了"思维导图"、"ARM体系架构学习"和"Linux内核驱动学习",这意味...
"Linux学习笔记.pdf":这可能是一份详尽的文档,包含了Linux的基础知识、命令行操作、系统管理、网络配置、软件安装、文件系统管理等多个方面。这份笔记可能涵盖了从初学者到高级用户所需的各种知识,帮助读者快速...
- **文件操作**:包括文件的打开、创建、读取、写入、同步等操作,以及相关的系统调用,如`open()`、`read()`、`write()`、`fsync()`等,展示了文件I/O的基本流程和高级技巧。 ### 关键知识点总结 1. **ARM9处理器...
3. **文件系统和I/O操作**:讨论文件描述符、打开/关闭文件、读写文件、标准输入输出以及文件权限等。 4. **网络编程**:讲解TCP/IP协议栈和套接字API,如何创建服务器和客户端应用程序。 5. **系统调用和库函数**...