Ответы пользователя по тегу C
  • Какие есть специфичные функции для glibc?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    с чем могут возникнуть проблемы

    Можно начать отсюда.
    Из моего опыта, самое часто всплывающее отличие glibc от других libc -- поддержка версионирования символов.
    Ответ написан
    Комментировать
  • Как загрузить ядро тиа ELF через службы UEFI (gnu-efi)?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Я пытаюсь загрузить 64-битное ядро по адресу 0x1000

    Я вижу в этом коде три проблемы:
    1. AllocatePages только возвращает адрес выделенной памяти по адресу в последнем параметре. Т.е. куда будут загружены сегменты ELF сказать нельзя и, соответственно, без динамической релокации этот ELF скорее всего работать не будет.
    2. Нет гарантии, что между блоками памяти выделенными через AllocatePages будет то же расстояние, что и между сегментами ELF, в то время как кодогенерация с флагом -fPIE вполне может считать это расснояние неизменным. Такой ELF нужно загружать в один большой блок памяти, сохраняя расстояние между сегментами.
    3. В следствие пункта 1 запуск ядра по адресу header.e_entry -- это прыжок в неизвестность.
    Ответ написан
    Комментировать
  • Как написать Netflow сенсор с нуля?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    как мне модифицировать мою программу, чтобы она могла определять потоки и корректно отправлять их коллектору?

    Для этого тебе надо понять, что такое "потоки" о которых ты говоришь, где находится информация о принадлежности пакета потоку и как до неё добираться.

    Если ограничиться определением по ссылке, то тебе надо будет разбирать сырые пакеты до сетевого уровня, откидывать не-IP, из IP извлекать IP-протокол, адрес источника, адрес назначения и тип сервиса, для протоколов TCP и UDP дополнительно извлекать порт источника и порт назначения. Всё это довольно просто сделать самому вооружившись RFC по упомянутым протоколам. Также надо будет понять, что делать с фрагментированными IP-пакетами.
    Ответ написан
    Комментировать
  • Зачем нужен signed в Си?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    по умолчанию int является signed

    А вот о char этого сказать нельзя, знаковость char зависит от реализации. Вдобавок, в стандарте есть следующая сноска (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n... 6.2.5:15):
    Irrespective of the choice made, char is a separate type from the 
    other two and is not compatible with either.


    signed char -- это, пожалуй, единственное не вызывающее вопросов применение ключевого слова signed в C-коде написанном человеком. В остальных случаях роль signed -- чисто декоративная.
    Ответ написан
    Комментировать
  • Как сделать, чтобы printf() заменял -nan на любой заданный символ?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Как сделать, чтобы вместо -nan печатался символ "-" ?

    Только добавив свою логику в свою программу, проверяющую значение на NaN и печатающую в зависимости от этого то или иное значение. С помощью стандартного printf -- никак.
    Ответ написан
    Комментировать
  • Указатели в языке Си?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    почему программа завершилась? Вмешалась ОС? Но если так, то почему?

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

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

    Значение указателя ты поменять можешь, но это не то, что было написано в коде. В коде было написано поменять значение на которое указатель указывает. Поменять указатель было бы a = (int *)10;

    могу ли я указателю дать ссылку на конкретную ячейку в памяти?

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

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

    В памяти всё хранится как последовательность байтов. Любую последовательность байтов можно проинтерпретировать как указатель. Но чтобы компилятор С понял что ты имеешь в виду, ему надо явно сказать, что ты хочешь проинтерпретировать целое число как адрес: int *a = (int *)0x0061FF18;
    Ответ написан
    Комментировать
  • Почему inline функция не компилилируется?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Почему так происходит?

    Потому что стандарт С11 (9899:201x 6.7.4:7, черновик доступен тут) говорит следующее про спецификатор inline:

    Any function with internal linkage can be an inline function. For a function
    with external linkage, the following restrictions apply: If a function is declared
    with an inline function specifier, then it shall also be defined in the same translation
    unit. If all of the file scope declarations for a function in a translation unit include
    the inline function specifier without extern, then the definition in that translation
    unit is an inline definition. An inline definition does not provide an external
    definition for the function, and does not forbid an external definition in another
    translation unit. An inline definition provides an alternative to an external
    definition, which a translator may use to implement any call to the function
    in the same translation unit. It is unspecified whether a call to the function uses
    the inline definition or the external definition.

    Самое главное тут -- в середине: инлайн-реализация не предоставляет и не запрещает реализацию со внешней линковкой, и в последнем предложении: транслятор волен сам выбирать, какую реализацию функции использовать -- инлайновую или со внешней линковкой.
    Поскольку ты написал так:
    inline void some_inline_func(uint32_t num)
    {
        . . .
    }

    ты тем самым реализовал инлайн-версию этой функции, но не реализовал версию со внешней линковкой. А транслятор выбрал (вернее ты ему подсказал выбрать, опцией -O0) использовать реализацию функции со внешней линковкой, поэтому ты получил ошибку от линковщика.

    Вариантов решения этой проблемы примерно 3:
    - определить функцию следующим образом:
    extern inline void some_inline_func(uint32_t num)
    {
        . . .
    }

    Такая функция будет доступна из других единиц трансляции.

    - либо определить функцию следующим образом:
    static inline void some_inline_func(uint32_t num)
    {
        . . .
    }

    Такая функция будет доступна только для данной единицы трансляции.

    - оставить в этом исходнике всё как есть, а в каком-нибудь другом определить следующую функцию:
    void some_inline_func(uint32_t num)
    {
        . . .
    }

    Это самый дурацкий вариант, поскольку эти две реализации могут не совпадать и в зависимости от выбора транслятора ты можешь получить разное поведение.
    Ответ написан
    Комментировать
  • Как при http запросе curl не выводить ничего в консоль?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    как выполнить запрос и не выводить ничего в консоль?

    Если результат запроса надо сохранить -- то открыть файл на запись и передать полученный указатель в CURLOPT_WRITEDATA. Если сохранять не надо -- установить функцию, которая будет игнорировать передаваемые ей для записи данные в CURLOPT_WRITEFUNCTION.
    Ответ написан
    Комментировать
  • Как замерять время выполнения функции?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Как замерять время выполнения функции?

    Можно воспользоваться парой clock_gettime / clock_getres.

    как можно искусно замедлить выполнение

    Можно организовать искусный цикл вокруг замеряемой функции и разделить его общее время выполнения на количество итераций. Но детали зависят от того, какое именно время вы там пытаетесь померить. Т.е. если там важны эффекты от кеша, то тупой цикл может быть недостаточно искусным.
    Ответ написан
    Комментировать
  • Как задать значение последней переменной?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Имеется код вида
    int64_t a = b/c
    b и c какие-то произвольные числа
    требуется записать 0 в переменную a, если произошел SIGFPE.
    Проверять значения b и c не вариант, нужно как-то задавать значение для a в обработчике сигнала.

    Задача в такой постановке выглядит странной, если это не просто учебная задача на обработку сигналов.
    Если это учебная задача -- то конечно, и ассемблерная вставка тут может помочь, но это не вопрос для тостера.
    Если это практическая задача, то решить её можно следующими способами:

    - наиболее простой и переносимый, но не самый быстрый -- таки проверить значения b и c перед вычислениями.

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

    - следующий вариант -- нифига не простой и не переносимый -- устанавливать обработчик сигнала с помощью sigaction, заказывать доставку SA_SIGINFO и анализировать ucontext (третий аргумент) в обработчике сигнала. Сложность заключается в том, что связь между тем, что будет записано в ucontext и исходным кодом на C ни разу не очевидна. Т.е. можно извлечь из памяти по адресу info->si_addr инструкцию вызвавшую сбой, проанализировать её длину и куда она записывает результат, записать в результат 0 и передвинуть адрес возврата. Но это выглядит как неоправданное количество мороки.
    Ответ написан
  • Ошибка unhandled dwarf2 в Valgrind как можно исправить и с чем связана?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    при использовании Valgrind выпадает ошибка:

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

    Опытным путём пришёл к выводу что проблемма связана с ошибкой в самом make.

    м... а почему valgrind ругается вот на это:
    ==6410== Valgrind:   "/media/mid/Main_file_m2/Road/rank 0/libftTester/a.out"
    Ответ написан
  • Нормален ли такой код?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Нормален ли такой код?

    В каком смысле "нормален"? Он понятен, но не очень хорошо организован и делает какие-то с виду лишние действия.
    Ответ написан
  • Как инициализировать n так что бы оно работало для всех введенных n,а не только для 2?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    код работает только для 2 строк , а как записать что бы для всех которые будет вводить пользователь?

    заменить
    int k[2]={};
    на
    int k[100]={};
    Ответ написан
    Комментировать
  • Чем это достигается?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    выбором компилятора? опциями компилятора? неизвестно, ты не привёл никакой полезной информации.
    Ответ написан
    2 комментария
  • Как общаться с процессом в Linux?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Нужно написать программу, которая будет посылать в stdin программы данные и забирать из stdout.

    Что я делаю не так?


    Ты пользуешься не тем методом. pidfd_open делает совсем не то, что ты ожидаешь, см. раздел "Use cases for PID file descriptors" по приведённой ссылке.

    Взаимодействие с дочерним процессом через stdin/stdout обычно реализуется через пайп. Вот пример.

    Вот ещё пример который делает exec

    #include <unistd.h>
    
    int main()
    {
            int fd[2][2];
            pipe(fd[0]);
            pipe(fd[1]);
            pid_t pid_fork = fork();
            if (!pid_fork) {
                    // Дочерний процесс
                    close(fd[0][1]);
                    close(fd[1][0]);
                    dup2(fd[0][0], STDIN_FILENO);
                    dup2(fd[1][1], STDOUT_FILENO);
                    execl("/usr/bin/tr", "/usr/bin/tr", "l", "r", NULL);
            } else {
                    // Родительский процесс
                    close(fd[0][0]);
                    close(fd[1][1]);
                    char buf[1000];
                    ssize_t sz;
    
                    write(fd[0][1], "hello, world\n", sizeof("hello, world\n") - 1);
                    close(fd[0][1]);
                    sz = read(fd[1][0], buf, sizeof(buf));
                    if (sz > 0) {
                            write(STDOUT_FILENO, buf, sz);
                    }
            }
            return 0;
    }
    Ответ написан
    Комментировать
  • Почему после вызова Message Box, GetKeyState() не считывает состояние клавиши?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Почему … GetKeyState() не считывает состояние клавиши?

    Потому что чтобы GetKeyState работал, поток должен обрабатывать свою очередь сообщений (например, циклом, наподобие этого).
    Из man GetKeyState:
    The key status returned from this function changes as a thread reads key messages from its message queue. The status does not reflect the interrupt-level state associated with the hardware. Use the GetAsyncKeyState function to retrieve that information.
    Ответ написан
    Комментировать
  • Почему для драйвера мыши используется тот же порт, что и в драйвере клавиатуры?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Такие же порты используются при нахождении нажатой клавиши на клавиатуре, так как они могут быть и там и тут?

    Это называется мультиплексирование. В конце концов PS/2 -- довольно сложный интерфейс, работающий с командами, а не то чтобы клавиатура была напрямую подсоединена к одному из портов PC. См. https://wiki.osdev.org/Mouse_Input#Keyboard.2FAux_...
    Ответ написан
    Комментировать
  • Как исправить ошибку "Кадр не находится в модуле" при создании потока?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    "Кадр не находится в модуле". Подскажите с чем это может быть связано
    HANDLE h = CreateThread(
        NULL,
        0,
        factorial(gn),
        NULL,
        NULL,
        NULL
    );

    Третий параметр CreateThread -- указатель на функцию, которая будет запущена в создаваемом потоке. А у тебя записано скорее всего что-то другое. Если ты хотел запустить функцию factorial в потоке, она должна 1) иметь определённый прототип (вот такой), и 2) в функцию CreateThread надо передать её адрес, а не результат её вызова (например, так: CreateThread(NULL, 0, factorial, NULL, NULL, NULL)). 3) если ты сделаешь эти два изменения, тебе прийдётся также переделать передачу параметра в функцию factorial и получение результата её работы.
    Ответ написан
  • Как реализовать _open() для добавления своих потоков ввода-вывода?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    Почитать про libgloss, понять что надо самому разбирать пути переданные в параметре name и где-то хранить данные о том, что за файл и как был открыт. Понять, как именно ты хочешь "реализовать работу со своими устройствами" и… реализовать её.
    Ответ написан
  • Почему не работает inportb(0x60)?

    jcmvbkbc
    @jcmvbkbc
    http://dilbert.com/strip/1998-08-24
    во-первых компилятор даёт предупреждение, … , но ошибки не производит. Я спокойно линкую файл недо-драйвера с основным ядром, (конечный результат - .efi),

    Дело в том, что у EFI-приложений нет динамической линковки, а неопределённый символ после копирования из .so в .efi просто ссылается в никуда. Если вывести неопределённые символы для bootx64.so, то inportb будет среди них:
    objdump -t bootx64.so | grep UND
    0000000000000000         *UND*  0000000000000000 inportb


    В qemu результат вызова этой функции выглядит так:
    Trace 0: 0x7fb15d338e80 [0000000000000000/000000007e6e22de/0x40c2b0]
    ----------------                      
    IN: 
    0x7e6e2081:  bf 60 00 00 00           movl     $0x60, %edi
    0x7e6e2086:  b8 00 00 00 00           movl     $0, %eax
    0x7e6e208b:  e8 90 57 00 00           callq    0x7e6e7820
    
    Trace 0: 0x7fb15d3390c0 [0000000000000000/000000007e6e2081/0x40c2b0]
    ----------------                      
    IN: 
    0x7e6e7820:  af                       scasl    (%rdi), %eax
    0x7e6e7821:  af                       scasl    (%rdi), %eax
    0x7e6e7822:  af                       scasl    (%rdi), %eax
    0x7e6e7823:  af                       scasl    (%rdi), %eax
    0x7e6e7824:  af                       scasl    (%rdi), %eax
    0x7e6e7825:  af                       scasl    (%rdi), %eax
    0x7e6e7826:  af                       scasl    (%rdi), %eax
    0x7e6e7827:  af                       scasl    (%rdi), %eax
    0x7e6e7828:  af                       scasl    (%rdi), %eax
    0x7e6e7829:  af                       scasl    (%rdi), %eax
    0x7e6e782a:  af                       scasl    (%rdi), %eax
    0x7e6e782b:  af                       scasl    (%rdi), %eax
    …


    С другой стороны, если предоставить такую функцию:
    unsigned char
    inportb(unsigned short port)
    {
            unsigned char v;
            asm volatile ("in {%1|%b0}, {%b0|%1}\n" : "=a"(v) : "d"(port));
            return v;
    }

    то она прекрасно вызывается и возвращает значение.

    Можно добавить опцию -zdefs в команду линковки чтобы получать ошибку линковки при наличии ссылок на неопределённые символы.

    Чтобы посмотреть, какой именно код выполняется, я запускаю qemu с монитором в консоли (дополнительным ключом -monitor stdio). Я нажимаю ESC когда в QEMU запускается tianocore и выбираю Boot Manager -> EFI Internal Shell, а там пишу fs0:efi\boot\bootx64.efi, после этого в мониторной консоли включаю логгирование (командами logfile log, log in_asm,exec), после чего нажимаю enter в консоли EFI. После этого можно смотреть в файл log и искать в нём знакомые байты из objdump.
    Ответ написан