《Linux程序设计》学习笔记07——数据管理

来源:互联网 发布:店老大软件 编辑:程序博客网 时间:2024/04/30 00:00

 

内存管理

Linux为应用程序提供了一个简洁的视图,它能反映一个巨大的可直接寻址的内存空间。此外,Linux还提供了内存保护机制,它避免了不同的应用程序之间的互相干扰。

我们使用mallocfree函数来完成动态内存的分配和释放。与DOS下的程序不能访问超过640K的内存相比,在Linux系统上使用malloc可以开辟兆字节的内存空间。实际上,在Linux系统上使用malloc可以申请的内存也是有限的:当申请的内存空间足够大时,物理内存将耗尽,此时内核将会开始使用所谓的交换空间(swap space);当物理内存和交换空间都耗尽时,或者栈超出了最大长度时,内核将拒绝内存申请并可能提前终止程序。

提示Linux擅长管理内存,它允许应用程序使用数量非常巨大的内存,甚至使用一个单独的非常大的内存块。但是,必须要记住的是:分配了两块内存并不见得肯定能够得到一个单独的可以连续寻址的内存块,而很有可能是两个分开的内存块。

注意:由于malloc函数返回的是一个void*指针,因此我们需要通过类型转换,将其转换至我们需要的指针类型。实际上,malloc函数可以保证其返回的内存是地址对齐的(很有可能是4字节对齐),因此它返回的指针可以转换为任何类型。

空指针:当使用malloc开辟内存时,往往需要测试返回值是否是空指针。这里要说的是:空指针并非等价于(void *)0,实际上“空指针的内部(或运行期)表达形式很可能并不是全零,而且对不同的指针类型可能不一样”。因此,不要想当然地把NULL看成零值,也尽量不要再使用if(!ptr)方式来测试malloc函数的返回值,应该使用if(ptr==NULL)

 

文件锁定

Linux提供了多种特性来实现文件锁定。最简单的方法是以原子操作的方式创建锁文件,所谓“原子操作”就是在创建锁文件时,系统将不允许任何其他的事情发生。

锁文件仅仅只是充当一个指示器的角色,程序间需要通过相互协作来使用它们。为了创建一个用作锁指示器的文件,使用在fcntl.h文件中定义的带O_CREATO_EXCL标志的open系统调用。

注意:由上述锁文件方式建立的文件严格仅属于创建它的进程,也就是说,即使使用open系统调用创建该锁文件的进程调用close关闭了该文件,其他进程也再无法使用open系统调用打开该文件。

实际上,用创建锁文件的办法来控制诸如串行口之类的资源的独占式访问时一个不错的选择,但它并不适用于大型的共享文件。这种情况下,我们可以通过文件中的锁定区域来解决这个问题:文件的一部分被锁定,但其他程序可以访问这个文件的其他部分。这杯称为文件段锁定或文件区锁定。

Linux提供了至少两种方式来完成这一工作:使用fcntl系统调用和使用lockf调用。其中fcntl系统调用时最常使用的方式。而使用fcntl对文件锁定的操作在《精通UNIXC语言编程与项目实践》的学习笔记3中有详细的解释。

注意fcntllockf的锁定机制不能同时工作。它们使用不同的底层实现,因此你决不能混合使用两种类型的调用,而应该坚持使用其中的一种。

提示:当对文件的区域加锁之后,访问文件中的数据应该使用底层的readwrite调用,而不要使用高级的freadfwrite函数,这一点是非常重要的。

 

dbm数据库

所有版本的Linux以及大多数的UNIX版本都随系统带有一个基本的,但却非常高效的数据存储的例程集,称为dbm数据库。dbm数据库适合于存储相对比较静态的索引化数据。

dbm数据库的优点是它非常容易被编译进一个可发布的二进制可执行程序,因为它无需安装独立的服务器,而且即使它需要的底层库文件还未被安装,也不会有什么危险。

dbm数据允许通过使用索引来存储可变长的数据结构,然后通过索引或简单的顺序扫描数据库来检索结构。dbm数据适用于处理那些被频繁访问但却很少被更新的数据,因为它创建数据项时非常慢,而检索时却非常快。

