MMAP和结构用C [英] Mmap and struct in C

查看:219
本文介绍了MMAP和结构用C的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在C.结果MMAP读写结构
我有一个名为insert_med函数,它允许一个新的结构的插入的 MED 的进MMAP和各结构(具有独特的的)已被写入在不同的位置阵列(当添加了新的结构,它具有在所述阵列的最后一个空的位置被添加)。结果,
两个结构 MED 的不能有相同的的,你可以在code波纹管见。在的是独一无二的。结果
我的code不工作 - 错误信息的变量结构图:宣布并在文件已准备好mmapped时 - 但我不知道为什么。我想我可能做一些错误的方式。

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&; SYS / stat.h>
#包括LT&;&unistd.h中GT;
#包括LT&;&fcntl.h GT;
#包括LT&; SYS / mman.h>#定义FILEPATH/tmp/mmapped.bin
#定义NUMINTS(1000)
#定义FILESIZE(NUMINTS *的sizeof(int)的)结构MED {
    INT关键;
    焦炭名[25];
    INT quant_min;
    INT定量;
};insert_med(INT ARGC,CHAR *的argv []){
    INT I;
    INT的fd;
    INT结果;
    结构图*; / * mmapped结构的数组* /    / *打开文件进行写入。
    * - 创建文件,如果它不存在。
    * - 如果它已经存在,将其截断为0大小。 (不是真的需要)
    *
    *注:O_WRONLY模式mmaping时是不够的。
    * /
    FD =开(FILEPATH,O_RDWR | O_CREAT | O_TRUNC,(mode_t)0600);
    如果(FD == -1){
    PERROR(错误打开文件进行写入);
    出口(EXIT_FAILURE);
    }    / *伸展文件大小结构的(mmapped)阵列的大小
     * /
    结果= lseek的(FD,FILESIZE-1,SEEK_SET);
    如果(结果== -1){
    关闭(FD);
    PERROR(错误调用lseek的()为拉伸文件);
    出口(EXIT_FAILURE);
    }    / *东​​西需要在文件的结尾被写入
     *具有文件居然有新的大小。
     *只是写在当前文件位置会做一个空字符串。
     *
     * 注意:
     * - 在该文件中的当前位置是在拉伸的端
     *由于调用lseek的文件()。
     * - 一个空字符串实际上是一个单一的'\\ 0'字符,因此一个零字节
     *将在文件的最后一个字节被写入。
     * /
    结果=写入(FD,,1);
    如果(结果!= 1){
    关闭(FD);
    PERROR(错误写入文件的最后一个字节);
    出口(EXIT_FAILURE);
    }    / *现在文件已准备好mmapped。
     * /
    地图=(结构*)MMAP(0,FILESIZE,PROT_READ | PROT_WRITE,MAP_SHARED,FD,0);
    如果(图== MAP_FAILED){
    关闭(FD);
    PERROR(错误mmapping文件);
    出口(EXIT_FAILURE);
    }    结构体MED米;
    结构体MED S;
    INT A = 0;    的printf(重点配有:);
    scanf函数(%d个,&安培; m.key);    //返回的strcmp0的时候,两个字符串相等
    对于(i = 0; I< = NUM​​INTS ++我){    INT A = 0;
    图[i] =&放大器; S;    读(FD,&放大器; S,1);    如果((STRCMP(安培; m.key,&安培; s.key))== 0){
        一个++;
        的printf(医学%d个已经存在\\ n,s.key);
        打破;
    }
    }    如果(A == 0){
    的printf(名称配有:);
    scanf函数(%S,&安培; m.name);
    的printf(定量分钟的MED属性:);
    scanf函数(%d个,&安培; m.quant_min);
    的printf(定量的配有:);
    scanf函数(%d个,&安培; m.quant);    图[i] =&放大器;米;    写(FD,与安培;男,1);
    的printf(医学%d个保存\\ n,m.key);
    }    / *不要忘了释放内存mmapped
     * /
    如果(在munmap(地图,FILESIZE)== -1){
    PERROR(错误未mmapping文件);
    / *在这里决定是否关闭(FD)和退出()或没有。依靠... */
    }    / *取消mmaping不关闭文件,所以我们还需要做到这一点。
     * /
    关闭(FD);
}


解决方案

在您的code,你有:

  INT的结果;
结构图*; / * mmapped结构的数组* /

您previously定义结构MED (而不是结构图),并且您试图定义一个键入结构图* 的变量,但给它没有名字。编译器是正确的抱怨。你可能需要类似:

  INT的结果;
结构体中*地图= 0; / *结构的mmapped数组中* /


朱莉娅 <一个href=\"http://stackoverflow.com/questions/27697228/mmap-and-struct-in-c/27697316#comment43808020_27697316\">commented:


  

我现在有以下错误:


  
  

      
  • 无效使用不确定型结构配有的

  •   
  • deferencing指向不完全类型

  •   

  
  

