我想用C语言读取和编写mmap的结构。
我有一个名为insert_med的函数,它允许在mmap中插入一个新的struct med ,并且每个struct(带有唯一的 key )必须写在不同的位置数组(当添加新结构时,必须将其添加到数组的最后一个空位置。)
两个结构 med 不能具有相同的键,如下面的代码所示。 键是唯一的
我的代码不起作用 - 带有变量“struct map”的错误消息:声明时和文件准备好mmapped时 - 但我不知道为什么。我想我可能做错了事。
#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);
}
答案 0 :(得分:7)
在您的代码中,您有:
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]=&s
和map[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)
的倍数。
read()
上的fd
充其量是可疑的 - 删除它; 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;除了它是素数之外没有任何意义,小而且使名称唯一)。
相当多的代码保留在问题中,而不是从头开始重写。请注意,代码要小心验证输入 - 例如,它会检测并剔除EOF,而不是轻率地继续并且不做任何有用的操作。它包括一个输出循环来打印存储的数据。它使用函数来管理输入的部分。如果所有输入都是在函数中处理的话会更好。
#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); */
}
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
$
每个实际药物的提示都在一行,因为输入是从文件中读取的,而不是从键盘读取的。
/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 <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;
}
您还可以使用与err_syserr()
类似的功能简化错误报告,您可以在我的答案中找到Creating a file with given size。