Программа на C, скомпилированная с cygwin в Windows, работает, ошибка сегментации под Linux. Является ли cygwin GCC «плохим»? - PullRequest
13 голосов
/ 10 февраля 2009

Для моего класса Programming 102 нас попросили предоставить код на C, который компилируется и работает под Linux. На моем жестком диске недостаточно свободного места для установки Linux вместе с Windows, поэтому я использую cygwin для компиляции своих программ.

Самая последняя программа, которую мне приходилось сдавать в компиляции, и прекрасно работает под Cygwin. Он прекрасно компилируется под Linux, но в середине выполнения возникает ошибка сегментации. Я объяснил это аспиранту, который дает нам урок, и он сказал, что версия GCC в Cygwin позволяет компилировать и выполнять неаккуратный код.

Несколько ссылок, которые я нашел через Google, не являются окончательными. Один поток, который я обнаружил, сказал, что причиной сбоя seg в Linux является утечка памяти. Почему это не повлияет на версию Cygwin?

Я бы использовал компьютеры университета, но я не могу использовать на них Subversion, что значительно помешало бы моим усилиям. (Я новичок в кодировании и часто должен иметь возможность вернуться к X ревизиям назад).

Является ли версия GCC cygwin более «слабой» с кодом, который он компилирует? Если да, есть ли какие-то очевидные проблемы, на которые нужно обратить внимание при кодировании? Есть ли альтернатива для написания кода, который будет работать под Linux?

Редактировать

Спасибо за ответы. Я не был достаточно откровенен в своем первоначальном посте: ошибка в моем коде была для меня чем-то большим (я новичок в программировании и, в конце концов, очень зеленый, когда дело доходит до C). Мой TA подразумевал, что GCC Cygwin является менее надежным компилятором, позволяющим запускать гораздо более небрежный код, чем тот, который есть в GNU / Linux. Мне это показалось странным, поэтому я провел поиск в Интернете, но не смог найти никаких ссылок на этот факт.

Больше, чем обвинять компилятор и мой код, мне было интересно, что может быть причиной запуска программы под Windows и сбоя под Linux. Ответы: разные менеджеры памяти и компоновка кучи / стека в Windows / Linux иллюстрировали это.

Будет ли вывод о том, что GCC cygwin так же «хорош», как GNU / Linux », и это основополагающая операционная система / просто удача, что моя глючная программа работает под одной, а другая не очень правильная?

Что касается публикации исходного кода, это домашнее задание, поэтому я бы предпочел сам найти проблему, если это вообще возможно:)

Редактировать 2

Я принял ответ Джальфа, так как он говорит о том, что заставляет программу работать под Windows, а не под Linux, что я и хотел знать. Спасибо всем, кто внес свой вклад, все они были очень интересными и информативными ответами.

Когда я найду проблему и исправлю ее, я загружу zip-файл со всем исходным кодом этой нерабочей версии, на случай, если кому-то будет интересно узнать, что я, черт возьми, сделал:)

Редактировать 3

Для тех, кто заинтересован в просмотре кода, я обнаружил проблему, и это действительно было связано с указателями. Я пытался вернуть указатель из функции. Указатель, который я пытался вернуть, был объявлен внутри функции и поэтому уничтожался после выполнения функции. Проблемный код закомментирован в строках 22-24.

Не стесняйтесь высмеивать мой код.