地图[I] =&放大器; S 地图[I] =安培;:M


好吧,我不看远远超出了第一个错误,哪来的有一,通常有很多的。

例如:

 地图=(结构*)MMAP(0,FILESIZE,PROT_READ | PROT_WRITE,MAP_SHARED,FD,0);

需要投改为(结构中*)

 地图[I] =&放大器; S;

是错误的。语义,地图[I] = S; 将是正确的(从复制数据S 进入映射数组) - 但是,转念一想,分配是完全错误的。不这样做;删除该行。

FILESIZE 也许应该是的sizeof的倍数(结构图),而不是<$ C的倍数$ C>的sizeof(INT)。

阅读() FD 是可疑充其量 - 删除; 的mmap()的整点是为了避免直接文件I / O。

该键值是整数;你不使用的strcmp()来比较它们,你只需使用:

 如果(m.key ==地图[I] .KEY)

分配地图[I] =&放大器;米; 是错误的,再次,应予以删除,而的write()是错误的。你有一个数组,地图,你访问它像使用任何其他数组,系统处理幕后的I / O。请注意,您应该检查阅读()的write()操作的工作,但是当你删除变得毫无意义这些操作无妨。

您需要检查值如何分配到地图阵列,并确保你不尝试从初始化值读地图阵列。

有可能仍然是其他问题;我没有编译,更不用说跑时,code。但这些意见应帮助你对你的方式。

请注意,从编译器的错误信息应该帮助过;你需要学习如何跨preT他们。确保你有足够的警告,编译和你正在修复所有的警告。如果你使用GCC,那么的gcc -Wall -Wextra -Werror -std = C11 是一个相当不错的开始(我通常添加了一堆关于原型和函数定义警告过 - 其中一些,但不是全部,都包括在上述的)。请记住,C编译器知道了很多关于C比你在这个阶段你的职业生涯做;听其警告小心,因为你应该假设它是正确的,你犯了一个错误。这就是我做的,我已经用C语言为超过30年,现在被编码。


工作code mm7.c

创建的程序是 MM7 (记忆7;有7其他比它是素数,小的没有意义,使名称唯一)。

在code相当数量的留作写的问题,而不是从头重写。请注意,code是仔细验证输入 - 它检测并捞出上EOF,而不是轻率地继续无所事事有用的,例如。它包括一个输出回路以打印存储的数据。它使用功能来管理输入的部分。它会更好,如果所有的输入是在函数处理。

 的#include&LT; fcntl.h&GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;
#包括LT&; SYS / mman.h&GT;
#包括LT&; SYS / stat.h&GT;
#包括LT&;&unistd.h中GT;#定义FILEPATH/tmp/mmapped.bin
#定义NUMINTS(1000)
#定义FILESIZE(NUMINTS *的sizeof(结构MED))结构MED
{
    INT关键;
    焦炭名[25];
    INT quant_min;
    INT定量;
};print_med静态无效(为const char *标记,常量结构中* MED)
{
    的printf(%S:%4D:Q(%2D,分2D%):%S \\ n,
           标签,med-&GT;的关键,med-&GT;定量,med-&GT; quant_min,med-&GT;名);
}静态INT med_in_map(常量结构中*地图,诠释num_meds,INT键)
{
    的for(int i = 0; I&LT; num_meds ++ I)
    {
        如果(键==地图[I] .KEY)
        {
            的printf(医学%d个已经存在\\ n,键);
            返回1;
        }
    }
    返回0;
}静态INT get_new_key(常量结构中*地图,诠释num_meds,为int *键)
{
    而((重点配有:printf的)大于0&放大器;&放大器; scanf的(%d个键)== 1)
    {
        如果(med_in_map(地图,num_meds,*键)== 0)
            返回0;
    }
    返回EOF;
}INT主要(无效)
{
    INT的fd;
    INT结果;
    结构体中*地图; / * mmapped结构的数组* /    / *打开文件进行写入。
     * - 创建文件,如果它不存在。
     * - 如果它已经存在,将其截断为0大小。 (不是真的需要)
     *
     *注:O_WRONLY模式mmapping时是不够的。
     * /
    FD =开(FILEPATH,O_RDWR | O_CREAT | O_TRUNC,(mode_t)0600);
    如果(FD == -1)
    {
        PERROR(错误打开文件进行写入);
        出口(EXIT_FAILURE);
    }    / *注:ftruncate(FD,FILESIZE);更简单* /
    / *伸展文件大小结构的(mmapped)数组的大小* /
    结果= lseek的(FD,FILESIZE - 1,SEEK_SET);
    如果(结果== -1)
    {
        关闭(FD);
        PERROR(错误调用lseek的()为拉伸文件);
        出口(EXIT_FAILURE);
    }    / *东​​西需要在文件的结尾被写入
     *具有文件居然有新的大小。
     *只是写在当前文件位置会做一个空字符串。
     *
     * 注意:
     * - 在该文件中的当前位置是在拉伸的端
     *由于调用lseek的文件()。
     * - 一个空字符串实际上是一个单一的'\\ 0'字符,因此一个零字节
     *将在文件的最后一个字节被写入。
     * /
    结果=写入(FD,,1);
    如果(结果!= 1)
    {
        关闭(FD);
        PERROR(错误写入文件的最后一个字节);
        出口(EXIT_FAILURE);
    }    / *现在文件已准备好mmapped。 * /
    地图=(结构中*)MMAP(0,FILESIZE,PROT_READ | PROT_WRITE,MAP_SHARED,FD,0);
    如果(图== MAP_FAILED)
    {
        关闭(FD);
        PERROR(错误mmapping文件);
        出口(EXIT_FAILURE);
    }    / *输入回路* /
    INT num_meds;
    为(num_meds = 0; num_meds&下; NUMINTS; num_meds ++)
    {
        结构体MED米;
        memset的(安培;男,'\\ 0',sizeof的(M));        如果(get_new_key(地图,num_meds,&安培; m.key)== EOF)
            打破;        的printf(名称配有:);
        如果(scanf函数(%S,m.name)!= 1)
            打破;
        的printf(定量分钟的MED属性:);
        如果(scanf的(%d个,&放大器;!m.quant_min)= 1)
            打破;
        的printf(定量的配有:);
        如果(scanf的(%d个,&放大器;!m.quant)= 1)
            打破;        图[num_meds] =米;        的printf(医学%d个保存\\ n,m.key);
    }    / *输出回路* /
    的printf(\\ nRecorded吃药:\\ n);
    的for(int i = 0; I&LT; num_meds;我++)
    {
        字符缓冲区[32];
        的snprintf(缓冲液,的sizeof(缓冲液)中,M%.4d,i)的;
        print_med(缓冲,和放大器;图[I]);
    }    / *不要忘了释放内存mmapped * /
    如果(在munmap(地图,FILESIZE)== -1)
    {
        PERROR(错误未mmapping文件);
        / *在这里决定是否关闭(FD)和退出()或没有。依靠... */
    }    / *取消mmapping不关闭文件,所以我们还需要做到这一点。 * /
    关闭(FD);    / *删除这个文件? * /
    / *取消链接(FILEPATH); * /
}

