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

Posted

技术标签:

【中文标题】C:更新共享内存 pthread 并使用 strncpy【英文标题】:C: Update shared memory pthreads and using strncpy 【发布时间】:2022-01-05 15:33:08 【问题描述】:

我正在尝试学习如何在不使用全局变量的情况下使用线程更新一些共享内存,但是当我尝试使用 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;

【问题讨论】:

【参考方案1】:

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


除此之外,由于所有typedefing 和不必要的包装器结构,您的代码难以理解。此外,基本上所有的名称都非常缺乏信息性。随着这一切的发生,你自己感到困惑也就不足为奇了。

这里:

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

...您将指向共享内存段的指针分配给变量shared_test,类型为shm_pointer_t *,又名struct shared_pointer_test *。该内存未初始化,特别是 shared_test-&gt;array_ptr 未分配为指向任何内容

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

另外,

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

这是您的代码的一个变体,它删除了很多 goop,改进了 (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] = '\0';
    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;

【讨论】:

这解释太棒了!我一直在努力解决这个问题,所以我很感激你为解释它付出了多少努力!约翰,你是个传奇!我正在使用的原始示例代码使用来自另一个进程的共享内存,但我试图将其分解为一个程序,以便我可以理解它是如何工作的(但最终变得更加困惑)。再次感谢! 这似乎适用于字符串,但不适用于单个字符 char test2。有没有办法为结构分配一个字符?当我在线程test_data-&gt;test2 = 'T' 中设置它时,当我尝试从主printf("Test char int: %c\n", cshared-&gt;test_array[i].test2) 打印它时它似乎没有更新?它是空白的。如果我更新整数、字符串或浮点数,它们都可以工作吗? @roboticsmick,设置char 类型的对象并没有什么特别之处,它也不适用于设置int 类型的对象。如果这对您不起作用,那么我需要查看整个程序以确定错误在哪里。这将是一个单独问题的主题,尽管我鼓励您自己解决。也许这是学习如何使用调试器的好时机。 感谢 John,学习如何使用调试器听起来很有用。当我改变结构的顺序时,主要打印的字符。这没有多大意义,所以某处一定有错误。谢谢你的建议!

以上是关于C:更新共享内存 pthread 并使用 strncpy的主要内容,如果未能解决你的问题,请参考以下文章

在共享内存中初始化 pthread 互斥锁

pthread 从共享内存中读取

fork(),C 中的共享内存和指针

一些提示,以避免在这个pthreaded C程序中出现死锁

使用共享内存时遇到一些麻烦

pthread读写锁不起作用吗? [关闭]