/**
*  Returns array of valid searches based on current coordinate
*/
void determine_searches(int row, int col, int last_row, int last_col, int *active_search){
    // define coordinate categories and related valid search directions
    int Library0[] = {2, 3, 4, -1};
    int Library1[] = {4, 5, 6, -1};
    int Library2[] = {2, 3, 4, 5, 6, -1};
    int Library3[] = {0, 1, 2, 3, 4, 5, 6, 7, -1};
    int Library4[] = {0, 1, 2, -1};
    int Library5[] = {0, 6, 7, -1};
    int Library6[] = {0, 1, 2, 6, 7, -1};
    int Library7[] = {0, 1, 2, 3, 4, -1};
    int Library8[] = {0, 4, 5, 6, 7, -1};

    int * Library[] = { 
        Library0, Library1, Library2,
        Library3, Library4, Library5,
        Library6, Library7, Library8,
    };

    // declare (and assign memory to) the array of valid search directions that will be returned
    //int *active_search;
    //active_search = (int *) malloc(SEARCH_DIRECTIONS * sizeof(int));


    // determine which is the correct array of search directions based on the current coordinate
    // top left corner
        int i = 0;
    if(row == 0 && col == 0){
        while(Library[0][i] != -1){
            active_search[i] = Library[0][i];
            i++;
        }
    }
    // top right corner
    else if(row == 0 && col == last_col){
        while(Library[1][i] != -1){
            active_search[i] = Library[1][i];
            i++;
        }
    }
    // non-edge columns of first row
    else if(row == 0 && (col != 0 || col != last_col)){
        while(Library[2][i] != -1){
            active_search[i] = Library[2][i];
            i++;
        }
    }
    // non-edge coordinates (no edge columns nor rows)
    else if(row != 0 && row != last_row && col != 0 && col != last_col){
        while(Library[3][i] != -1){
            active_search[i] = Library[3][i];
            i++;
        }
    }
    // bottom left corner
    else if(row == last_row && col == 0){
        while(Library[4][i] != -1){
            active_search[i] = Library[4][i];
            i++;
        }
    }
    // bottom right corner
    else if(row == last_row && col == last_col){
        while(Library[5][i] != -1){
            active_search[i] = Library[5][i];
            i++;
        }
    }
    // non-edge columns of last row
    else if(row == last_row && (col != 0 || col != last_col)){
        while(Library[6][i] != -1){
            active_search[i] = Library[6][i];
            i++;
        }
    }
    // non-edge rows of first column
    else if((row != 0 || row != last_row) && col == 0){
        while(Library[7][i] != -1){
            active_search[i] = Library[7][i];
            i++;
        }
    }
    // non-edge rows of last column
    else if((row != 0 || row != last_row) && col == last_col){
        while(Library[8][i] != -1){
            active_search[i] = Library[8][i];
            i++;
        }
    }
    active_search[i] = -1;
}

Ответы [ 11 ]

14 голосов
/ 10 февраля 2009

Я не хочу показаться грубым, но, вероятно, ваш код плох, а не компилятор. ;) Подобные проблемы на самом деле встречаются чаще, чем вы думаете, потому что разные ОС и компиляторы будут по-разному организовывать данные вашего приложения в стеке и куче. Первый может быть особенно проблематичным, особенно если вы в конечном итоге перезаписываете память в стеке или ссылаетесь на освобожденную память, которую система решила использовать для чего-то другого. Так что, в принципе, иногда вы можете сойти с рук, но в других случаях ваше приложение задохнется и умрет. В любом случае, если это происходит с ошибками, это потому, что вы пытались сослаться на память, которая вам не была разрешена, так что это скорее «счастливое совпадение», что она не вылетала под другой системой / компилятором.

Но на самом деле, segfault - это segfault, поэтому вместо исправления конфигурации компилятора вместо отладки конфигурации компилятора следует отладить код.

Редактировать : Хорошо, теперь я понимаю, что вы имеете в виду ... Я думал, что вы решаете эту проблему с "X отстой, но Y работает просто отлично!" отношение, но, похоже, это твоя ТА, у кого это есть. ;)