例输入数据文件 mm7.data

本模拟端子输入的号码试图重复。

  1 Hydrocontin 3 5
1
2 Paxodontin 1 1
2
37布洛芬2 12
129 Butoxydione 12月29日
4安慰剂2 22
37
129
4
2
1
9231阿司匹林99 99

实例与数据文件运行:

  $ ./mm7&LT; mm7.data
MED值名称:MED属性关键定量。分钟。 MED值:定量。 MED值:医学1中保存。
MED的关键:1地中海已经存在。
MED值名称:MED属性关键定量。分钟。 MED值:定量。 MED值:地中海2保存。
MED的关键:地中海2已经存在。
MED值名称:MED属性关键定量。分钟。 MED值:定量。 MED值:医学37保存。
MED值名称:MED属性关键定量。分钟。 MED值:定量。 MED值:129地中海保存。
MED值名称:MED属性关键定量。分钟。 MED值:定量。 MED值:医学4中保存。
MED属性键:地中海37已经存在。
MED的重点:地中海129已经存在。
MED的关键:医学4已经存在。
MED的关键:地中海2已经存在。
MED的关键:1地中海已经存在。
MED值名称:MED属性关键定量。分钟。 MED值:定量。 MED值:医学9231保存。
MED的重点:
记录吃药:
M0000:1:Q(5分3):Hydrocontin
M0001:2:Q(1分钟1):Paxodontin
M0002:37:Q(12分钟2):布洛芬
M0003:129:Q(29岁,最小12):Butoxydione
M0004:4:Q(22分钟2):安慰剂
M0005:9231:Q(99分钟99):阿司匹林
$

这些提示都在一行上的每个实际用药,因为输入是从文件中读取,而不是从键盘。

数据文件的十六进制转储 /tmp/mmapped.bin

  $ ODX /tmp/mmapped.bin
0×0000:01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00 .... Hydrocontin。
0×0010:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0×0020:03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F ............ Paxo
0x0030:64 6F 6E 74 69 6E 00 00 00 00 00 00 00 00 00 00 dontin ..........
×0040:00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 ................
0x0050:25 00 00 00 49 62 75 70 72 66 6F 6E 65 00 00 00%... ...布洛芬
0x0060:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0070:02 00 00 00 0C 00 00 00 81 00 00 00 42 75 74 6F ............布托
0x0080:78 79 64 69 6F 6E 65 00 00 00 00 00 00 00 00 00 xydione .........
0x0090:00 00 00 00 00 00 00 00 0C 00 00 00 1D 00 00 00 ................
0x00A0:04 00 00 00 50 6C 61 63 65 62 6F 00 00 00 00 00 ....安慰剂.....
0x00B0:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00C0:02 00 00 00 16 00 00 00 0F 24 00 00 41 73 70 69 ......... .. $ ASPI
0x00D0:72 69 6E 00 00 00 00 00 00 00 00 00 00 00 00 00 RIN .............
0x00E0:00 00 00 00 00 00 00 00 63 00 00 00 63 00 00 00 ........ç。C ...
0x00F0:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*(2484)
0x9C40:
$

