C:更新共享内存 pthreads 并使用 strncpy

C: Update shared memory pthreads and using strncpy

我正在尝试学习如何在不使用全局变量的情况下使用线程更新一些共享内存,但是当我尝试使用 strncpy 时,我不断收到“分段错误(核心转储)”错误?

我正在尝试从每个线程中的共享内存中引用唯一的数据结构?

如有任何提示,我们将不胜感激!

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>

#define THREADCOUNT 3
#define ARRAYCOUNT 3
#define SHM_SIZE 48
#define KEY_NAME "test"

const int threadNumArray[3] = {0, 1, 2};

/* Data struct */
typedef struct struct_test
{
    int testi;     /* 4 bytes */
    float testf;   /* 4 bytes */
    char testc[6]; /* 8 bytes (2 bytes padding) */
} test_t;

/* Arrays of data */
typedef struct shared_array_test
{
    test_t test_array[ARRAYCOUNT];
} shm_array_t;

/* Pointer to arrays */
typedef struct shared_pointer_test
{
    shm_array_t *array_ptr;
} shm_pointer_t;

/* Thread structs with pointer to data array and thread number */
typedef struct thread_array_test
{
    int threadNum;
    shm_pointer_t *shared_ptr;
} thread_array_t;

void *test_process(void *arg)
{
    thread_array_t *thread_array = (void *)arg;                                                    /* Pointer to shared data */
    test_t *test_data = &thread_array->shared_ptr->array_ptr->test_array[thread_array->threadNum]; /* Data from thread number */
    char *testing = "TESTING";                                                                     /* String */
    strncpy(test_data->testc, testing, 6);                                                         /* String copy to shared segement */
    test_data->testf = 10.2;                                                                       /* Assign float */
    test_data->testi = 2;                                                                          /* Test integer */
    return NULL;
}

int main()
{
    /* Create shared memory segement */
    int shm_test;
    shm_pointer_t *shared_test;
    if ((shm_test = shm_open(KEY_NAME, O_CREAT | O_RDWR, 0666)) < 0)
    {
        printf("Error: shm_open");
        return -1;
    }
    ftruncate(shm_test, SHM_SIZE);
    if ((shared_test = mmap(0, SHM_SIZE, PROT_WRITE, MAP_SHARED, shm_test, 0)) == MAP_FAILED)
    {
        printf("Error: mmap");
        return -1;
    }

    /* Creates structs for each thread */
    thread_array_t thread_array[THREADCOUNT];
    for (int i = 0; i < THREADCOUNT; i++)
    {
        thread_array[i].shared_ptr = shared_test;      /* Shared data */
        thread_array[i].threadNum = threadNumArray[i]; /* Thread id */
    }

    /* Start threads */
    pthread_t threads[THREADCOUNT];
    for (int i = 0; i < THREADCOUNT; i++)
    {
        pthread_create(&threads[i], NULL, test_process, thread_array + i);
    }
    /* Join threads */
    for (int i = 0; i < THREADCOUNT; i++)
    {
        pthread_join(threads[i], NULL);
    }
    /* Test - Print strings */
    for (int i = 0; i < THREADCOUNT; i++)
    {
        printf("Test: %s", shared_test->array_ptr->test_array->testc);
    }
    return 0;
}

初步注意,完全没有必要设置POSIX共享内存段来实现同进程线程之间的数据共享。 POSIX 共享内存主要是为了让不同的进程共享内存。同一进程的线程彼此在同一地址 space 中运行,并且它们自动可以访问该 space 的内容。事实上,这是线程的关键特征之一。


由于所有 typedefing 和不必要的包装结构,您的代码很难理解。此外,基本上所有的名称都没有提供多少信息。随着这一切的发生,你自己感到困惑也就不足为奇了。

这里:

    if ((shared_test = mmap(0, SHM_SIZE, PROT_WRITE, MAP_SHARED, shm_test, 0)) == MAP_FAILED)

...您将指向共享内存段的指针分配给类型为 shm_pointer_t *、a.k.a 的变量 shared_teststruct shared_pointer_test *。该内存未初始化,特别是 shared_test->array_ptr 未分配指向任何内容.

