为什么我会因为我的mutex_lock而得到一个分段错误?

snvhrwxg  于 2023-04-19  发布在  其他
关注(0)|答案(2)|浏览(119)

我正在做一个解决哲学家用餐问题的程序,当我试图锁定我的互斥锁时,我得到了一个分段错误。我不允许使用全局变量,所以我不得不用指针移动我的互斥锁,我觉得我这样做的方式很糟糕,我有点迷失在自己的代码中。下面是重要的函数

# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <pthread.h>
# include <sys/time.h>

typedef struct s_philo
{
    int                 n_philo;        //* number of philosophers
    int                 time_to_die;    //* time to die in ms
    int                 time_to_eat;    //* time to eat in ms
    int                 time_to_sleep;  //* time to sleep in ms
    int                 need_eat;       //* number of times each philosopher must eat
    u_int64_t           start_time;     //* time when the simulation starts
}   t_philo;

typedef struct s_data
{
    pthread_mutex_t     *left_fork;     //* pointer to the left fork
    pthread_mutex_t     *right_fork;    //* pointer to the right fork
    u_int64_t           last_eaten;     //* time when the philosopher last ate
    t_philo             *philo;         //* links to the philo struct
    int                 id;             //* id of the philosopher
    int                 dead;           //* 1 if the philosopher is dead
    int                 is_eating;      //* 1 if the philosopher is eating
    int                 n_eat;          //* number of times the philosopher has eaten
}   t_data;

u_int64_t   get_time(void);                                                             //* returns the time in ms
int         ft_usleep(useconds_t time);                                                 //* sleeps for time ms
int         ft_atoi(const char *str);                                                   //* converts a string to an int
void        init_philo(t_philo *philo, int argc, char **argv);                          //* initializes the philo struct
void        init_data(t_data *data, t_philo *philo, int id);                            //* initializes the data struct
void        *routine(void *vdata);                                                      //* routine of the philosophers
void        *monitor(void *vdata);                                                      //* monitors the philosophers (exemples: death)
int         ft_eat(void *vdata);                                                        //* eat function
int         ft_sleep(void *vdata);                                                      //* sleep function
int         ft_think(void *vdata);                                                      //* think function

int main(int argc, char **argv)
{
    t_philo         philo;
    t_data          data[ft_atoi(argv[1])];
    pthread_t       philos[ft_atoi(argv[1])];
    pthread_t       monitoring;
    pthread_mutex_t forks[ft_atoi(argv[1])];
    int             i;

    i = 0;
    init_philo(&philo, argc, argv);
    while (i < philo.n_philo)
    {
        pthread_mutex_init(&forks[i], NULL);
        i++;
    }
    i = 0;
    while(i < philo.n_philo)
    {
        init_data(&data[i], &philo, i);
        data->left_fork = &forks[i];
        if (i == 0)
            data->right_fork = &forks[philo.n_philo - 1];
        else
            data->right_fork = &forks[i - 1];
        i++;
    }
    i = 0;
    philo.start_time = get_time();
    pthread_create(&monitoring, NULL, &monitor, &data);
    while (i < philo.n_philo)
    {
        pthread_create(&philos[i], NULL, &routine, &data[i]);
        i++;
    }
    i = 0;
    while (i < philo.n_philo)
    {
        pthread_join(philos[i], NULL);
        pthread_mutex_destroy(&forks[i]);
        i++;
    }
    pthread_join(monitoring, NULL);
    return (0);
}

void    *monitor(void *vdata)
{
    t_data      *data;
    int         i;

    data = (t_data *)vdata;
    i = 0;
    while (1)
    {
        while(i < data->philo->n_philo)
        {
            if (get_time() - data[i].last_eaten > (u_int64_t)data[i].philo->time_to_die && data[i].last_eaten != 0 && data[i].is_eating == 0)
            {
                printf("%llu %d died\n", get_time() - data->philo->start_time, data->id);
                data[i].dead = 1;
                exit(0);
            }
            i++;
        }
        i = 0;
    }
    return (NULL);
}

void    *routine(void *vdata)
{
    t_data  *data;

    data = (t_data *)vdata;
    if (data->id % 2 == 0)
        ft_sleep(data);
    while (data->dead == 0)
    {
        ft_eat(data);
        ft_sleep(data);
        ft_think(data);
    }
    return (NULL);
}

int ft_sleep(void *vdata)
{
    time_t      strt;
    t_data      *data;

    data = (t_data *)vdata;
    strt = data->philo->start_time;
    printf("%llu %d is sleeping\n", get_time() - strt, data->id);
    ft_usleep(data->philo->time_to_sleep);
    return (0);
}

int ft_eat(void *vdata)
{
    time_t  strt;
    t_data  *data;

    data = (t_data *)vdata;
    strt = data->philo->start_time;
    pthread_mutex_lock(data->left_fork);
    printf("%llu %d has taken a fork\n", get_time() - strt, data->id);
    pthread_mutex_lock(data->right_fork);
    printf("%llu %d has taken a fork\n", get_time() - strt, data->id);
    data->is_eating = 1;
    printf("%llu %d is eating\n", get_time() - strt, data->id);
    ft_usleep(data->philo->time_to_eat);
    data->last_eaten = get_time();
    data->n_eat++;
    pthread_mutex_unlock(data->left_fork);
    pthread_mutex_unlock(data->right_fork);
    data->is_eating = 0;
    return (0);
}

