C中的Mmap和struct

我想在C中用mmap读写结构。
我有一个名为insert_med的函数,它允许在mmap中插入一个新的struct med ,并且每个struct(带有一个唯一的 )都必须写在数组的不同位置(当添加一个新结构时,它必须是添加在数组的最后一个空位置)。
两个结构med不能具有相同的密钥,如下面的代码所示。 关键是独一无二的。
我的代码不起作用 – 带有变量“struct map”的错误消息:声明时和文件准备好mmapped时 – 但我不知道为什么。 我想我可能做错了事。

#include  #include  #include  #include  #include  #include  #include  #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); } 

在您的代码中,您有:

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

您之前定义了struct med (而不是struct map ),并且您尝试定义struct map *类型的变量,但没有为其指定名称。 编译器是抱怨的权利。 你可能需要这样的东西:

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

朱莉娅 评论道 :

我现在有以下错误:

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

map[i]=&smap[i]=&m

好吧,我看起来并没有比第一个错误更远,但是哪里有一个,通常有很多。

例如:

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

需要将演员变为(struct med *)

 map[i]=&s; 

是错的。 从语义上讲, map[i] = s; 是正确的(将数据从s复制到映射的数组中) – 但是,在第二个想法,分配是完全错误的。 不要这样做; 删除该行。

你的FILESIZE应该是sizeof(struct map)的倍数,而不是sizeof(int)的倍数。

fd上的read()充其量是可疑的 – 删除它; mmap()是避免直接文件I / O.

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

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

赋值map[i]=&m; 又错了,应该删除,而write()是错误的。 你有一个数组, map ,你可以像访问任何其他数组一样访问它,系统处理幕后的I / O. 请注意,您应该始终检查read()write()操作是否有效,但是当您删除这些操作时,这会变得毫无意义。

您需要查看如何将值分配给map数组,并确保不要尝试从map数组中读取未初始化的值。

可能还有其他问题; 我没有编译,更不用说运行代码了。 但这些评论应该会帮助你。

请注意,编译器的错误消息也应该有所帮助; 你需要学习如何解释它们。 确保您正在编译足够的警告,并且您正在修复所有警告。 如果你使用GCC,那么gcc -Wall -Wextra -Werror -std=c11是一个相当不错的开始(我通常会添加一些关于原型和函数定义的警告 – 一些,但不是全部,其中包含在上面)。 请记住,C编译器对C的了解远远超过你职业生涯的这个阶段。 仔细听它的警告,因为你应该认为它是正确的,你犯了一个错误。 这就是我的工作,而且我已经用C编写了30多年的代码。


工作代码mm7.c

创建的程序是mm7 (内存映射7;除了它是素数之外没有任何意义,小而且使名称唯一)。

相当数量的代码留在问题中,而不是从头开始重写。 请注意,代码要小心validation输入 – 例如,它会检测并剔除EOF,而不是轻率地继续并且不做任何有用的操作。 它包括一个输出循环来打印存储的数据。 它使用函数来管理输入的部分。 如果所有输入都是在函数中处理的话会更好。

 #include  #include  #include  #include  #include  #include  #include  #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); */ } 

示例数据输入文件mm7.data

这模拟终端输入,尝试重复该数字。

 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 

使用数据文件运行示例:

 $ ./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 $ 

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

hex转储数据文件/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: $ 

添加了memset() ,因为hex-dump在数据结构中显示了垃圾 - 无害的垃圾,但仍然是垃圾:

 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........ … 

使用以下内容初始化结构:

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

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

 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........ … 

使用非空文件

创建文件时代码不再包含O_TRUNC ,但这使得硬编码的文件名不太合适(但我没有修复)。 find_num_entries()函数只是对数据进行线性搜索,以查找列表上次停止的位置。 更复杂的代码将通过数据进行二进制搜索,以查找空条目的开始位置。 在查找键时也有线性搜索。 最好将文件保持在排序顺序,以便也可以使用二进制搜索。 实际上,在生产版本中有一个标题记录可能是明智的,它有一个神奇的数字来标识文件类型(如果文件没有以正确的幻数开头,它显然不是这个程序写的文件) ,因此不应解释其内容),并记录其他细节,如条目数和最小和最大键值。

 #include  #include  #include  #include  #include  #include  #include  #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; } 

您还可以使用类似于err_syserr()函数使错误报告更简单,您可以在我的答案中找到它,例如创建具有给定大小的文件 。