您为所有线程提供了 shared_test 指针的副本,并且它们尝试使用它,就好像 array_ptr 成员是一个有效指针一样。这很可能是您问题的根源,尽管还有其他缺陷可能会产生类似的可观察到的影响(见下文)。

此外,

  • SHM_SIZE 使用数字常量没有帮助,因为这无法让您了解如何选择大小或内存的预期用途。使用 sizeof 而不是整数常量。此外,因为它的使用非常本地化,我认为制作一个宏实际上没有帮助——没有代码会更清晰。
  • strncpy() 如果在其第三个参数指定的字符数内没有遇到空终止符,则不会添加空终止符。在您的情况下,如果程序实际到达该点,这将导致您的线程将未终止的字符数组写入各种 testc 成员。这本质上并不是错误的,但是您不能将这些数组视为字符串,main() 稍后会尝试这样做。 strncat() 通常是比 strncpy() 更好的选择,即使前者可能需要通过在其第一个位置写入终止符来准备目的地。
  • 您似乎至少对 POSIX 共享内存(您正在使用的)和 System V 共享内存感到困惑。具体来说,“KEY_NAME”让人想起 SysV IPC 密钥,而 POSIX 共享内存 object 只是有名称。这些名字应该以“/”开头,而你的不是。
  • 好的代码注释很有价值,但无用的代码注释还不如没有注释。注释应该解释重要但不明显的事情,例如选择实现的原因、函数或代码段的期望或先决条件等。
  • 不要尝试手动计算结构大小。它是 error-prone 和 non-portable。相反,使用 sizeof 计算 object 和需要的数据类型大小。
  • main() should generally return a number in the range 0 - 125,不是-1
  • perror 是发出有关库函数调用失败的错误消息的便捷方式。
  • 虽然文字 0 是一个有效的 null-pointer 常量,但最好使用 NULL 来达到目的,因为这样可以使意图更清楚。
  • 关于代码风格,避免包含不需要的 headers。

这是您的代码的一个变体,它删除了很多代码,改进了 (IMO) 大部分命名,并修复了大部分样式问题。它比原来的更短更清晰,而且恰好可以正常工作。

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

#define THREADCOUNT 3
#define SHM_NAME "/test"

/* Data struct */
struct item {
    int testi;     /* 4 bytes */
    float testf;   /* 4 bytes */
    char testc[6]; /* 8 bytes (2 bytes padding) */
};

/* The structure of the shared memory contents */
struct shared_data {
    struct item test_array[THREADCOUNT];
};

/* Per-thread information */
struct thread_info {
    int threadNum;
    struct shared_data *shared_ptr;
};

void *test_process(void *arg) {
    struct thread_info *my_info = arg;
    struct item *test_data = &my_info->shared_ptr->test_array[my_info->threadNum];

    test_data->testc[0] = '[=11=]';
    strncat(test_data->testc, "TESTING", sizeof(test_data->testc) - 1);
    test_data->testf = 10.2;
    test_data->testi = 2;

    return NULL;
}

int main(void) {
    /* Create shared memory segement */
    int shm_test;
    struct shared_data *shared;

    if ((shm_test = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666)) < 0) {
        perror("shm_open");
        return 1;
    }
    ftruncate(shm_test, sizeof(*shared));
    if ((shared = mmap(0, SHM_SIZE, PROT_WRITE, MAP_SHARED, shm_test, 0)) == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    /* Set up per-thread data */
    struct thread_info thread_array[THREADCOUNT];
    for (int i = 0; i < THREADCOUNT; i++) {
        thread_array[i].shared_ptr = shared;
        thread_array[i].threadNum = i;
    }

    /* Start threads */
    pthread_t threads[THREADCOUNT];
    for (int i = 0; i < THREADCOUNT; i++) {
        pthread_create(&threads[i], NULL, test_process, thread_array + i);
    }

    /* Join threads */
    for (int i = 0; i < THREADCOUNT; i++) {
        pthread_join(threads[i], NULL);
    }

    /* Test */
    for (int i = 0; i < THREADCOUNT; i++) {
        printf("Test: %s\n", shared->test_array[i].testc);
    }

    return 0;
}