memset的()加入,因为十六进制转储显示的数据结构中的垃圾 - 垃圾无害的,但尽管如此,垃圾:

  0×0000:01 00 00 00 48 79 64 72 63 6F 6E 6F 74 69 00 6E .... Hydrocontin。
0×0010:2F 62 69 6E 3A 2F 75 73 72 67 2F 75 6E 62 2F 69 /斌:在/ usr / GNU / BI
0×0020:03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F ............ Paxo
0x0030:64 6F 6E 74 69 00 6E 00 62 2F 69 6E 3A 2F 75 73 dontin ../斌:/我们
×0040:72 2F 67 6E 75 62 2F 69 01 00 00 00 01 00 00 00转/ GNU / BI ........
...

与初始化结构:

 结构MED M = {.KEY = 0。名称=,.quant = 0,.quant_min = 0};

并没有完全消除,因为(这让我吃惊!)填充字节结构中的名称后的垃圾:

  0×0000:01 00 00 00 48 79 64 72 63 6F 6E 6F 74 69 00 6E .... Hydrocontin。
0×0010:00 00 00 00 00 00 00 00 00 00 00 00 00 2F 62 69 ............. /双向
0×0020:03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F ............ Paxo
0x0030:64 6F 6E 74 69 6E 00 00 00 00 00 00 00 00 00 00 dontin ..........
×0040:00 00 00 00 00 2F 62 69 01 00 00 00 01 00 00 00 ... /双向........
...


具有非空文件工作

在code不再包括 O_TRUNC 创建时的文件,但是,使硬codeD文件名不太合适的(但我已经不固定的)。在 find_num_entries()函数只是不通过数据的线性搜索,找到那里的名单不放过最后一次。更复杂的code会做一个二进制搜索通过数据寻找到空条目开始。还有在查找钥匙线性搜索。这将是更好地保持在文件中有序,使二进制搜索可用于这一点。事实上,它可能是明智的有在生产的版本,有一个神奇的数字来识别文件类型(如果文件不正确的幻数开始头记录,这显然不是一个文件,这个程序写,所以它的内容不应该是跨preTED),并记录其他细节,如参赛人数和可能的最小和最大键值。

 的#include&LT; fcntl.h&GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;
#包括LT&; SYS / mman.h&GT;
#包括LT&; SYS / stat.h&GT;
#包括LT&;&unistd.h中GT;#定义FILEPATH/tmp/mmapped.bin
#定义NUM_MEDS(1000)
#定义FILESIZE(NUM_MEDS *的sizeof(结构MED))结构MED
{
    INT关键;
    焦炭名[25];
    INT quant_min;
    INT定量;
};print_med静态无效(为const char *标记,常量结构中* MED)
{
    的printf(%S:%4D:Q(%2D,分2D%):%S \\ n,
           标签,med-&GT;的关键,med-&GT;定量,med-&GT; quant_min,med-&GT;名);
}静态INT med_in_map(常量结构中*地图,诠释num_meds,INT键)
{
    的for(int i = 0; I&LT; num_meds ++ I)
    {
        如果(键==地图[I] .KEY)
        {
            的printf(医学%d个已经存在\\ n,键);
            返回1;
        }
    }
    返回0;
}静态INT get_new_key(常量结构中*地图,诠释num_meds,为int *键)
{
    而((重点配有:printf的)大于0&放大器;&放大器; scanf的(%d个键)== 1)
    {
        如果(med_in_map(地图,num_meds,*键)== 0)
            返回0;
    }
    返回EOF;
}静态INT find_num_entries(常量结构中*地图,诠释max_meds)
{
    INT I;
    对于(i = 0; I&LT; max_meds;我++)
    {
        如果(图[I] .KEY == 0)
            打破;
    }
    返回我;
}INT主要(无效)
{
    INT的fd;
    INT结果;
    结构体中*地图;    FD =开(FILEPATH,O_RDWR | O_CREAT,(mode_t)0600);
    如果(FD == -1)
    {
        PERROR(错误打开文件进行写入);
        出口(EXIT_FAILURE);
    }    结果= ftruncate(FD,FILESIZE);
    如果(结果== -1)
    {
        关闭(FD);
        PERROR(错误调用ftruncate()为设置文件大小);
        出口(EXIT_FAILURE);
    }    地图=(结构中*)MMAP(0,FILESIZE,PROT_READ | PROT_WRITE,MAP_SHARED,FD,0);
    如果(图== MAP_FAILED)
    {
        关闭(FD);
        PERROR(错误mmapping文件);
        出口(EXIT_FAILURE);
    }    / *输入回路* /
    INT num_meds;
    对于(num_meds = find_num_entries(地图,NUM_MEDS); num_meds&LT; NUM_MEDS; num_meds ++)
    {
        结构体MED米;
        memset的(安培;男,'\\ 0',sizeof的(M));        如果(get_new_key(地图,num_meds,&安培; m.key)== EOF)
            打破;        的printf(名称配有:);
        如果(scanf函数(%S,m.name)!= 1)
            打破;
        的printf(定量分钟的MED属性:);
        如果(scanf的(%d个,&放大器;!m.quant_min)= 1)
            打破;
        的printf(定量的配有:);
        如果(scanf的(%d个,&放大器;!m.quant)= 1)
            打破;        图[num_meds] =米;        的printf(医学%d个保存\\ n,m.key);
    }    / *输出回路* /
    的printf(\\ nRecorded吃药:\\ n);
    的for(int i = 0; I&LT; num_meds;我++)
    {
        字符缓冲区[32];
        的snprintf(缓冲液,的sizeof(缓冲液)中,M%.4d,i)的;
        print_med(缓冲,和放大器;图[I]);
    }    / *不要忘了释放内存mmapped * /
    如果(在munmap(地图,FILESIZE)== -1)
    {
        PERROR(错误未mmapping文件);
        / *在这里决定是否关闭(FD)和退出()或没有。依靠... */
    }    / *取消mmapping不关闭文件,所以我们还需要做到这一点。 * /
    关闭(FD);
    返回0;
}