int ft_think(void *vdata)
{
    time_t  strt;
    t_data  *data;

    data = (t_data *)vdata;
    strt = data->philo->start_time;
    printf("%llu %d is thinking\n", get_time() - strt, data->id);
    return (0);
}

int ft_usleep(useconds_t time)
{
    time_t  start;
    start = get_time();
    while ((get_time() - start) < time)
        usleep(time / 10);
    return(0);
}

u_int64_t   get_time(void)
{
    struct timeval  tv;
    
    gettimeofday(&tv, NULL);
    return ((tv.tv_sec * (u_int64_t)1000) + (tv.tv_usec / 1000));
}

void    init_philo(t_philo *philo, int argc, char **argv)
{
    philo->n_philo = ft_atoi(argv[1]);
    philo->time_to_die = ft_atoi(argv[2]);
    philo->time_to_eat = ft_atoi(argv[3]);
    philo->time_to_sleep = ft_atoi(argv[4]);
    if (argc == 6)
        philo->need_eat = ft_atoi(argv[5]);
    else
        philo->need_eat = -1;
    philo->start_time = get_time();
}

void    init_data(t_data *data, t_philo *philo, int id)
{
    
    data->id = id;
    data->last_eaten = 0;
    data->dead = 0;
    data->is_eating = 0;
    data->n_eat = 0;
    data->philo = philo;
}
wj8zmpe1

wj8zmpe11#

根据这个定义:

t_data          data[ft_atoi(argv[1])];

,这不会初始化所有数据:

i = 0;
    while(i < philo.n_philo)
    {
        init_data(&data[i], &philo, i);
        data->left_fork = &forks[i];
        if (i == 0)
            data->right_fork = &forks[philo.n_philo - 1];
        else
            data->right_fork = &forks[i - 1];
        i++;
    }

变量data指定了一个数组,所以它在上面的表达式中出现的地方,它衰减到指向第一个数组元素的指针。也就是说,你反复设置data[0]的分支,而从不设置其他元素的分支。i没有出现在表达式data中,并且data本身从未被修改,这将是你的线索。
应该这样写:

for (i = 0; i < philo.n_philo; i++) {  // prefer 'for' for simple iteration
        init_data(&data[i], &philo, i);
        data[i].left_fork = &forks[i];
        if (i == 0) {  // never omit braces around 'if' and 'else' bodies
            data[i].right_fork = &forks[philo.n_philo - 1];
        } else {
            data[i].right_fork = &forks[i - 1];
        }
    }

有了这个改变,你的程序就可以在没有segfaulting的情况下为我运行,并最终终止。

ru9i0ody

ru9i0ody2#

这不是答案。我懒得调试你的代码。这只是一些扩展形式的注解。
你创建了一个新的线程来运行monitor(),但是你的main()线程除了在monitor线程运行时等待之外什么也不做。为什么要创建线程呢?为什么不直接从主线程调用monitor()呢?
评论中的信息太多:例如,int time_to_die; //*time to die in ms。您可以使用int time_to_die_ms;传达相同的信息
t_philo的更好的名称是t_parameterst_configurationt_data的更好的名称是t_philosopherroutine()的更好的名称是philosopher_main()
所有的void*参数和所有的类型转换是怎么回事?唯一需要 * void*的地方是线程函数monitorroutine的参数。任何其他地方,如果你想传递一个指向t_data示例的指针,参数应该声明为t_data*类型。例如,ft_eat(t_data* data)而不是ft_eat(void* vdata)
monitor()线程和routine()线程共享对t_data字段的访问,而没有任何同步。这使得程序的行为正式“未定义”。
monitor()线程 spins: 它 * 不断地 * 尽可能快地检查哲学家。这是使用整个CPU的所有可用周期,同时可能 * 饥饿 * 其他线程。您不需要它经常检查。也许在外部循环中睡眠一秒钟,给予其他线程有机会做他们的事情。
ft_usleep()中,毫秒和微秒之间存在很多混淆。看起来每次调用它,它都会执行大约一万次循环迭代,休眠time/10000毫秒。(time/10微秒)。如果你喜欢毫秒,那么除了在调用usleep()时 * 小心地、明确地 ,在任何地方都使用毫秒。另外,我会去掉循环:

void ft_mssleep(int milliseconds) {
    useconds_t microseconds = (useconds_t)1000 * milliseconds;
    usleep(microseconds);
}

你已经得到了声明为返回int的函数,它总是返回return (0),而它们的调用者从不检查返回值。为什么这些函数不声明为返回void呢?
这没有任何意义:

data[i].dead = 1;
    exit(0);

如果你做的下一件事是杀死整个程序,那么设置哲学家的dead标志就没有意义了。最好干脆把dead标志都去掉。如果哲学家因为死锁而“死”了,那么即使monitor()没有立即杀死整个程序,它也永远不会醒来检查这个标志。

  • 如果调用者想睡眠超过几千秒,那么你担心溢出,所以你会循环吗?这可能是对这个例子的要求考虑过度了,但是如果你真的想处理这种情况,那么你可能会想做一些比你实际做的更复杂的事情。

相关问题