LINUX.ORG.RU

Использование глобальных переменных в С


2

3

Все вечер добрый. Господа всея знающие, помогите пожалуйста разобраться с проблемой: есть код tty.h

#ifndef __TTY_H__
#define __TTY_H__

#include <types.h>

#define VIDEO_WIDTH     80          //ширина экрана
#define VIDEO_HEIGHT    25          //высота экрана
#define VIDEO_SIZE      VIDEO_WIDTH * VIDEO_HEIGHT * 2
#define VIDEO_RAM       0xb8000     //адрес видеопамяти

void init_tty();
extern void set_text_attribute(ui8);
extern void clrscr();
extern void putchar(ui8);
extern void puts(ui8*);

#endif // __TTY_H__

и tty.c
#include <tty.h>

volatile static ui32     cursor;          //положение курсора
volatile static ui8      attribute;       //текущий аттрибут символа

//Инициализация tty
void init_tty() {
    cursor = 0;
    attribute = 3;
}

//Смена текущего аттрибута символа
extern void set_text_attribute(ui8 c) {
    attribute = c;
}

//Очистка экрана
extern void clrscr() {
    ui8      *video = VIDEO_RAM;
    //attribute = 0x35;

    for( ui32 i = 0; i < VIDEO_SIZE; i += 2 ) {
        video[ i ] = attribute;
        video[ i + 1] = ' ';
        // video[ i ] = 0x0f;
        // video[ i + 1] = attribute;
    }
    
    cursor = 0;
}

//Вывод одного символа в режиме телетайпа
extern void putchar(ui8 ch) {
    ui8     *video = VIDEO_RAM;
    ui32    i;

    switch( ch ) {
        case '\n': //Если это символ новой строки
            cursor += VIDEO_WIDTH;
            cursor -= cursor % VIDEO_WIDTH;
            break;

        default:
            video[ cursor * 2 + 1 ] = ch;
            cursor += 1;
            break;
    }

    //Если курсор вышел за границу экрана, сдвинем экран вверх на одну строку
    if( cursor > VIDEO_WIDTH * VIDEO_HEIGHT ) {
        for( i = VIDEO_WIDTH * 2; i <= VIDEO_WIDTH * VIDEO_HEIGHT * 2 + VIDEO_WIDTH * 2; i++ ) {
            *( video + i - VIDEO_WIDTH * 2 ) = *( video + i );
        }
        cursor-=VIDEO_WIDTH;
    }
}

//Вывод строки, заканчивающейся нуль-символом
extern void puts(ui8 *s) {
    init_tty();
    while(*s) {
        putchar(*s);
        s++;
    }
}

Обе глобальные переменные (cursor и attribute) ну никак не хотят менять свои значения, в коде функции clrscr (закоментил) производил проверку присвоения значения переменной, но на экран все равно попадает '\0'

компилю с такими флагами:

-O0 -fomit-frame-pointer -ffreestanding -finline-functions -nostdinc -fno-builtin -nostdlib -std=c99
через gcc-4.8 (пробовал 4.6 и 4.4)

з. ы. Ах да, как вы уже наверное догадались, все это делается в программе, запущенной из-под самописного загрузчика, тестирую на vmware

Ответ на: Shit! от Eddy_Em

errno это обычно макрос, подставляющий (грубо говоря) вызов функции, читающей значение из нужного (особенного) участка памяти трэда.

yoghurt ★★★★★
()
Ответ на: комментарий от Eddy_Em

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

А вот велосипеды с квадратными колёсами знаешь почему не нужны? Да просто потому, что по нашим дорогам лучше на круглых ездить. Они лучше всего приспособлены к нашим рандомным ямам. Если не знаешь как делать — делай по стандарту. Если не знаешь, можно-ли дать доступ  — запрети доступ. Дело в том, что ты никогда не подгадаешь под другого говнокодера, потому исходи из предположения, что другой говнокодер делает всё неправильно. Даже если другой говнокодер — ты сам завтра.