В любом случае, вот еще несколько советов по устранению таких проблем:

  • Ищите арифметику указателей, ссылки / разыменования для возможного "doh!" ошибки. Любое место, где вы добавляете / вычитаете одно (иначе, ошибки забора), особенно подозрительно.
  • Закомментируйте вызовы malloc / free вокруг проблемной области и любых связанных областей, где используются эти указатели. Если код перестает работать, значит, вы движетесь в правильном направлении.
  • Предполагая, что вы, по крайней мере, определили общую область, в которой происходит сбой вашего кода, вставьте туда операторы раннего возврата и найдите точку, где ваш код не падает. Это может помочь найти область где-то между этой точкой и местом, где ваш код действительно падает. Помните, что такой segfault не обязательно может произойти непосредственно в строке кода, где находится ваша ошибка.
  • Используйте инструменты отладки памяти, доступные в вашей системе.
    • В Unix, посмотрите это руководство по отладке памяти в Unix и valgrind profiler (@Sol, спасибо за напоминание об этом)
    • В Visual Studio / Windows ваш хороший друг CrtCheckMemory () оказывается довольно удобным. Кроме того, ознакомьтесь с шаблонами отладки памяти CRT , поскольку они являются одной из самых приятных особенностей работы в VS. Часто достаточно просто оставить открытой вкладку памяти в VS, чтобы диагностировать подобные ошибки, как только вы запомните различные шаблоны.
    • В Mac OSX вы можете установить точку останова на malloc_error_break (из gdb или Xcode), что заставит его отладчик прерывать всякий раз, когда malloc обнаруживает повреждение памяти. Я не уверен, доступно ли это в других версиях Unix, но быстрый поиск в Google, кажется, показывает, что это только для Mac. Кроме того, довольно «экспериментальная» версия valgrind, кажется, существует для OSX .
8 голосов
/ 10 февраля 2009

Как уже говорили другие, вы можете разместить здесь часть своего кода, даже если это не совсем так. Возможно, все еще будет хорошим опытом обучения, чтобы все здесь изучили ваш код и посмотрели, смогут ли они найти причину ошибки.

Но да, проблема в том, что существует так много зависимых от платформы, а также в основном случайных факторов, влияющих на программу на Си. Виртуальная память означает, что иногда доступ к нераспределенной памяти будет работать, потому что вы попали в неиспользуемую часть страницы, которая была выделена в какой-то более ранний момент. В других случаях это будет ошибка, потому что вы попадете на страницу, которая вообще не была выделена вашему процессу. И это действительно невозможно предсказать. Это зависит от того, где была выделена ваша память, на краю страницы или в середине? Это зависит от ОС и диспетчера памяти, а также от того, какие страницы были выделены на данный момент, и ...... Вы понимаете. Разные компиляторы, разные версии одних и тех же компиляторов, разные ОС, разное программное обеспечение, драйверы или аппаратное обеспечение, установленное в системе, все может измениться, независимо от того, возникает ли ошибка при доступе к нераспределенной памяти.

Что касается заявления TA о том, что cygwin более «слабый», то это мусор по одной простой причине. Ни один компилятор не поймал ошибку! Если бы «родной» компилятор GCC действительно был менее слабым, он дал бы вам ошибку во время компиляции. Segfaults не генерируются компилятором. Компилятор мало что может сделать, чтобы убедиться в том, что вместо программы, которая, по-видимому, работает, вы получаете ошибку сегмента.

4 голосов
/ 10 февраля 2009

Я не слышал ничего особенного о странности GCC в Cygwin, но в вашем случае, вероятно, было бы неплохо использовать параметр командной строки -Wall для gcc, чтобы показать все предупреждения, чтобы увидеть, находит ли он что-либо, что может быть причиной ошибки в вашем коде.

3 голосов
/ 10 февраля 2009

У вас определенно есть ошибка где-то в вашем коде. Вполне возможно, что диспетчер памяти Windows более слабый, чем менеджер памяти Linux. В Windows вы можете делать плохие вещи с памятью (например, перезаписывать границы массивов, утечки памяти, двойное освобождение и т. Д.), Но это позволяет вам избежать неприятностей. Известная история, связанная с этим, может быть найдена в http://www.joelonsoftware.com/articles/APIWar.html (поищите «SimCity» в этой (несколько длинной) статье).

2 голосов
/ 10 февраля 2009

Это почти наверняка ошибка указателя или переполнение буфера, возможно, неинициализированная переменная.