您还可以使错误使用类似的功能,以 err_syserr(),你可以在我的答案,如<一个发现简单的报告href=\"http://stackoverflow.com/questions/27714834/c-creating-a-file-with-given-size/27725619#27725619\">Creating给定大小的文件。

I would like to read and write structs with mmap in C.
I have a function named insert_med which allows the insertion of a new struct med into the mmap and each struct (with a unique key) has to be written in a different position of the array (when a new struct is added, it has to be added in the last empty position of the array).
Two structs med CAN'T have the same key as you can see in the code bellow. The key is unique.
My code isn't working - error messages with the variable "struct map": when declared and when the file is ready to be mmapped - but I don't know why. I think I'm probably doing something the wrong way.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

#define FILEPATH "/tmp/mmapped.bin"
#define NUMINTS (1000)
#define FILESIZE (NUMINTS * sizeof(int))

struct med{
    int key;
    char name[25];
    int quant_min;
    int quant;
};

insert_med(int argc, char *argv[]){
    int i;
    int fd;
    int result;
    struct map*;  /* mmapped array of structs */

    /* Open a file for writing.
    *  - Creating the file if it doesn't exist.
    *  - Truncating it to 0 size if it already exists. (not really needed)
    *     
    * Note: "O_WRONLY" mode is not sufficient when mmaping.
    */
    fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
    if (fd == -1) {
    perror("Error opening file for writing");
    exit(EXIT_FAILURE);
    }

    /* Stretch the file size to the size of the (mmapped) array of structs
     */
    result = lseek(fd, FILESIZE-1, SEEK_SET);
    if (result == -1) {
    close(fd);
    perror("Error calling lseek() to 'stretch' the file");
    exit(EXIT_FAILURE);
    }

    /* Something needs to be written at the end of the file to
     * have the file actually have the new size.
     * Just writing an empty string at the current file position will do.
     *
     * Note:
     *  - The current position in the file is at the end of the stretched 
     *    file due to the call to lseek().
     *  - An empty string is actually a single '\0' character, so a zero-byte
     *    will be written at the last byte of the file.
     */
    result = write(fd,"",1);
    if (result != 1) {
    close(fd);
    perror("Error writing last byte of the file");
    exit(EXIT_FAILURE);
    }

    /* Now the file is ready to be mmapped.
     */
    map = (struct*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED) {
    close(fd);
    perror("Error mmapping the file");
    exit(EXIT_FAILURE);
    }

    struct med m;
    struct med s;
    int a=0;

    printf("Key of med: ");
    scanf("%d",&m.key);

    //strcmp return "0" when the two strings are equal
    for (i = 0; i <=NUMINTS; ++i) {

    int a=0;
    map[i]=&s;

    read(fd,&s,1);

    if ((strcmp(&m.key,&s.key))==0){
        a++;    
        printf("Med %d already exits. \n",s.key);    
        break;
    }
    }

    if (a==0){
    printf("Name of med: ");
    scanf("%s",&m.name);
    printf("Quant. min. of med: ");
    scanf("%d",&m.quant_min);
    printf("Quant. of med: ");
    scanf("%d",&m.quant);

    map[i]=&m;

    write(fd,&m,1);
    printf("Med %d saved. \n",m.key);
    }

    /* Don't forget to free the mmapped memory
     */
    if (munmap(map, FILESIZE) == -1) {
    perror("Error un-mmapping the file");
    /* Decide here whether to close(fd) and exit() or not. Depends... */
    }

    /* Un-mmaping doesn't close the file, so we still need to do that.
     */
    close(fd);
}