dbm数据有各种各样不同的版本,这里重点介绍ndbm接口。

dbm数据库的基本元素是需要储存的数据块以及与它关联的在检索数据时用作关键字的数据块。每个dbm数据库必须有一个针对每个要处处的数据块的唯一的关键字。为了操纵这些数据块,头文件ndbm.h定义了一个名为datum的新数据类型。该类型确切的定义依赖于具体实现,但至少包含下面这些成员:

        void *dptr;    // 数据的起点

        size_t dsize;   // 数据的长度

当我们打开一个dbm数据库时,将创建两个物理文件,它们的后缀分别是.pag.dir,而仅仅返回一个DBM类型指针,它被用来访问这两个文件。这两个文件不应该被直接读写,对它们的访问一定要通过dbm例程来进行。类似于FILE类型,DBM类型用来访问数据库的结构。下面给出一个典型的dbm使用例程:

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <ndbm.h>

#include <assert.h>

#include <string.h>

 

#define DB_FILE "/tmp/dbm_test"  // 定义了dbm数据库实际存在的位置和名字

#define ITEMS 2

 

struct test_data{      // 自定义的数据块结构

        char misc[15];

        int value;

        char more[21];

};

 

int main()

{

        struct test_data items[ITEMS];

        struct test_data item_got;

 

        char key_to_use[20];

        int i, result;

 

        datum key_datum;

        datum data_datum;

 

        DBM *pDbm;

 

        pDbm = dbm_open(DB_FILE, O_RDWR | O_CREAT, 0666); // 打开dbm数据库

        assert(pDbm != (DBM *)0);

 

        memset(items, '/0', sizeof(items));    // 预先安排好需要写入的数据块

        strncpy(items[0].misc, "First!", 15);

        items[0].value = 47;

        strncpy(items[0].more, "foo", 21);

 

        strncpy(items[1].misc, "Second!", 15);

        items[1].value = 13;

        strncpy(items[1].more, "bar", 21);

 

        for(i=0;i<ITEMS;i++){

                sprintf(key_to_use, "%c%c%d", items[i].misc[0], items[i].more[0], items[i].value);  // 自行定义关键字的模式

                key_datum.dptr = (void *)key_to_use;

                key_datum.dsize = strlen(key_to_use);

                data_datum.dptr = (void *)&items[i];

                data_datum.dsize = sizeof(struct test_data);

                     // 写入数据块

                result = dbm_store(pDbm, key_datum, data_datum, DBM_REPLACE);

                assert(result == 0);

        }

 

        sprintf(key_to_use, "Ff%d", 47);

        key_datum.dptr = (void *)key_to_use;

        key_datum.dsize = strlen(key_to_use);

        data_datum = dbm_fetch(pDbm, key_datum);  // 读取数据块

        if(data_datum.dptr){

                printf("Data got/n");

                memcpy(&item_got, data_datum.dptr, data_datum.dsize);

                printf("Get item - %s %d %s/n", item_got.misc, item_got.value, item_got.more);

        } else

                printf("No data found for key %s/n", key_to_use);

 

        dbm_close(pDbm); // 关闭dbm数据库

 

        exit(0);

}

上面程序中,我们先存储了两个数据块,继而按关键字读取一个数据块。

首先,我们将需要存储的数据定义为test_data结构。dbm数据库的打开和关闭使用dbm_opendbm_close函数来完成。dbm_store函数用于向一个打开的数据库写入一个数据块,第一个参数指定了打开的dbm数据库对应的DBM指针,第二、三个参数指明了数据块及其关键字对应的datum结构变量,第四个参数则指明了操作类型(常用DBM_REPLACE)。每个数据块对应的关键字可以是任意形式的,但在实际编程中最好统一模式。函数dbm_fetch用来从打开的dbm数据库中提取一个数据块,第二个参数指明了需要提取数据块的关键字。

至于其他的dbm函数可以自行查看手册。本章最后展示了CD唱片应用程序的完整代码。它详细地描述了如何使用dbm数据库来完成数据的存储和访问,为我们自己设计使用dbm数据库带来了很大的帮助。

原创粉丝点击