Неинициализированный указатель обычно указывает ни на что, но иногда он указывает на что-то; чтение из него или запись в него, как правило, приводит к сбою программы, но, опять же, МОЖЕТ не делать этого.

Запись или чтение из освобожденной памяти - это та же история; вам это может сойти с рук, а может и нет.

Эти ситуации зависят от того, как именно составлены стек, куча и что делает среда выполнения. Вполне возможно создать плохую программу, которая работает с одной комбинацией компилятор / среда выполнения, а не с другой, просто потому, что она перезаписывает что-то, что не имеет значения (так много), или что неинициализированная переменная «случается» содержит действительную значение для контекста, в котором оно используется.

2 голосов
/ 10 февраля 2009

Cygwin-версия gcc может иметь другие флаги по умолчанию и подправленные настройки (например, wchar_t, равный 2 байтам), но я сомневаюсь, что он более «слаб» в коде, и даже в этом случае - ваш код не должен падать. Если это так, то, скорее всего, в вашем коде есть ошибка, которую нужно исправить. Например, ваш код может зависеть от определенного размера wchar_t или может выполнять код, который совсем не гарантированно работает, как запись в строковые литералы.

Если вы пишете чистый код, он также работает на Linux. В настоящее время я использую Firefox и рабочий стол KDE, которые вместе состоят из миллионов строк C ++, и я не вижу, чтобы эти приложения зависали:)

Я рекомендую вам вставить свой код в ваш вопрос, чтобы мы могли посмотреть, что происходит не так.

Тем временем вы можете запустить свою программу на gdb, который является отладчиком для linux. Вы также можете скомпилировать все включенные проверки с ошибками и все предупреждения. mudflaps проверяет ваш код во время выполнения на наличие различных нарушений:

[js@HOST2 cpp]$ cat mudf.cpp
int main(void)
{
  int a[10];
  a[10] = 3;  // oops, off by one.
  return 0;
}
[js@HOST2 cpp]$ g++ -fmudflap -fstack-protector-all -lmudflap -Wall mudf.cpp
[js@HOST2 cpp]$ MUDFLAP_OPTIONS=-help ./a.out
  ... showing many options ...
[js@HOST2 cpp]$ ./a.out 
*******                 
mudflap violation 1 (check/write): time=1234225118.232529 ptr=0xbf98af84 size=44
pc=0xb7f6026d location=`mudf.cpp:4:12 (main)'                                   
      /usr/lib/libmudflap.so.0(__mf_check+0x3d) [0xb7f6026d]                    
      ./a.out(main+0xb9) [0x804892d]                                            
      /usr/lib/libmudflap.so.0(__wrap_main+0x4f) [0xb7f5fa5f]                   
Nearby object 1: checked region begins 0B into and ends 4B after                
mudflap object 0x9731f20: name=`mudf.cpp:3:11 (main) int a [10]'                
bounds=[0xbf98af84,0xbf98afab] size=40 area=stack check=0r/3w liveness=3        
alloc time=1234225118.232519 pc=0xb7f5f9fd                                      
number of nearby objects: 1                                                     
*** stack smashing detected ***: ./a.out terminated                             
======= Backtrace: =========
....

Существует множество проверок с помощью грязевых флагов, и вышеприведенное запускает a.out с использованием параметров по умолчанию. Другим инструментом, который помогает при таких ошибках, является valgrind, который также может помочь вам найти утечки или устранить их, как описано выше. Если для переменной среды "MALLOC_CHECK_" задано значение 1, сообщения также будут распечатаны. См. Справочную страницу malloc для других возможных значений для этой переменной.

Для проверки, где происходит сбой вашей программы, вы можете использовать gdb:

[js@HOST2 cpp]$ cat test.cpp
int main() {
    int *p = 0;
    *p = 0;
}
[js@HOST2 cpp]$ g++ -g3 -Wall test.cpp
[js@HOST2 cpp]$ gdb ./a.out
...
(gdb) r
Starting program: /home/js/cpp/a.out

Program received signal SIGSEGV, Segmentation fault.
0x080483df in main () at test.cpp:3
3           *p = 0;
(gdb) bt
#0  0x080483df in main () at test.cpp:3
(gdb)