解决方案

In your code, you have:

int result;
struct map*;  /* mmapped array of structs */

You previously defined struct med (and not struct map), and you tried to define a variable of type struct map * but gave it no name. The compiler is right to complain. You probably need something like:

int result;
struct med *map = 0;  /* mmapped array of struct med */


Julia commented:

I now have the following errors:

  • invalid use of undefined type "struct med"
  • deferencing pointer to incomplete type

in map[i]=&s and map[i]=&m.

Well, I didn't look much further than the first error, but where's there's one, there are usually many.

For example:

map = (struct*)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

needs the cast changed to (struct med *).

map[i]=&s;

is wrong. Semantically, map[i] = s; would be correct (copy the data from s into the mapped array) — but, on second thoughts, the assignment is simply wrong. Don't do it; delete that line.

Your FILESIZE should probably be a multiple of sizeof(struct map) rather than a multiple of sizeof(int).

The read() on fd is dubious at best — remove it; the whole point of mmap() is to avoid direct file I/O.

The key values are integers; you don't use strcmp() to compare them, and you simply use:

if (m.key == map[i].key)

The assignment map[i]=&m; is wrong, again, and should be deleted, and the write() is wrong. You have an array, map, and you access it like you would any other array, and the system handles the I/O behind the scenes. Note that you should always check that read() and write() operations worked, but that becomes moot when you delete those operations anyway.

You will need to review how values are assigned to the map array, and ensure you don't try to read from uninitialized values in the map array.

There may still be other problems; I've not compiled, much less run, the code. But these comments should help you on your way.

Note that the error messages from the compiler should be helping too; you need to learn how to interpret them. Make sure you're compiling with enough warnings and that you're fixing all the warnings. If you use GCC, then gcc -Wall -Wextra -Werror -std=c11 is a fairly good start (I usually add a bunch of warnings about prototypes and function definitions too — some, but not all, of which are included in the above). Remember, the C compiler knows a lot more about C than you do at this stage of your career; listen to its warnings with care, because you should assume it is correct and you've made a mistake. That's what I do, and I've been coding in C for more than 30 years now.


Working code mm7.c

The created program is mm7 (memory map 7; there's no significance to 7 other than it is prime, small and makes the name unique).

A fair amount of the code is left as written in the question, rather than rewrite from scratch. Note that the code is careful to validate input — it detects and bails out on EOF, rather than blithely continuing and doing nothing useful, for example. It includes an output loop to print the stored data. It uses functions to manage parts of the input. It would be better if all the input were handled in functions.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#define FILEPATH "/tmp/mmapped.bin"
#define NUMINTS (1000)
#define FILESIZE (NUMINTS * sizeof(struct med))

struct med
{
    int key;
    char name[25];
    int quant_min;
    int quant;
};

static void print_med(const char *tag, const struct med *med)
{
    printf("%s: %4d: Q(%2d, min %2d): %s\n",
           tag, med->key, med->quant, med->quant_min, med->name);
}

static int med_in_map(const struct med *map, int num_meds, int key)
{
    for (int i = 0; i < num_meds; ++i)
    {
        if (key == map[i].key)
        {
            printf("Med %d already exists.\n", key);
            return 1;
        }
    }
    return 0;
}

static int get_new_key(const struct med *map, int num_meds, int *key)
{
    while (printf("Key of med: ") > 0 && scanf("%d", key) == 1)
    {
        if (med_in_map(map, num_meds, *key) == 0)
            return 0;
    }
    return EOF;
}

int main(void)
{
    int fd;
    int result;
    struct med *map;  /* mmapped array of structs */

    /* Open a file for writing.
     *  - Creating the file if it doesn't exist.
     *  - Truncating it to 0 size if it already exists. (not really needed)
     *
     * Note: "O_WRONLY" mode is not sufficient when mmapping.
     */
    fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
    if (fd == -1)
    {
        perror("Error opening file for writing");
        exit(EXIT_FAILURE);
    }

    /* NB: ftruncate(fd, FILESIZE); is simpler */
    /* Stretch the file size to the size of the (mmapped) array of structs */
    result = lseek(fd, FILESIZE - 1, SEEK_SET);
    if (result == -1)
    {
        close(fd);
        perror("Error calling lseek() to 'stretch' the file");
        exit(EXIT_FAILURE);
    }

    /* Something needs to be written at the end of the file to
     * have the file actually have the new size.
     * Just writing an empty string at the current file position will do.
     *
     * Note:
     *  - The current position in the file is at the end of the stretched
     *    file due to the call to lseek().
     *  - An empty string is actually a single '\0' character, so a zero-byte
     *    will be written at the last byte of the file.
     */
    result = write(fd, "", 1);
    if (result != 1)
    {
        close(fd);
        perror("Error writing last byte of the file");
        exit(EXIT_FAILURE);
    }

    /* Now the file is ready to be mmapped.  */
    map = (struct med *)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED)
    {
        close(fd);
        perror("Error mmapping the file");
        exit(EXIT_FAILURE);
    }

    /* Input loop */
    int num_meds;
    for (num_meds = 0; num_meds < NUMINTS; num_meds++)
    {
        struct med m;
        memset(&m, '\0', sizeof(m));

        if (get_new_key(map, num_meds, &m.key) == EOF)
            break;

        printf("Name of med: ");
        if (scanf("%s", m.name) != 1)
            break;
        printf("Quant. min. of med: ");
        if (scanf("%d", &m.quant_min) != 1)
            break;
        printf("Quant. of med: ");
        if (scanf("%d", &m.quant) != 1)
            break;

        map[num_meds] = m;

        printf("Med %d saved.\n", m.key);
    }

    /* Output loop */
    printf("\nRecorded meds:\n");
    for (int i = 0; i < num_meds; i++)
    {
        char buffer[32];
        snprintf(buffer, sizeof(buffer), "M%.4d", i);
        print_med(buffer, &map[i]);
    }

    /* Don't forget to free the mmapped memory */
    if (munmap(map, FILESIZE) == -1)
    {
        perror("Error un-mmapping the file");
        /* Decide here whether to close(fd) and exit() or not. Depends... */
    }

    /* Un-mmapping doesn't close the file, so we still need to do that.  */
    close(fd);

    /* Remove file? */
    /* unlink(FILEPATH); */
}