emulek
()
Ответ на: комментарий от kike

Если автор правильно грузит ядро, как указано в скрипте линкера, начиная с двух МБ, то попасть на memory mapped port он никак не может.

Вот именно: «если». А я пока что так и не узнал, где хотя бы переход в защищённый режим делается. grep -Ri gdt по исходникам тоже ничего приятного не выдал.

Miguel ★★★★★
()
Ответ на: комментарий от Eddy_Em

Думаю, если по гнутой сишной библиотечке пройтись, там все-таки найдется достаточное количество глобальных переменных. А уж ведро ими явно под завязку забито!

ты так говоришь, как будто-бы в этом есть что-то хорошее...

emulek
()
Ответ на: комментарий от emulek

Можно подумать, в этом есть что-то плохое!

Естественно, "совсем" глобалить переменные на запись-чтение не стоит: лучше в оберточку положить. Но таки очень часто нужно в том или ином сишном файле некоторое количество переменных сделать глобальными. На всякий случай можно их еще static объявить, чтобы не было путаницы, если в каком-то другом файле заведешь глобальную переменную с таким же именем.

Да, говнокодер я тот еще ;)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Естественно, «совсем» глобалить переменные на запись-чтение не стоит: лучше в оберточку положить.

мля... дык в том и суть, что я изначально против безобёрточных, как у ТСа.

Но таки очень часто нужно в том или ином сишном файле некоторое количество переменных сделать глобальными. На всякий случай можно их еще static объявить, чтобы не было путаницы, если в каком-то другом файле заведешь глобальную переменную с таким же именем.

вот это — совсем другое дело. Только это не «глобальные», а вполне локальные для данного модуля трансляции. Не нужно выносить сор из избы.

emulek
()
Ответ на: комментарий от emulek

Думаю, стоит добавить, что даже к «доверенным» переменным и кондициям не может быть доверия, пока все ассерты не пройдут испытания временем и отделом тестирования. Проблема глобального контекста это всего лишь следствие отсутствия проверки инвариантов в рантайме, что есть более глобальная проблема, извините за каламбур.

arturpub ★★
()
Ответ на: комментарий от arturpub

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

проверка инвариантов в рантайме дорого стоит, и потому не нужна. ИМХО.

emulek
()
Ответ на: комментарий от invy

Ты давай по теме пиши, профи, а не загаживай интересную тему.

по теме я уже высказался. Что именно тебе непонятно?

emulek
()
Ответ на: комментарий от emulek

в каком сообщении?
«глобальные переменные - говнокод» - не ответ. это как «топор - лучшее средство от головной боли».

invy ★★★★★
()
Ответ на: комментарий от invy

«глобальные переменные - говнокод» - не ответ.

дык тебе наверное непонятно, как от них избавится? Или ты не понимаешь, какой в них вред? Или ты просто против, ибо я так считаю?

emulek
()
Ответ на: комментарий от emulek

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

В продукт по идее ассерты не должны входить, но в жизни тот же гтк например никогда их не отключает (он перестает абортиться, но все равно мычит в stderr). И это доставляет, т.к. вместо «double-free or corrupt, lol» ты получаешь нормальное сообщение: где, как и что было не так. Если сплюсовать все время, которое я провел в отладке рваных куч и стеков, то вряд ли ассерты в моих поделиях когда-либо меня уделают.

Что касается сабжевого кода, то ui32 cursor в real-mode должен бы проверяться на попадание в буфер, иначе можно записать что угодно и кому угодно, там 0xC0000 совсем рядом.

arturpub ★★
()
Ответ на: комментарий от emulek

1. Глобальные переменные - фича языка и топикстартер ей пользуется.
2. В данном коде ошибка связанна с глобальными переменными. Вопрос, почему в них значение не меняется.

Ответ на вопрос, почему в них значение не меняется я от тебя не слышу.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от emulek

