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

Ответ на: комментарий от Laz

linker.ld

OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x200000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata)
    *(.rdata)
    /*. = ALIGN(4096);*/
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    /*. = ALIGN(4096);*/
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}

start_kernel.asm

[bits 32]
[extern kernel_main]
[global  start]
start:
    mov     esp, 200000h - 4
    call kernel_main

kernel.c

#include <sys_tables.h>
#include <tty.h>

void kernel_main() {
	init_tty();
	clrscr();
	// puts("Hello world");
	while(1); // повесим в ожидании чуда.
}

Сборка


nasm -felf64 -o start_kernel.o start_kernel.asm
ld -T linker.ld -o kernel.bin start_kernel.o ....

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

в том то и дело, что у меня ошибок и предупреждений нет, я запускаю прогу и вывожу значения глобальных переменных на экран, они не меняются, даже после явного присвоения им значений...всегда 0. В нете кто то кричит, что надо откатиться на gcc 4.6.2, я прбовал, результат тот же, и на 4.4 тоже. На другом форуме пишут что ошибка в linker.ld

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

Как пожелаете)))

make -C kernel
make[1]: Вход в каталог `/home/energy/work/hypervisor/kernel'
make -C core
make[2]: Вход в каталог `/home/energy/work/hypervisor/kernel/core'
gcc-4.6 -I/home/energy/work/hypervisor/kernel/core/../../include -O0 -fomit-frame-pointer -ffreestanding -finline-functions -nostdinc -fno-builtin -nostdlib -std=c99 -Wall -c /home/energy/work/hypervisor/kernel/core/tty.c /home/energy/work/hypervisor/kernel/core/kernel.c /home/energy/work/hypervisor/kernel/core/sys_tables.c /home/energy/work/hypervisor/kernel/core/string.c
/home/energy/work/hypervisor/kernel/core/tty.c: In function ‘clrscr’:
/home/energy/work/hypervisor/kernel/core/tty.c:19:23: warning: initialization makes pointer from integer without a cast [enabled by default]
/home/energy/work/hypervisor/kernel/core/tty.c: In function ‘putchar’:
/home/energy/work/hypervisor/kernel/core/tty.c:37:22: warning: initialization makes pointer from integer without a cast [enabled by default]
/home/energy/work/hypervisor/kernel/core/sys_tables.c: In function ‘init_gdt’:
/home/energy/work/hypervisor/kernel/core/sys_tables.c:6:17: warning: unused variable ‘gdt_reg’ [-Wunused-variable]
/home/energy/work/hypervisor/kernel/core/sys_tables.c: At top level:
/home/energy/work/hypervisor/kernel/core/sys_tables.c:3:33: warning: ‘_GDT’ defined but not used [-Wunused-variable]
ld -r -o core.bin /home/energy/work/hypervisor/kernel/core/sys_tables.o /home/energy/work/hypervisor/kernel/core/tty.o /home/energy/work/hypervisor/kernel/core/kernel.o /home/energy/work/hypervisor/kernel/core/string.o
make[2]: Выход из каталога `/home/energy/work/hypervisor/kernel/core'
nasm -felf64 -o start_kernel.o start_kernel.asm
ld -T linker.ld -o kernel.bin start_kernel.o  core/core.bin
make[1]: Выход из каталога `/home/energy/work/hypervisor/kernel'
make -C bootloader
make[1]: Вход в каталог `/home/energy/work/hypervisor/bootloader'
rm -f boot_loader.img
nasm -i../kernel/ -f bin stage_2.asm -o stage_2.bin
nasm -f bin stage_1.asm -o boot_loader.img
make[1]: Выход из каталога `/home/energy/work/hypervisor/bootloader'
dd if=bootloader/boot_loader.img of=floppy conv=notrunc
4+1 записей получено
4+1 записей отправлено
скопировано 2353 байта (2,4 kB), 0,000248372 c, 9,5 MB/c

energyclab
() автор топика

