而手工管理,则是可以准确的知道资源的生命期,在准确的位置回收它。在 C++ 中,体现在析构函数中写明 delete 用到的资源,并由编译器自动生成的代码析构基类和成员变量。
所以,为 C++ 写一个垃圾收集器,并不和手工管理资源冲突。自动化管理几乎在所有有点规模的 C++ 工程中都在使用,只不过用的是引用计数的策略而非垃圾收集而已。也就是说,我们使用 C++ 或 C 长期以来就是结合了手工管理和自动管理在构建系统了。无论用引用计数,还是用垃圾收集,软件实现的细节上,该手工管理的地方我们依旧可以手工管理。
UNIX访问文件的传统方法是用 open 打开它们, 如果有多个进程访问同一个文件,则每一个进程在自己的地址空间都包含有该文件的副本,这不必要地浪费了存储空间。下图说明了两个进程同时读一个文件的同一页的情形。系统要将该页从磁盘读到高速缓冲区中,每个进程再执行一个存储器内的复制操作将数据从高速缓冲区读到自己的地址空间。
二、共享存储映射
现在考虑另一种处理方法:进程 A 和进程 B 都将该页映射到自己的地址空间,当进程 A 第一次访问该页中的数据时,它生成一个缺页中断。内核此时读入这一页到内存并更新页表使之指向它。以后,当进程 B 访问同一页面而出现缺页中断时,该页已经在内存,内核只需要将进程 B 的页表登记项指向次页即可。如下图所示:
if(fork() == 0) { sleep(1); printf("child got a message: %s\n", p_map); sprintf(p_map, "%s", "hi, dad, this is son"); munmap(p_map, BUF_SIZE); //实际上,进程终止时,会自动解除映射。 exit(0); }
sprintf(p_map, "%s", "hi, this is father"); sleep(2); printf("parent got a message: %s\n", p_map);
return0; }
七、对 mmap() 返回地址的访问
linux 采用的是页式管理机制。对于用 mmap() 映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由 mmap() 的 len 参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从 mmap() 返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。可用如下图示说明:
总结一下就是,文件大小,mmap 的参数 len 都不能决定进程能访问的大小,而是容纳文件被映射部分的最小页面数决定进程能访问的大小。下面看一个实例:
# import the necessary packages from PIL import Image import imagehash import argparse import shelve import glob
# construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-d", "--dataset", required = True, help = "path to input dataset of images") ap.add_argument("-s", "--shelve", required = True, help = "output shelve database") args = vars(ap.parse_args())
# loop over the image dataset for imagePath in glob.glob(args["dataset"] + "/*.jpg"): # load the image and compute the difference hash image = Image.open(imagePath) h = str(imagehash.dhash(image))
# extract the filename from the path and update the database # using the hash as the key and the filename append to the # list of values filename = imagePath[imagePath.rfind("/") + 1:] db[h] = db.get(h, []) + [filename]
# import the necessary packages from PIL import Image import imagehash import argparse import shelve
# construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-d", "--dataset", required = True, help = "path to dataset of images") ap.add_argument("-s", "--shelve", required = True, help = "output shelve database") ap.add_argument("-q", "--query", required = True, help = "path to the query image") args = vars(ap.parse_args())
# open the shelve database db = shelve.open(args["shelve"])
# load the query image, compute the difference image hash, and # and grab the images from the database that have the same hash # value query = Image.open(args["query"]) h = str(imagehash.dhash(query)) filenames = db[h] print"Found %d images" % (len(filenames))
# loop over the images for filename in filenames: image = Image.open(args["dataset"] + "/" + filename) image.show()