#ubuntu
#ubuntu
Вопрос:
Я пытаюсь изучить многопоточное и многопроцессорное программирование. Я очень новичок как в многопоточном / обработанном программировании, так и в среде Ubuntu. Я работал над приведенным ниже кодом добрых 10 часов и исправил все ошибки и предупреждения. Я начал кодировать это с помощью xCode, и он работает отлично и делает именно то, что я хочу, чтобы он делал, без каких-либо предупреждений или ошибок в этой среде. Но при попытке скомпилировать и запустить на Ubuntu я получаю ошибку сегментации (сброс ядра) Я не мог понять, какая часть кода привела к этой ошибке. Есть идеи, какая часть может вызвать ошибку? или почему я это получаю? Насколько я помню, в Linux нет ядра? Заранее большое вам спасибо!
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <err.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
int pid, i, rc, pid1, counter;
char* iterator[500];
char* file[500];
enum {
WALK_OK = 0,
WALK_BADPATTERN,
WALK_BADOPEN,
};
int walker(const char *dir, const char *pattern)
{
struct dirent *entry;
regex_t reg;
DIR *d;
counter=0;
if (regcomp(amp;reg, pattern, REG_EXTENDED | REG_NOSUB))
return WALK_BADPATTERN;
if (!(d = opendir(dir)))
return WALK_BADOPEN;
while ((entry = (readdir(d))) ){
if (!regexec(amp;reg, entry->d_name, 0, NULL, 0)){
puts(entry->d_name);
file[counter]=entry->d_name;
counter=counter 1;}
}
closedir(d);
regfree(amp;reg);
return counter;
}
void* project_statistics(int i){
FILE* f;
// size_t len;
char* line;
int read[3];
int arr[1000];
int p, m, fnl;
int counter2=0;
f=fopen(iterator[i], "r");
if (f==NULL) {
err(1, "%s", iterator[i]);
}
while((line=fgets((char*)read,sizeof(read),f))){
sscanf(line, "%d %d %d",amp;p, amp;m, amp;fnl);
arr[counter2]= p;
counter2 ;
}
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf, arr, (counter2) * sizeof(int));
//sort array;
int k, l, tmp;
for (k = 1; k < counter2; k ) {
l = k;
while (l > 0 amp;amp; firstHalf[l - 1] > firstHalf[l]) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l- 1];
firstHalf[l- 1] = tmp;
l--;
}
}
printf("course %d project median: %d, project min: %d, project max: %dn", i 1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
if(!feof(f)){
err(1, "getIn");
}
pthread_exit(NULL);
}
void* midterm_statistics(int i){
FILE* f;
int read[3];
char* line;
int arr2[1000];
int p, m, fnl;
int counter2=0;
f=fopen(iterator[i], "r");
if (f==NULL) {
err(1, "%s", iterator[i]);
}
while((line=fgets((char*)read,sizeof(read),f))){
sscanf(line, "%d %d %d",amp;p, amp;m, amp;fnl);
arr2[counter2]=m;
counter2 ;
}
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf, arr2, (counter2) * sizeof(int));
//sort array;
int k, l, tmp;
for (k = 1; k < counter2; k ) {
l = k;
while (l > 0 amp;amp; firstHalf[l - 1] > firstHalf[l]) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l- 1];
firstHalf[l- 1] = tmp;
l--;
}
}
printf("course %d project median: %d, project min: %d, project max: %dn", i 1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
if(!feof(f)){
err(1, "getIn");
}
pthread_exit(NULL);
}
void* final_statistics(int i){
FILE* f;
char* line;
int arr3[1000];
int read[3];
int p, m, fnl;
int counter2=0;
f=fopen(iterator[i], "r");
if (f==NULL) {
err(1, "%s", iterator[i]);
}
while((line=fgets((char*)read,sizeof(read),f))){
sscanf(line, "%d %d %d",amp;p, amp;m, amp;fnl);
arr3[counter2]=fnl;
counter2 ;
}
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf, arr3, (counter2) * sizeof(int));
//sort array;
int k, l, tmp;
for (k = 1; k < counter2; k ) {
l = k;
while (l > 0 amp;amp; firstHalf[l - 1] > firstHalf[l]) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l- 1];
firstHalf[l- 1] = tmp;
l--;
}
}
printf("course %d project median: %d, project min: %d, project max: %dn", i 1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
if(!feof(f)){
err(1, "getIn");
}
pthread_exit(NULL);
}
int main(int argc, const char * argv[]) {
char k[500];
int counter1=walker("/home/ey/Desktop/sampleFolder/", ".\.txt");
for (i=0; i<counter1; i ) {
strcpy(k, "/home/ey/Desktop/sampleFolder/");
strcat(k, file[i]);
iterator[i]=strdup(k);
printf("%s",iterator[i]);
}
printf("nMaster is startingn");
pthread_t tid1[counter1], tid2[counter1], tid3[counter1];
pthread_attr_t attr;
pthread_attr_init(amp;attr);
pthread_attr_setdetachstate(amp;attr, PTHREAD_CREATE_JOINABLE);
printf("nslave1 startn");
printf("n~Project Statistics~n");
sleep(2);
for (i=0; i<counter1; i ) {
rc=pthread_create(amp;tid1[i], amp;attr, (void*)*project_statistics,(void*)(intptr_t)i);
}
sleep(2);
printf("nslave1 donen");
printf("nslave2 startn");
printf("n~Midterm Statistics~n");
pid=fork();
sleep(2);
if (pid==0) {
for (i=0; i<counter1; i ) {
rc=pthread_create(amp;tid2[i], amp;attr,(void*)*midterm_statistics, (void*)(intptr_t)i);
}
sleep(2);
printf("nslave2 donen");
printf("nslave3 startn");
printf("n~Final Statistics~n");
}
sleep(2);
pid1=fork();
sleep(2);
if ((pid1==0)amp;amp;(pid==0)) {
for (i=0; i<counter1; i ) {
rc=pthread_create(amp;tid3[i], amp;attr, (void*)*final_statistics, (void*)(intptr_t)i);
}
sleep(2);
printf("nslave3 donen");
printf("nMaster is donen");
}
sleep(1);
pthread_attr_destroy(amp;attr);
pthread_exit(NULL);
}
Комментарии:
1. «Файл ядра» — это копия памяти процесса плюс некоторая дополнительная информация. Это записывается в файл и может использоваться для отладки программы. Если вы не можете найти файл ядра, проверьте ‘ulimit -c’. Возможно, вам потребуется изменить свой пользовательский лимит, например, на 50000. (запустите команду ulimit -c 50000). Когда у вас есть и исполняемый файл, и файл ядра, запустите ‘gdb exefile corefile’, чтобы запустить отладчик gnu. Затем выполните команду ‘backtrace’ внутри gdb. Надеюсь, это должно показать, где ваш код завершается с ошибкой. Используйте параметры gcc -O0 -ggdb для получения информации об отладке.
2. Я никогда раньше не использовал отладчик gnu. Но когда я ввел ulimit -c, я получаю 0, это нормально? также, когда я ввел ulimit -c 50000, ничего не появилось.
3. Ничего не «отображается», но при следующем сбросе ядра вашей программы будет сгенерирован файл ядра. Предупреждение: Gdb — не самый простой отладчик, доступный в Linux. Возможно, вы захотите поискать альтернативы. ddd может быть лучше.
4. Спасибо! Я выполнил шаги, о которых вы упомянули, и получил что-то __strcat_sse2_unaligned () в ../ sysdeps/ x86_64 / multiarch / strcpy-sse2-unaligned.S: 296 0x00005608e09c06ae в main (), поэтому я предполагаю, что что-то, что я сделал в main, вызывает сбой, но есть ли способ узнать точную точку?
5. В
gdb
, когда он останавливается из-за сбоя в сегменте [или когда вы используете его для проверки файла ядра], введитеtb
. Это дает вам обратную трассировку стека. Некоторые из фреймов будут вашим кодом. Наиболее близким к ошибке является обычный подозреваемый. Убедитесь, что вы компилируете с-g
[и, необязательно, с-O0
vs-O2
]
Ответ №1:
В main
, ваш strcat
сбой.
Адрес источника — file[i]
. file
это глобальный массив char *
указателей. Но, по-видимому, это никогда ни к чему не инициализируется.
Таким образом, strcat
вызов будет иметь второй аргумент NULL
, который вызывает ошибку segfault.
Это может произойти, если walker
возвращает ненулевое значение, которое было бы, если бы каталог не существовал (т. Е. возвращаемое значение равно WALK_BADOPEN
). Это может объяснить, почему это работает в одной системе, но не в другой (т. Е. каталог существует в одной, но не в другой).
Итак, walker
используется возврат кода ошибки, но main
это возвращаемое значение используется в качестве подсчета. Эта логика неверна. Я полагаю, вам нужно будет изменить возвращаемое значение walker
или main
получить счетчик другим способом.
Простой способ исправить это — присвоить кодам ошибок отрицательные значения и main
проверить это. Затем walker
можно вернуть количество правильно.
Итак, если каталог не существует, возвращаемое значение равно 2. Цикл в main
завершится с ошибкой file[0]
, потому что ничего в file
не было установлено ни на что.
Обновить:
Но на данный момент, поскольку я знаю, что каталог существует, могу ли я пытаться открыть его неправильным способом?
Не существует «неправильного» способа использования opendir
— он либо открывается, либо завершается сбоем, с которым вы уже справляетесь.
Но внутри walker
вы не можете полагаться на d_name
значение от итерации цикла к итерации, поэтому вам приходится использовать strdup
.
Изменение:
file[counter] = entry->d_name;
В:
file[counter] = strdup(entry->d_name);
Кроме того, вам следует ограничить проверку по максимуму для file
(например, в настоящее время только 500)
ОБНОВЛЕНИЕ # 2:
В ваших потоковых функциях вы выполняли fgets
into read
[не удачный выбор из-за read
функции libc]. Но это было:
int read[3];
Итак, длина линейного буфера составляла всего 12 байт. Это может привести к fgets
считыванию строки как двух частично разделенных строк. Это может привести к arr
переполнению массива
Я изменил это на:
char buf[1000];
Я объединил реплицированный код потоковых функций в общий.
Обратите внимание, что firstHalf
было выделено, но никогда не освобождалось. Итак, это была «утечка». Я добавил free
вызов для этого.
Также обратите внимание, что не было ничего, fclose(f)
что могло бы привести к fopen
возврату NULL
(т. Е. другого источника для segfault).
Я также переработал объединение потоков и fork
логику и добавил waitpid
. Также обратите внимание на добавление exit(0)
в дочерний код форка.
Пытаясь разобраться, я все упрощал, так что следующее — это справедливая доработка, которая поначалу может показаться немного «чуждой» [пожалуйста, простите за беспричинную очистку стиля]:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <err.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#if 1
#define MYDIR "/home/ey/Desktop/sampleFolder/"
#else
#define MYDIR "/tmp/data/"
#endif
#define dbgprt(_fmt...)
do {
if (opt_dbg)
printf(_fmt);
} while (0)
int opt_dbg;
int pid;
int i;
int rc;
int pid1;
int counter;
char *iterator[500];
char *file[500];
enum {
WALK_OK = 0,
WALK_BADPATTERN = -1,
WALK_BADOPEN = -2,
};
int
walker(const char *dir,const char *pattern)
{
struct dirent *entry;
regex_t reg;
DIR *d;
counter = 0;
if (regcomp(amp;reg,pattern,REG_EXTENDED | REG_NOSUB))
return WALK_BADPATTERN;
d = opendir(dir);
if (d == NULL)
return WALK_BADOPEN;
while (1) {
entry = readdir(d);
if (entry == NULL)
break;
if (!regexec(amp;reg,entry->d_name,0,NULL,0)) {
puts(entry->d_name);
file[counter] = strdup(entry->d_name);
counter = counter 1;
}
}
closedir(d);
regfree(amp;reg);
return counter;
}
void *
thread_common(void *arg,int column)
{
intptr_t i = (intptr_t) arg;
FILE *f;
// size_t len;
char *line;
int data[3];
char buf[1000];
int arr[1000];
int counter2 = 0;
f = fopen(iterator[i],"r");
if (f == NULL) {
err(1,"%s",iterator[i]);
}
dbgprt("DEBUG reading ...n");
while (1) {
line = fgets(buf,sizeof(buf),f);
if (line == NULL)
break;
sscanf(line,"%d %d %d",amp;data[0],amp;data[1],amp;data[2]);
arr[counter2] = data[column];
counter2 ;
dbgprt("DEBUG line %d %sn",counter2,iterator[i]);
if (counter2 >= 1000) {
printf("overflow %sn",iterator[i]);
exit(1);
}
}
if (!feof(f)) {
err(1,"getIn");
}
fclose(f);
int *firstHalf = malloc((counter2) * sizeof(int));
memcpy(firstHalf,arr,(counter2) * sizeof(int));
// sort array;
int k,
l,
tmp;
dbgprt("DEBUG sorting ...n");
for (k = 1; k < counter2; k ) {
for (l = k; (l > 0) amp;amp; (firstHalf[l - 1] > firstHalf[l]); l--) {
tmp = firstHalf[l];
firstHalf[l] = firstHalf[l - 1];
firstHalf[l - 1] = tmp;
l--;
}
}
printf("course %ld project median: %d, project min: %d, project max: %dn",
i 1,firstHalf[counter2 / 2],firstHalf[0],firstHalf[counter2 - 1]);
free(firstHalf);
return (void *) 0;
}
void *
project_statistics(void *arg)
{
return thread_common(arg,0);
}
void *
midterm_statistics(void *arg)
{
return thread_common(arg,1);
}
void *
final_statistics(void *arg)
{
return thread_common(arg,2);
}
int
main(int argc,char **argv)
{
intptr_t i;
char *cp;
char krkt[500];
--argc;
argv;
for (; argc > 0; --argc, argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'd':
opt_dbg = 1;
break;
default:
break;
}
}
int counter1 = walker(MYDIR,".\.txt");
dbgprt("main: walker returned %dn",counter1);
if (counter1 <= 0)
exit(1);
for (i = 0; i < counter1; i ) {
strcpy(krkt,MYDIR);
if (file[i] == NULL)
exit(3);
strcat(krkt,file[i]);
iterator[i] = strdup(krkt);
printf("%sn",iterator[i]);
}
printf("nMaster is startingn");
pthread_t tid1[counter1];
pthread_t tid2[counter1];
pthread_t tid3[counter1];
pthread_attr_t attr;
pthread_attr_init(amp;attr);
pthread_attr_setdetachstate(amp;attr,PTHREAD_CREATE_JOINABLE);
printf("nslave1 startn");
printf("n~Project Statistics~n");
//sleep(2);
for (i = 0; i < counter1; i )
rc = pthread_create(amp;tid1[i],amp;attr,project_statistics,(void *) i);
for (i = 0; i < counter1; i )
rc = pthread_join(tid1[i],NULL);
printf("nslave1 donen");
pid = fork();
if (pid == 0) {
printf("nslave2 startn");
printf("n~Midterm Statistics~n");
for (i = 0; i < counter1; i )
rc = pthread_create(amp;tid2[i],amp;attr,midterm_statistics,(void *) i);
for (i = 0; i < counter1; i )
rc = pthread_join(tid2[i],NULL);
printf("nslave2 donen");
exit(0);
}
pid1 = fork();
if (pid1 == 0) {
printf("nslave3 startn");
printf("n~Final Statistics~n");
for (i = 0; i < counter1; i )
rc = pthread_create(amp;tid3[i],amp;attr,final_statistics,(void *) i);
for (i = 0; i < counter1; i )
rc = pthread_join(tid3[i],NULL);
printf("nslave3 donen");
exit(0);
}
waitpid(pid,NULL,0);
waitpid(pid1,NULL,0);
printf("nMaster is donen");
pthread_attr_destroy(amp;attr);
return 0;
}
ОБНОВЛЕНИЕ # 3:
Также начало main мне не очень понятно, почему мы ждем ‘d’ и делаем switch-case, зачем нужно добавлять argv и argc в код? Поскольку код в некотором роде зависит от argv и argc, может ли мой способ компиляции вызывать проблему?
argc/argv
Код просто анализирует аргументы опции. Это довольно стандартный шаблон.
В этом случае, если вы это сделаете, ./main -d
это установится opt_d
. Затем dbgprt
макрос проверяет это и, если установлен, выполняет printf
. Таким образом, все, printf
относящиеся к отладочным выводам, были изменены на dbgprt
.
Это не изменяет выполнение программы, просто добавляет дополнительный отладочный вывод. Вы можете добавить больше dbgprt
, если хотите.
И вы можете добавить свои собственные параметры командной строки, добавив их в switch/case
.
Этот метод для «отладки printf» довольно распространен. Я предпочитаю использовать это gdb
там, где это возможно. Лично я пытаюсь вызвать программу с помощью gdb
только тогда, когда у меня «серьезная» ошибка, такая как segfault. gdb
могу ли я определить строку сбоя. Затем я добавляю такие вещи, как assert
отладочные распечатки и т.д., Чтобы заранее устранить проблему.
Я вроде понимаю логику, но мне не удалось запустить код. Я имею в виду, что он все еще работает над xcode.
Исправленные мной ошибки применимы и к xcode
версии.
Но в Linux это не выдает никаких ошибок или предупреждений, но при вводе ./ main Я ничего не получаю…
Если вы работаете на Linux, используйте -d
. Затем обратите внимание на вывод первого dbgprt
после вызова walker
.
Мое лучшее предположение заключается в том, что walker
возвращает отрицательное значение (т. Е. Каталог не существует — шаблон в порядке, так что это то, что осталось). Или возвращает 0, указывающее, что в каталоге нет файлов или нет файлов, соответствующих шаблону.
Программа должна завершиться с exit(1)
, поэтому проверьте код ошибки (например echo $?
)
Вы могли [и, подумав об этом, вероятно, должны] сначала изменить это dbgprt
обратно на printf
, чтобы оно всегда печаталось, даже если вы не указываете -d
. Таким образом, вы не получите «тихий» сбой, но программа заранее сообщит вам, если что-то не так.
Один из способов помочь в отладке этого — использовать gdb
. Выполняйте gdb ./main
. Затем сделайте, b walker
чтобы установить точку останова на walker
, Затем введите run
. gdb
остановит программу при первой инструкции walker
.
Затем вы можете ввести s
в «один шаг» программы. Вы можете продолжать повторять это. Когда у вас появится запрос, вы можете p
выполнить команду gdb для печати переменных. Это позволит вам увидеть, что walker
делает.
Когда в строке есть вызов libc
функции, такой как opendir
, readdir
, strdup
и т.д., Выполнение s
будет пытаться выполнить эти функции одним шагом. Длинный и не очень полезный. Итак, в такой строке используйте n
вместо этого. Если вы делаете это s
по ошибке, вы можете ввести finish
.
Когда вы решите, что выполнили достаточно шагов, вы можете ввести c
, который продолжит выполнение программы на полной скорости.
gdb
имеет много команд, приведенные выше — лишь некоторые. У него есть встроенная справка, поэтому введите help
в командной строке. Или, help b
и т.д. Доступно множество руководств.
Комментарии:
1. Но на данный момент, поскольку я знаю, что каталог существует, могу ли я пытаться открыть его неправильным способом?
2. Спасибо! Я внес изменение, о котором вы упомянули выше (strdup), но я все еще получаю ошибку сегментации. Поскольку вы сказали, что это может произойти, когда нет каталога для открытия, я дважды проверил, правильно ли я указываю отформатированный путь к функции walker, и это так. Я также пытался инициализировать файл char * [500], и единственное, что не выдает ошибку, — это инициализация с помощью {NULL}, что для меня не имеет никакого смысла..
3. Я получаю разные результаты в своей системе, но этого следовало ожидать. Можете ли вы отредактировать свой вопрос и опубликовать файл данных. Или, каким должен быть диапазон значений для трех значений grade [я могу сгенерировать тестовые файлы]. Я активно редактирую здесь, и синхронизация / ожидание потоков и процессов нуждается в доработке. Итак, можете ли вы отредактировать свой вопрос, чтобы объяснить общий замысел. Выполнение
sleep
после первого pthread_create не является детерминированным. Лучше зацикливатьсяpthread_join
. Это приводит к завершению всех потоков slave1 перед запуском потоков slave2. Это то, чего вы хотите?4. По умолчанию можно объединить, поэтому не требуется. Я нашел другие потенциальные возможности segfault. Я объединяю код потока в общую функцию, потому что разница заключается только в том, какой столбец данных используется (т. Е. 1, 2 или 3). В противном случае все функции потока идентичны и копируют друг друга.