На сишке без них писать не удобно. В сишке нет этого: ctx_t a; ctxf_t ctx_f(cxtf_t f, ctx_t t) { ctx_t new_f(void) {return f(t);} return new_f;} Мне пацаны говорили как это называется - я забыл.

Как-то так это можно запилить через статик-кастыль в функции, но мне лень. Поэтому мне проще запилить глобальную переменную-контекст, пусть даже статическую, чем ваять какой-то из этих вариантов. Вариант выше для меня красивей, чем 100500 обёрток над set/get_contex().

первую программу на си я написал лет 20 назад

Первую программу на си я написал 5минут назад, но это не помешает мне тебя с твоим жалким кукареканьем втоптать в говно.

anonymous
()
Ответ на: комментарий от arturpub

ну может ты и прав. Это сложный и неоднозначный вопрос.

emulek
()
Ответ на: комментарий от invy

1. Глобальные переменные - фича языка и топикстартер ей пользуется.

не всеми фичами, и не всегда нужно пользоваться. Вот например в сишечке есть фича auto, зачем она?

2. В данном коде ошибка связанна с глобальными переменными. Вопрос, почему в них значение не меняется. Ответ на вопрос, почему в них значение не меняется я от тебя не слышу.

для тех, кто в танке: необходимо убрать из кода эти глобальные переменные. Ответ на вопрос «как» ТС знает лучше меня.

emulek
()
Ответ на: комментарий от anonymous

На сишке без них писать не удобно. В сишке нет этого: ctx_t a; ctxf_t ctx_f(cxtf_t f, ctx_t t) { ctx_t new_f(void) {return f(t);} return new_f;} Мне пацаны говорили как это называется - я забыл.

Как-то так это можно запилить через статик-кастыль в функции, но мне лень. Поэтому мне проще запилить глобальную переменную-контекст, пусть даже статическую, чем ваять какой-то из этих вариантов. Вариант выше для меня красивей, чем 100500 обёрток над set/get_contex().

Первую программу на си я написал 5минут назад, но это не помешает мне тебя с твоим жалким кукареканьем втоптать в говно.

уже втоптал. Спасибо. Аргументов у меня нет. Засчитывай мой слив.

emulek
()

Кстати, energyclab, почему бы тебе в функции, которая ставит cursor, не сделать ((ui32*)video)[40] = &cursor, а в putchar() — ((ui32*)video)[80] = &cursor, и визуально оценить, линкер это виноват, или нет? Ну и вообще в каждой контрольной точке выводить адрес и значение переменной в к-л жесткое место на экране, без курсора и атрибутов, чисто белое по-черному в hex.

И мне до сих пор кажется, что раз адаптер читает вордами, а атрибут лежит в hibyte, то по четным смещениям будут лежать символы, а по нечетным — атрибуты. В сабжевом коде экран ты правильно очистил, а вот putchar() перепутан.

arturpub ★★
()
Ответ на: комментарий от energyclab

extern для того, чтобы каждый раз не инклудить заголовки и при этом варнинги не видет

триндец

x0r ★★★★★
()
Ответ на: комментарий от emulek

для тех, кто в танке: необходимо убрать из кода эти глобальные переменные. Ответ на вопрос «как» ТС знает лучше меня.

мне кажется, сейчас ты ведёшь себя как говнокодер. наличие проблемы описанной ТС-ом говорит о том, что что-то вообще работет не правильно и убрав глобальные переменные ты проблему не решишь, а только отсрочишь.

invy ★★★★★
()
Ответ на: комментарий от arturpub

ну тут холивар идет...

Ну и вообще в каждой контрольной точке выводить адрес и значение переменной в к-л жесткое место на экране, без курсора и атрибутов, чисто белое по-черному в hex. [/qoute]

Хороший совет, сейчас проверю адреса переменных

В сабжевом коде экран ты правильно очистил, а вот putchar() перепутан.

