Beginning Linux Programming 笔记(七)数据管理

    技术2022-05-20  49

    数据管理,这对每一个操作系统而言都是必须的。操作系统对数据的管理,可以分成三个方面,内存数据管理,文件管理和抽象数据管理。

    内存数据管理

    内存的管理应该对大部分C程序员都不陌生,对指针的内存分配是编程时经常会用到的。内存数据管理有下面几个主要函数:

    #include<stdlib.h>

    void *malloc(size_t size);

    void free(void *ptr_to_memory);

    void *calloc(size_t number_of_elements, size_t element_size);

    void *realloc(void *exist_memory, size_t newsize);

    有一个地方必须注意,有一些系统是用malloc.h文件来调用内存管理程序,我在window下面写C程序时就是用malloc.h这个文件。 malloc()函数会创建大小为size个字节的内存空间,free()函数可以释放内存。这一点可能就是C/C++一直被java取笑的地方,对于内存的处理,内存垃圾都需要程序员去释放处理,而java提供了强大的内存垃圾处理功能,大大减轻程序员的负担。其实这里并没有泼C/C++冷水的意思,只是阐述了C在内存处理方面的不足,但也是它的这种不足,这种面向底层的特点,才让C程序员对整个系统有绝对的控制权。像我们的java老师说过的一句话:能用java实现的东西都能用C实现。这也是C语言多年依旧在语言排行榜上面稳居前三的原因。

    扯得有点远了,回到我们的内存处理函数。calloc()与malloc的区别是calloc是用块的思想去分配的,程序员使用元素个数,还有每个元素单元大小去分配内存。本质上是一样的,calloc(n, m) 跟malloc(n*m)的作用是等效的。realloc()相当于先使用free(exist_memory),再malloc(newsize)。因此,使用malloc()跟free()就其实就足以覆盖内存处理的大部分功能了。

     

    文件锁

    对于文件的读写操作,有一个很重要的地方我们没提过,那就是读写冲突。假设有一种情况,A、B用户在想读写文件test,把里面的一个变量加1,他们都在对方写入硬盘之前读到变量,结果就造成了写入冲突。本来两个人执行的结果应该是加2,现在变成了加1。程序变得不可预测,所以我们需要去消解这种冲突。

    解决文件共享的冲突有两种方法,一种是使用信号量,后面14章会详细地叙述(不过我也不清楚我是否能看到第十四章,哈),另一种是使用文件锁,下面做具体介绍。

    #include <fcntl.h>

    int fcntl(int fildes, int command, struct flock *flock_structure);

    fildes是文件的id,也即是这个函数只能适用于用底层文件操作。

    command是一个常量,有三个选项,表示文件锁控制行为

    F_GETLK     (get lock)                        读取文件锁

    F_SETLK     (set lock)               设置文件锁

    F_SETLKW   (set lock or wait)        设置文件锁,如果不成功,则等待

    struct flock是文件锁的结构体

    short l_type;      F_RDLCK(读文件锁) F_UNLCK(解锁)  F_WRLCK(写文件锁)

    short l_whence;  SEEK_SET, SEEK_CUR, 或 SEEK_END,参考前面底层文件操作

    short l_start;        文件锁起点

    short l_len;       文件锁长度

    short l_pid;       文件锁进程id

     

    下面有两个例子:

    region_1.l_type = F_RDLCK;

    region_1.l_whence = SEEK_SET;

    region_1.l_start = 10;

    region_1.l_len = 20;

    res = fcntl(file_desc, F_SETLK, ®ion_1);

    对文件10-30字节这个区间增加一个读文件锁

     

    region_2.l_type = F_WRLCK;

    region_2.l_whence = SEEK_SET;

    region_2.l_start = 40;

    region_2.l_len = 10;

    res = fcntl(file_desc, F_SETLK, ®ion_2);

    对文件40-50字节这个区间增加一个写文件锁。

     

    读文件锁跟写文件锁是不一样的。对于读文件,我们可以允许多个程序同时读,但是此时,写入明显就是不妥的。对于一个进程在写文件时,读写文件都是不允许的,都有可能造成冲突。

    要注意的是,文件锁只是返回逻辑判断的结果,而没有真正限制到读写函数本身如何去执行。也即是文件锁提供的只是一个读写冲突的建议,具体如何操作那就要看你程序怎么做了。

     

    dbm数据库

    Linux大部分都提供了dbm数据管理工具,这个一个非结构化查询语言(noSQL)。个人感觉它是SQL的雏形,思想上面有挺多地方是一致的。

    dbm数据管理的思想是给数据建立索引,做数据查询时可以通过索引去查询,而不是完全扫描整行记录。并且,系统把插入跟查询都给封装起来,让用户跟方便检索。

    struct datum {

      void *dptr;        //数据指针

      size_t dsize;       //数据长度

    };

    #include <gdbm-ndbm.h>  /* 有些系统使用 #include<ndbm.h> */

    DBM *dbm_open(const char *filename, int file_open_flags, mode_t file_mode);  创建

    int dbm_store(DBM *database_descriptor, datum key, datum content, int store_mode)

    插入数据,key存储的是索引,content是存储数据。

    datum dbm_fetch(DBM *database_descriptor, datum key);     检索,key对应索引数据

    void dbm_close(DBM *database_descriptor)                关闭

    对于DBM类型的创建,跟文件创建是相似的,我就不多说了。

    数据的存储有一个参数,store_mode,控制着存储行为的处理。当值为store_insert,遇到索引重复时也会插入数据;当值为store_replace时,遇到索引重复时会直接替代,而不是插入一条新数据。因此,这个函数也可以用来更新数据,功能跟MYSQL的update类似。

     

    对于文件的编译,由于引入了一些库,所以要加一些选项跟参数。

    编译 dbm1.c这个文件的例子:

    使用ndbm.h库的用户:

    gcc –o dbm1 –I/usr/include/gdbm dbm1.c –lgdbm

    使用gdbm-ndbm.h库的用户:

    gcc –o dbm1 –I/usr/include/gdbm dbm1.c –lgdbm –lgdbm_compat

     

    上面就是我关于数据管理一章的一些总结,说得不对的地方,还请大家多多指出。


    最新回复(0)