Доброго времени суток!
Постановка задачи:
Есть драйвер, имитирующий работу устройства. В драйвере функция read поверяет готовность данных, и если данные не готовы, то выпадает в sleep.
Далее по таймеру имитируется поступление данных, и "будится" очередь.
Есть программа, которая читает данные из устройства.
При использовании отдельного потока на чтение, ПОСЛЕ УСПЕШНОГО ВЫПОЛНЕНИЯ чтений и вывода результатов программа завершается, НО часть процессов в системе начинает получать SIGSEGV. Сама программа никогда не получает этого сигнала и всегда успешно выполняется.
Если не использовать отдельный поток, то все работает.
linux-2.6.15 и linux-2.6.19
glibc-2.3.6
glibc-linuxthreads-2..3.6
gcc-3.4.5
Функция таймера в модуле:
Код
static void simple_wait_timer(unsigned long data)
{
int i;
struct simple_device *dev = (struct simple_device *)data;
dev->timer.expires += SIMPLE_TIMEOUT;
dev->ipos += SIMPLE_RX_TRANSFER;
if (dev->ipos >= SIMPLE_IN_SIZE)
dev->ipos = 0;
for (i = dev->ipos; i < dev->ipos + SIMPLE_RX_TRANSFER; i++)
dev->ibuf[i] = dev->val + i;
dev->val += SIMPLE_RX_TRANSFER;
atomic_set(&dev->rx_ready, 1);
wake_up_interruptible(&dev->rx_queue);
add_timer(&dev->timer);
}
Функция чтения в модуле:
Код
static ssize_t simple_read(struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
int retval = 0;
struct simple_device *dev = simple_devs[iminor(filp->f_dentry->d_inode)];
int pos;
TRACE;
if (count < (SIMPLE_RX_TRANSFER * sizeof(int))) {
return -ENOMEM;
}
if (atomic_read(&dev->rx_ready) != 1) {
interruptible_sleep_on(&dev->rx_queue);
PDEBUG("awake\n");
}
pos = dev->ipos;
retval = copy_to_user(buf, &dev->ibuf[pos], SIMPLE_RX_TRANSFER * sizeof(int));
if (retval) {
PERROR("Copied less data then requested!\n");
retval = -EFAULT;
} else {
retval = SIMPLE_RX_TRANSFER * sizeof(int);
}
atomic_set(&dev->rx_ready, 0);
return retval;
}
Программа:
Код
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <errno.h>
#define DEVICE_NAME "/dev/simple/simple0"
#define BUFSIZE (256 * sizeof(int))
#define PDEBUG(fmt, arg...) fprintf(stderr, fmt, ##arg)
int loop = 0;
char buf[65536];
void *read_thread(void *arg)
{
int fd = *((int *)arg);
int retval;
int i;
int len;
char s[256];
i = 0;
while (loop) {
retval = read(fd, buf, sizeof(buf));
if (retval < 0) {
fprintf(stderr, "Error reading device: %s\n", strerror(errno));
return NULL;
}
if (retval == (sizeof(int) * 256)) {
write(1, "R", 1);
} else {
write(1, "r", 1);
len = sprintf(s, "\n%d\n", retval);
write(1, s, len);
return NULL;
}
}
return NULL;
}
void *write_thread(void *arg)
{
/*
int i;
int d = *((int *)arg) + 0x30;
fprintf(stderr, "Thread for write started ");
for (i = 0; i < 10000; i++)
write(1, (char *) &d, 1);
*/
return NULL;
}
int main(int argc, char *argv[])
{
int retcode;
void *retval;
pthread_t th_a;
int fdr;
int fdw;
int i;
struct timeval tv, tc;
printf("Hello, world!\n");
fdr = open(DEVICE_NAME, O_RDONLY);
if (fdr < 0) {
perror("Can not open device");
return 1;
}
fprintf(stderr, "%d\n", fdr);
loop = 1;
retcode = pthread_create(&th_a, NULL, read_thread, (void *) &fdr);
if (retcode) {
perror("Thread for read creation failed");
}
for (i = 0; i < 64; i++) {
write(1, ".", 1);
gettimeofday(&tv, 0);
tc.tv_sec = tv.tv_sec;
tc.tv_usec = tv.tv_usec;
while (((tc.tv_sec * 1000000 + tc.tv_usec) - (tv.tv_sec * 1000000 + tv.tv_usec)) < 100000)
gettimeofday(&tc, 0);
}
loop = 0;
retcode = pthread_join(th_a, &retval);
if (retcode) {
perror("Joining of thread for read failed");
}
close(fdr);
printf("\n");
for (i = 0; i < 256; i++) {
if (!(i & 7))
printf("%5d:\t", i);
printf(" 0x%08X", ((int *)buf)[i]);
if ((i & 7) == 7)
printf("\n");
}
return 0;
}
Может ли кто нибудь найти в чем ошибка?
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть.
© Lewis Carroll. Alice's adventures in wonderland.