Скомпилируйте ваш код с помощью -g3, чтобы включить много отладочной информации, чтобы gdb мог помочь вам найти точные строки, в которых происходит сбой вашей программы. Все вышеперечисленные методы в равной степени применимы для C и C ++.

1 голос
/ 10 февраля 2009

Ошибка сегментации является результатом доступа к памяти по несуществующему (или ранее освобожденному) адресу. Что я нахожу очень интересным, так это то, что этот код НЕ работает под управлением Cygwin. Это может означать, что ваша программа использовала дикий указатель на адресное пространство некоторых других процессов и фактически могла прочитать его (задыхаться), или (более вероятно) код, который фактически вызвал segfault, не был достигнут, пока программа не была запущена под Linux .

Я рекомендую следующее:

  1. Вставьте свой код, так как это очень интересная проблема
  2. Отправить копию этого разработчику cygwin
  3. Получите дешевый VPS для Linux, если вам потребуется создавать больше программ для Linux, это значительно облегчит вашу жизнь.

После того, как вы работаете под Linux (то есть, зашли на ваш VPS), попробуйте работать со следующими программами:

  • GDB
  • Valgrind
  • Трассирование

Кроме того, вы можете попробовать библиотеки, такие как электрический забор, чтобы ловить такие вещи, которые происходят во время работы вашей программы.

Наконец, убедитесь, что -Wall передан gcc, вы хотите, чтобы предупреждения передавались.

1 голос
/ 10 февраля 2009

Некоторые подсказки:

  1. Разместите свой код. Держу пари, вы получите хороший вклад, который сделает вас лучшим программистом.

  2. Включите предупреждения с помощью опции -wall и исправьте все обнаруженные проблемы. Опять же, это может помочь вам стать лучшим программистом.

  3. Пройдите по коду с помощью отладчика. Помимо того, что он поможет вам понять, в чем проблема, он поможет вам стать лучшим программистом.

  4. Продолжать использовать Subversion или другую систему управления исходным кодом.

  5. Никогда не обвиняйте компилятор (или ОС или аппаратное обеспечение), пока вы не уверены, что точно определили проблему. Даже в этом случае будьте осторожны с собственным кодом.


GCC в Linux - это исходный код, идентичный GCC в Cygwin. Различия между платформами существуют из-за уровня эмуляции Cygwin POSIX и базового API Windows. Возможно, что дополнительные слои более простительны, чем базовое оборудование, но на это не стоит рассчитывать.

Поскольку это домашняя работа, я бы сказал, что размещение кода - еще лучшая идея. Что может быть лучше, чем учиться у профессиональных программистов? Тем не менее, я рекомендую указывать любые предложения, которые вы вносите, в ближайших комментариях.

1 голос
/ 10 февраля 2009

Ошибка сегментации означает, что вы пытались получить доступ к памяти, которую вы не могли, что обычно означает, что вы пытались разыменовать нулевой указатель или дважды удалили память или получили дикий указатель. Есть две причины, по которым вы могли бы показаться хорошими в Cygwin, а не в Linux: либо это была проблема с менеджерами памяти, либо вам повезло больше на одном из них, чем на другом. Это почти наверняка ошибка с вашим кодом.

Чтобы исправить это, посмотрите на использование вашего указателя. Подумайте о замене умных указателей на необработанные указатели. Попробуйте выполнить поиск для удаления и обнулить указатель сразу после этого (можно безопасно удалить нулевой указатель). Если вы можете получить трещину в Linux, попробуйте получить трассировку стека через gdb и посмотрите, есть ли что-то явно не так в той строке, где это происходит. Изучите, как вы используете все указатели, которые не инициализированы. Если у вас есть доступ к инструменту отладки памяти, используйте его.

1 голос
/ 10 февраля 2009

Делаете ли вы какие-либо специфичные для платформы допущения, например размер типов данных , выравнивание структуры данных в struct с или порядковый номер ?

...