Example data input file mm7.data

This simulates terminal input with attempted repeats of the number.

1 Hydrocontin 3 5
1
2 Paxodontin 1 1
2
37 Ibuprofen 2 12
129 Butoxydione 12 29
4 Placebo 2 22
37
129
4
2
1
9231 Aspirin 99 99

Example run with the data file:

$ ./mm7 < mm7.data
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 1 saved.
Key of med: Med 1 already exists.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 2 saved.
Key of med: Med 2 already exists.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 37 saved.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 129 saved.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 4 saved.
Key of med: Med 37 already exists.
Key of med: Med 129 already exists.
Key of med: Med 4 already exists.
Key of med: Med 2 already exists.
Key of med: Med 1 already exists.
Key of med: Name of med: Quant. min. of med: Quant. of med: Med 9231 saved.
Key of med: 
Recorded meds:
M0000:    1: Q( 5, min  3): Hydrocontin
M0001:    2: Q( 1, min  1): Paxodontin
M0002:   37: Q(12, min  2): Ibuprofen
M0003:  129: Q(29, min 12): Butoxydione
M0004:    4: Q(22, min  2): Placebo
M0005: 9231: Q(99, min 99): Aspirin
$

The prompts are all on one line for each actual medication because the input was read from a file, not from the keyboard.

Hex dump of data file /tmp/mmapped.bin

$ odx /tmp/mmapped.bin
0x0000: 01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00   ....Hydrocontin.
0x0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0020: 03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F   ............Paxo
0x0030: 64 6F 6E 74 69 6E 00 00 00 00 00 00 00 00 00 00   dontin..........
0x0040: 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00   ................
0x0050: 25 00 00 00 49 62 75 70 72 6F 66 65 6E 00 00 00   %...Ibuprofen...
0x0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0070: 02 00 00 00 0C 00 00 00 81 00 00 00 42 75 74 6F   ............Buto
0x0080: 78 79 64 69 6F 6E 65 00 00 00 00 00 00 00 00 00   xydione.........
0x0090: 00 00 00 00 00 00 00 00 0C 00 00 00 1D 00 00 00   ................
0x00A0: 04 00 00 00 50 6C 61 63 65 62 6F 00 00 00 00 00   ....Placebo.....
0x00B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x00C0: 02 00 00 00 16 00 00 00 0F 24 00 00 41 73 70 69   .........$..Aspi
0x00D0: 72 69 6E 00 00 00 00 00 00 00 00 00 00 00 00 00   rin.............
0x00E0: 00 00 00 00 00 00 00 00 63 00 00 00 63 00 00 00   ........c...c...
0x00F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
* (2484)
0x9C40:
$

The memset() was added because the hex-dump showed garbage in the data structure — harmless garbage, but garbage nonetheless:

0x0000: 01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00   ....Hydrocontin.
0x0010: 2F 62 69 6E 3A 2F 75 73 72 2F 67 6E 75 2F 62 69   /bin:/usr/gnu/bi
0x0020: 03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F   ............Paxo
0x0030: 64 6F 6E 74 69 6E 00 00 2F 62 69 6E 3A 2F 75 73   dontin../bin:/us
0x0040: 72 2F 67 6E 75 2F 62 69 01 00 00 00 01 00 00 00   r/gnu/bi........
…