extern void puts(ui8 *s) {

А на кой там extern? (и в остальных функциях)

Дикость какая-то!

А еще до меня не доходит, нафига писать volatile, если ты не для МК пишешь?

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

Не оно: в случае ТСа глобальные переменные "наружу" не вылезают. Это как private-члены недоООПовского языка.

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

нет это не оно, там static стоит просто из-за моих разных экспериментов, так как я бьюсь с этим давно уже...без static таже ситуация

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

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

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

Нифига не понял.

Ты бы кинул минимальный рабочий пример куда-нибудь на pastebin, чтобы можно было проверить.

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

Если у него есть обработчики прерываний, многопоточность и т. д. (он ведь пишет ОС), то volatile также будет не лишним для переменных, которые могут меняться из другой нити или из обработчика прерываний.

KivApple ★★★★★
()

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

[...] переменные [...] не хотят менять свои значения

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

http://www.catb.org/~esr/faqs/smart-questions.html

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

Так не бывает.

Ещё как бывает. Например, если адрес переменной попал на какой-нибудь memory-mapped port или что-то такое.

Я не смотрел внимательно код, но мне интересно: в каком режиме процессора выполняется этот код, где и как производится переход в этот режим, где и как выставляются всякие интересности типа GDT,.. ну, дальше посмотрим.

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

Ещё как бывает. Например, если адрес переменной попал на какой-нибудь memory-mapped port или что-то такое.

Согласен.

gv
()

Если нужен отладочный вывод, а не конкретно вывод в VGA-буфер, то возьми QEMU и пиши в его COM-порт. Избавишься от необходимости скроллить память.

https://github.com/l4ka/pistachio/blob/master/user/lib/io/ia32.cc#L93

Нужный номер порта 0x3f8.

Как показала практика, на qemu init порта и ожидания готовности не обязательны.

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

Оффтоп: один puts для вывода — жестоко.

Взгляни на http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/arch/sandpoint/stand/n...

netbsd-шный printf, не требующий никаких дополнительных функций, кроме функции вывода одного символа. Берёшь оттуда printf, _doprnt и pr_int, а в качестве putchar используй что душе угодно.

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

Всем извините, я был в отъезде, неудобно очень, отвечаю на все вопросы.

1. Я просил минимальный пример, а не все в куче. 2. И уж тем паче без виртуалки.

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

А ты часом атрибут с символом местами не перепутал?

уже точно известно, что глобальные переменные не изменяются...

С ключём компиляции -O0 работает?

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

Ещё как бывает. Например, если адрес переменной попал на какой-нибудь memory-mapped port или что-то такое.

Вот порывшись на форумах, я понял, что проблема скорее всего в linker.ld, что то не так размечено, что именно, я не понимаю...Поэтому и спрашиваю, неужели никто не встречал такой проблемы...

Если нужен отладочный вывод, а не конкретно вывод...

Это понятно, я просто до этого еще не добрался, пока что меня волнует проблема глобальных переменных, а не отладки...

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

Видели, но граб не хотелось бы использовать по определенным причинам,мне необходимо самому переводить проц в защищенный режим...Плюс в примере делается multiboot, у меня просто binary

energyclab
() автор топика

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

Собственно, твой загрузчик переходит в защищённый режим?

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

Что-то я не нашёл, в каком makefile ядро записывается на floppy.

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

Вдогонку: floppy не загрузочный, makefiles доставляют.

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

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

kike
()

Обе глобальные переменные

глобальные переменные нужны только говнокодерам. (первую программу на си я написал лет 20 назад).

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

Какой ты резкий. Посмотрел бы я на твой быдлокод без глобальных переменных. У тебя, небось, и errno своя, неглобальная?

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

Ещё как бывает. Например, если адрес переменной попал на какой-нибудь memory-mapped port или что-то такое.

в говнокоде — да, бывает всё что угодно.

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

Посмотрел бы я на твой быдлокод без глобальных переменных.

а зачем там глобальные переменные, в твоём коде? Зачем вообще организовывать межмодульную связь именно переменными?

errno своя, неглобальная?

errno она не моя, а чужая. В коде её нет, есть только extrn куда-то хрен знает куда. Возможно вообще в ассемблерный код, а там все переменные — глобальные.

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

а зачем там глобальные переменные, в твоём коде?

А вот, например, наподобие errno: хранить постоянно какие-то глобальные параметры. Чаще всего у меня глобальными являются пара-тройка структур (скажем, настройки и т.п.). Невозможно обойтись без глобальных переменных!

Зачем вообще организовывать межмодульную связь именно переменными?

Какие модули? Внутри одного потока все постоянное лежит в глобальных переменных, а между потоками обмен идет на обычных IPC.

errno она не моя, а чужая.

Можно подумать, ты бы сделал без глобальной переменной! Ну невозможно это!!!

Возможно вообще в ассемблерный код

Нет, там все проще.

а там все переменные — глобальные.

И чего тогда бухтишь? Ну, напишешь ты в функции вместо глобальной переменной "static". И что? По сути это будет глобальная переменная, но доступ к ней будет лишь из этой функции, т.е. для других функций придется какие-то костыли городить, либо вместо простого доступа к глобальной переменной опять вызывать функцию, которая эту переменную вернет. Костылищща!

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

Хм, надо будет проверить. Если у каждого потока errno своя, то каким-то чудесным образом она "размножается". Мистика.

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

Shit!

Почитал по теме про errno на SO. Оказывается, эта фигня уже давным-давно не является переменной. Это — функция! Вона как! Они для потокобезопасности запихнули глобальную переменную в статическую переменную функции.

Дошло до жирафа. Принимаю соболезнования.

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

А вот, например, наподобие errno: хранить постоянно какие-то глобальные параметры. Чаще всего у меня глобальными являются пара-тройка структур (скажем, настройки и т.п.). Невозможно обойтись без глобальных переменных!

в классической сишечке нет никакого надёжного способа против того, что-бы какой-то мудак менял ВСЕ параметры программы из любого говномодуля. Потому, если глобальная переменная есть, всегда найдётся мудак, который пишет туда не то, не так, и не тогда. Причём отыскать этот говномодуль довольно проблематично. Зачем на пороге своего дома класть грабли? Уж поверь мне, ты на них наступишь куда как раньше, чем вор. Ибо вор светит под ноги фонариком, а ты — ходишь так.

Какие модули? Внутри одного потока

да при чём тут потоки? Я про модули трансляции.

errno

Можно подумать, ты бы сделал без глобальной переменной! Ну невозможно это!!!

почему нет? Она вполне может быть статической, а наружу торчит только функция её чтения. Зачем тебе туда писать?

Возможно вообще в ассемблерный код

Нет, там все проще.

это только в твоей текущей реализации. Я завтра могу любую HEX написать.

И чего тогда бухтишь? Ну, напишешь ты в функции вместо глобальной переменной «static». И что? По сути это будет глобальная переменная, но доступ к ней будет лишь из этой функции, т.е. для других функций придется какие-то костыли городить, либо вместо простого доступа к глобальной переменной опять вызывать функцию, которая эту переменную вернет. Костылищща!

дык зачем тебе вообще сишечка нужна, если (например) возвращаемое функцией значение, это по сути просто глобальный регистр eax? А «указатель на массив из указателей на функцию принимающую float и указатель на char* и возвращающую double» это по сути обычный адрес, который ничем не отличается от eax или там int? Нахрена эти сложности? По сути всё проще и понятнее.

PS: а ещё лучше писать на bf, там ещё проще и понятнее. 8 операторов, этого хватит всем. Тьюринг гарантирует и доказывает.

PPS: вот эти, как ты говоришь «костылищща» как раз и нужны для того, что-бы собрать из Over9000 разных модулей написанных разными людьми в разное время одну большую программу. И что-бы она не трещала по швам. Если взять детский конструктор, то «глобальная переменная» там == такая ось, которая проходит через ВСЕ детали. Я надеюсь тебе понятно, какой это геморрой, когда деталей больше двух.

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

Ну и чего ты мне объясняешь что-то? Я — не программист. Мне нужно, чтобы мой велосипед выполнял для меня ту задачу, которую я на этот велосипед возложил. И мне совершенно насрать, что об этом велосипеде думают окружающие. Если захотят использовать — на здоровье.

Eddy_Em ☆☆☆☆☆
()
Ответ на: Shit! от Eddy_Em

Это — функция! Вона как! Они для потокобезопасности запихнули глобальную переменную в статическую переменную функции.

ну вот видишь, как оно всё получается... Ну дык ещё какие будут примеры неговнокодерских и глобальных переменных?

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

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

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