Сабж посвящен вопросу про глобальные переменные, с putchar я накосячил уже в процессе изучения проблемы. В коде ядра первым вызывается clrscr, вот с ним и решил эксперементировать, решу вопрос с глобальными переменными, будет решен и вопрос с puthchar)))

energyclab
() автор топика
Ответ на: комментарий от x0r

extern для того, чтобы каждый раз не инклудить заголовки и при этом варнинги не видет

триндец

Сори, эт я погорячился...

energyclab
() автор топика
Ответ на: комментарий от Miguel

Загрузчик разбит на 3 уровня: 1 - грузится biosом из первого сектора. Он узнает сколько секторов ему необходимо считать с диска, считывает их по адресу 7E00h и прыгает на него

2 - второй загрузчик размечает три сегмента (код, данные и отдельный сегмент на видеопамять) и переводит проц в защищенный режим, далее код передается 3-му загрузчику

3 - знает размер самого ядра и копирует его по адресу 2M ну и прыгает туда...

energyclab
() автор топика
Ответ на: комментарий от Miguel

Значит я вывел значения адресов.

код вывода

    unsigned int s = &cursor;
    for(ui32 i = 21; s; i -= 2) {
        video[i] = (s % 10) + '0';
        s /= 10;
    }

    s = &attribute;
    for(ui32 i = 41; s; i -= 2) {
        video[i] = (s % 10) + '0';
        s /= 10;
    }

РЕЗУЛЬТАТ

cursor = 2101248 = 0x201000
attribute = 2101252 = 0x201004

energyclab
() автор топика
Ответ на: комментарий от energyclab

Ситуация с локальными переменными

{
   ...
   ui8      *video=VIDEO_RAM
   video = 2097108 = 0x1fffd4
   ...
   volatile char    f='3';
   f = 2097107 = 0x1fffd3
   ...
}

Что то как то это не очень правильно, когда их адреса меньше 0x200000

energyclab
() автор топика
Ответ на: комментарий от invy

мне кажется, сейчас ты ведёшь себя как говнокодер. наличие проблемы описанной ТС-ом говорит о том, что что-то вообще работет не правильно и убрав глобальные переменные ты проблему не решишь, а только отсрочишь.

а с глобальными переменными IRL такие проблемы типичны. Если их убрать (скажем внутрь функции), то проблема «решается». Код просто не соберётся, и компилятор/линкер скажет — в каком месте. А вот когда переменная глобальная, проблема может быть везде. Из-за глобальности.

Т.е. убрав глобальные переменные кодер локализует проблему, и увидит, в каком месте он набыдлокодил.

А делать это за ТСа мне лень.

PS: это «проблема заборчиков на крыше»: да, они часто мешают. Но они всё равно нужны, хотя тоже стоят денег. И это несмотря на то, что даже ребёнок может через такой заборчик перелезть.

emulek
()
Ответ на: комментарий от energyclab

Что то как то это не очень правильно, когда их адреса меньше 0x200000

Ты же сам устанавливаешь esp на (200000h - 4) в start_kernel.asm или это не ты?

anonymous
()
Ответ на: комментарий от Miguel

Вообще мне кажется файл линкера ничего не решает, так как я формат binary выбрал, то где там секции bss, data, rodata и т.д. Сейчас буду смотреть как elf ядро грузить

energyclab
() автор топика
Ответ на: комментарий от anonymous

http://zalil.ru/34887902

Я вот сейчас собрал elf и потом дизасемблировал, там переменные в стеке объявлены и обращение к ним корректное, но проверить это не могу, т.к. не знаю как elf ядру передать управление

energyclab
() автор топика
Ответ на: комментарий от Miguel

Так господа знатоки, есть у меня ответ почему не меняется значение, но нет ответа как это исправить

Ситуация в следующем. Дизасcемблировал я elf ядро и увидел, что там операции по записи в глобальную переменную сделаны следующим образом