Initializing the structure with:

struct med m = { .key = 0, .name = "", .quant = 0, .quant_min = 0 };

didn't completely eliminate the garbage because of the padding bytes in the structure after the name (which surprised me!):

0x0000: 01 00 00 00 48 79 64 72 6F 63 6F 6E 74 69 6E 00   ....Hydrocontin.
0x0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 2F 62 69   ............./bi
0x0020: 03 00 00 00 05 00 00 00 02 00 00 00 50 61 78 6F   ............Paxo
0x0030: 64 6F 6E 74 69 6E 00 00 00 00 00 00 00 00 00 00   dontin..........
0x0040: 00 00 00 00 00 2F 62 69 01 00 00 00 01 00 00 00   ...../bi........
…


Working with a non-empty file

The code no longer includes O_TRUNC when creating the file, but that makes the hard-coded file name less appropriate (but I've not fixed that). The find_num_entries() function simply does a linear search through the data to find where the list left off last time. More sophisticated code would do a binary search through the data to find where the empty entries start. There's also linear search in the lookup for keys. It would be better to keep the file in sorted order so that a binary search can be used for that, too. Indeed, it might be sensible to have a header record in a production version which had a magic number to identify the file type (if the file doesn't start with the correct magic number, it clearly isn't a file that this program wrote, so its contents shouldn't be interpreted), and to record other details such as number of entries and maybe minimum and maximum key values.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#define FILEPATH "/tmp/mmapped.bin"
#define NUM_MEDS (1000)
#define FILESIZE (NUM_MEDS * sizeof(struct med))

struct med
{
    int key;
    char name[25];
    int quant_min;
    int quant;
};

static void print_med(const char *tag, const struct med *med)
{
    printf("%s: %4d: Q(%2d, min %2d): %s\n",
           tag, med->key, med->quant, med->quant_min, med->name);
}

static int med_in_map(const struct med *map, int num_meds, int key)
{
    for (int i = 0; i < num_meds; ++i)
    {
        if (key == map[i].key)
        {
            printf("Med %d already exists.\n", key);
            return 1;
        }
    }
    return 0;
}

static int get_new_key(const struct med *map, int num_meds, int *key)
{
    while (printf("Key of med: ") > 0 && scanf("%d", key) == 1)
    {
        if (med_in_map(map, num_meds, *key) == 0)
            return 0;
    }
    return EOF;
}

static int find_num_entries(const struct med *map, int max_meds)
{
    int i;
    for (i = 0; i < max_meds; i++)
    {
        if (map[i].key == 0)
            break;
    }
    return i;
}

int main(void)
{
    int fd;
    int result;
    struct med *map;

    fd = open(FILEPATH, O_RDWR | O_CREAT, (mode_t)0600);
    if (fd == -1)
    {
        perror("Error opening file for writing");
        exit(EXIT_FAILURE);
    }

    result = ftruncate(fd, FILESIZE);
    if (result == -1)
    {
        close(fd);
        perror("Error calling ftruncate() to 'set' the file size");
        exit(EXIT_FAILURE);
    }

    map = (struct med *)mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED)
    {
        close(fd);
        perror("Error mmapping the file");
        exit(EXIT_FAILURE);
    }

    /* Input loop */
    int num_meds;
    for (num_meds = find_num_entries(map, NUM_MEDS); num_meds < NUM_MEDS; num_meds++)
    {
        struct med m;
        memset(&m, '\0', sizeof(m));

        if (get_new_key(map, num_meds, &m.key) == EOF)
            break;

        printf("Name of med: ");
        if (scanf("%s", m.name) != 1)
            break;
        printf("Quant. min. of med: ");
        if (scanf("%d", &m.quant_min) != 1)
            break;
        printf("Quant. of med: ");
        if (scanf("%d", &m.quant) != 1)
            break;

        map[num_meds] = m;

        printf("Med %d saved.\n", m.key);
    }

    /* Output loop */
    printf("\nRecorded meds:\n");
    for (int i = 0; i < num_meds; i++)
    {
        char buffer[32];
        snprintf(buffer, sizeof(buffer), "M%.4d", i);
        print_med(buffer, &map[i]);
    }

    /* Don't forget to free the mmapped memory */
    if (munmap(map, FILESIZE) == -1)
    {
        perror("Error un-mmapping the file");
        /* Decide here whether to close(fd) and exit() or not. Depends... */
    }

    /* Un-mmapping doesn't close the file, so we still need to do that.  */
    close(fd);
    return 0;
}

You can also make the error reporting simpler using functions analogous to err_syserr(), which you can find in answers of mine such as Creating a file with given size.

这篇关于MMAP和结构用C的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