200010:       c6 05 ed 2f 00 00 0f    mov    BYTE PTR [rip+0x2fed],0xf        # 203004 <attribute>

теперь выхлоп binary

10:   c6 05 ed 2f 00 00 0f    mov    BYTE PTR ds:0x2fed,0xf

Откуда видно, что во втором случае адрес переменной высчитывается через сегмент данных, а, т.к. я в PM режиме, то физический адрес 0x2fed и есть адрес переменной в момент записи...

вывел я на экран значение по адресу 0x2fed и увидел то, записал при инициализации переменной

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

Ваши мысли господа

energyclab
() автор топика
Ответ на: комментарий от energyclab

Ну я вижу, что 2fed это смещение относительно rip в момент, когда уже засосалась команда, так что эффективное смещение будет 200010+2fed+7 = 203004, как и прокомментировано дизассемблером. Похоже на position-independent code (-fPIC).

Почему в бинаре это стало абсолютным смещением? Большой вопрос. Так точно быть не должно, какой-то артефакт линкера (или твоих опций, что более вероятно). Другой адрес ты наблюдаешь потому, что rip все время меняется, и в другом месте в elf'е было бы корректное обращение (по сумме нового rip+смещение == 203004), а в бинаре продолжает наблюдаться тот же артефакт.

Попробуй что-ли с -fPIC собирать все?

зы: чтобы не писать всем «смотри выше», можешь просто кастануть их в конце поста, например sudo cast [user]energyclab[/user].

arturpub ★★
()
Ответ на: комментарий от arturpub

Похоже на position-independent code (-fPIC)

4-ре дня я просидел с этой проблемой, а решение оказалось таким простым...

Всем и, в частности, arturpub за решение проблемы огромное спасибо, -fPIC действительно помог. Тему считаю закрытой

sudo cast energyclab.

Спасибо, про это я не знал

sudo cast energyclab.

energyclab
() автор топика
Ответ на: комментарий от energyclab

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

Я немного поковырялся с твоими makefiles и у меня вроде начало собираться с правильными адресами.

Что я добавил:

К флагам CFLAGS добавить -m32, к флагам линкера (LDFLAGS) добавить -melf_i386, изменить флаг для сборки start_kernel.asm для nasm -felf32.

Я запускать не пробовал, но при дисассемблировании kernel.bin видны статичные адреса, какие и положены по linker.ld

anonymous
()
Ответ на: комментарий от anonymous

а запускал образ ядра из под моего загрузчика? ПРосто я дальше выяснил, что переменные через стек не передаются...

energyclab
() автор топика
Ответ на: комментарий от energyclab

Просто мой загрузчик при запуске elf ядра ругается... а так я знаю, что elf формирует все указатели правильно, видел при дизассемблировании... Те флаги которые ты установил по сути только elf ядро собирают...А файл конфига для binary в принципе не правильный был, я сегодня просто коментил все и ничего не изменялось, только когда я линковку сделал как

ld -Ttext=0x200000 -oformat=binary ...
я увидел правильный ассемблерный код с правильными адресами, однако при вызове процедур я не увидел упаковки параметров в стек... Где то читал, что на архитектуре х86_64 передача параметров производится по максимум через стек... но фиг знает как здесь, я в long mode не переходил... хз че с этим делать... sudo cast energyclab

energyclab
() автор топика
Ответ на: комментарий от energyclab

Эти же параметры у тебя в linker.ld задаются, не уверен, что передача их же в командной строке решила проблему.

Посмотрел сейчас генерируемый у себя код, у меня все передается через стек.

anonymous
()
Ответ на: комментарий от anonymous

Слушай, я вообще походу не понимаю ничего в этой жизни... Походу elf64 правильно выполняется только в long mode, а я в него не переходил... да и вообще я его не изучал... Да, у меня все работает теперь правильно... спасибо за помощь, я уже шесть дней потратил на это... а все 64 битный режим...

energyclab
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.