Титла: Малък въпрос за lseek() Публикувано от: shoshon в Aug 28, 2012, 17:20 Здравейте,
Отдавна не съм пускал тема, но днес намерих нещо интересно. Та, ровя се в лекциите на Мария ( по досадни за мен причини ) и стигам до секцята със задачки за lseek(). Решавам да напиша следния код, ( ядрото на skynet ): Код
Целта на кода е да тества какво ще се получи ако имаме отрицателен offset. Тривиалната част на теста мина ОК - не може да има зададен отрицателен offset от началото на файла и lseek() връща -1 като задава стойност EINVAL на errno... Изпълнението на програмката води до: Код
НО http://pubs.opengroup.org/onlinepubs/009695399/functions/lseek.html Цитат The POSIX.1-1990 standard did not specifically prohibit lseek() from returning a negative offset. Therefore, an application was required to clear errno prior to the call and check errno upon return to determine whether a return value of ( off_t)-1 is a negative offset or an indication of an error condition. The standard developers did not wish to require this action on the part of a conforming application, and chose to require that errno be set to [EINVAL] when the resulting file offset would be negative for a regular file, block special file, or directory. Интересно. Нека сега да коментираме #include <unistd.h>. . Простия ламър, като мен, би си помислил, че този код няма да се компилира, но това разбира се не така... lseek e системен примитив и gcc не би трябвало да се интересува от някакъв специален хедър. Нека сега да премахнем #include <unistd.h> и да компилираме наново. Грешка уж няма, но: Код
Т.е lseek връща число по-малко от 0. -10 , ако трябва да сме точни - отрицателен offset, а и за капак не задава errno. Ок. Нека да видим асемблерния код: Код
Очевадно става въпрос, за това, че когато няма include на <unistd.h> gcc използва друга дефиниция на пустата функция, защото размера на файловете е различен (имам предвид, че явно call lseek има друго значение при свързването). Моля ви някой да ми обясни къде бъркам? Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 29, 2012, 02:05 Не мога да репродуцирам твоя резултат, но знам каква е причината за това. Няма нищо общо с lseek().
Примерът ти не е коректен. errno съдържа кода на грешка от последната изпълнена libc функция. В случаят обаче това не е lseek(), а write(), защото ти го ползваш за да ти изпише "lseek returned an error" и това наистина минава без драми Има един проблем с това - POSIX стандарта не специфицира изрично какво трябва да се случи ако функцията не връща грешка. Това е въпрос на имплементация. Тоест, може да си дефинира код за "success", но може просто да не промени стойността на errno. Подозирам че при твоята системна C библиотека става нещо такова, включването на unistd.h променя поведението поради някаква причина. Не мога да кажа дали това е правилно или грешно, понеже пак казвам, стандартът не покрива такива случаи. Поради тази причина, ако трябва да проверяваш errno, прави го _веднага_ след функцията, иначе не е сигурно какво ще има там. В случаят дори един write() е напълно достатъчен да ти счупи нещата. Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 29, 2012, 02:46 Мерси за помощта!
Истината е, размяната на write и perror не променя резултата. В wiki пише, че: In the C and C++ programming languages, unistd.h is the name of the header file that provides access to the POSIX operating system API. It is defined by the POSIX.1 standard, the base of the Single Unix Specification, and should therefore be available in any conforming (or quasi-conforming) operating system/compiler (all official versions of Unix, including Mac OS X, Linux, etc. Тук смисъла на API някак ми убягва. Не е ли работа на компилатора да разбира кои функции са примитиви и да записва съответните прекъсвания? И аз имам теория btw, но наистина ме е срам да я споделя :) Вече не мисля, че въпросът ми е толкова важен - прост пример до къде стига човек заради един пропуснат include... Титла: Re: Малък въпрос за lseek() Публикувано от: appmaster в Aug 29, 2012, 03:14 Я сподели каква ти е торията, че и на мен ми стана интересно.
Според мен има нещо общо това, че компилатора ти ползва едни ресурси, а операционнат ти е кеширала някакви други/стари библиотеки и при деклариране на include-a ти сменя версиите може би... Странна история. Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 29, 2012, 03:20 А, това вече е много странно. Така както го разбирам, unistd.h ти гарантира (доколкото може да се гарантира) POSIX-подобно поведение. В противен случай, функциите от системната библиотека може да имат implementation-specific поведение. Как става това - примерно с макроси. Ако включиш unistd.h, тогава се #define-ват разни неща, спрямо които имаш така да го наречем "POSIX" поведение.
Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 29, 2012, 18:00 По случайност да си с 64 битова ОС?
Моето предположение е... (преобразуването на типовете е различно с/без макросите от unistd.h) Я пробвай с това - при мен даде очаквания резултат off_t offset = -10; off_t off = lseek(fd, offset , SEEK_SET); Без горното ми даваше следното с unistd.h errno=22 EINVAL Invalid argument lseek returned error! без unistd.h errno=0 Success lseek returned error! - това само защото върнатия офсет е отрицателен НО никъде не проверявате ст-та на errno! Добра идея е да се проверява errno все-пак; даже хората препоръчват да се му присвоява 0 преди извикване на функция и веднага след това ст-та да се съхранява в локална променлива (и е малко по-сложно при многонишкови приложения щото е volatile) П.П. Другата ми (по-тъпата) идея беше за Selinux, но след кратка проверка установих че е горното поне на мойта машина Fedora x86_64 NB! Уф - Една бърза проверка оналйн щеше да ми спести половин час експерименти... П.П.П. Много стар бъг - януари 1995 г. - http://web.archiveorange.com/archive/v/nfJUhVgv6RvEuqi9NzXB Хехех - Важното е да не настъпваме едни и също мотики повече от веднъж SYNOPSIS #include <unistd.h> off_t lseek(int fildes, off_t offset, int whence) It is an off_t so 64 bits. and I'd recommand to use a cast (off_t) 100 instead of just 100 but that may not be necessary if <unistd.h> is included. Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 29, 2012, 19:02 @remotex точно това е проблема errno си е 0 без да го пипам. И аз си мислех за битовете :D
Здравейте пак, За да не ми умре темичката, а и за да дам последен шанс на bob_bob_mara да се изяви, ето моята мисла: Включването на unistd.h яно е важно за да знае компилатора капабилитито ( български превод :( ) на систмата. Пример: Ако компилирам със включен unistd.h, lseek се извиква със следните инструкции: 400638: 8b 45 fc mov -0x4(%rbp),%eax 40063b: ba 01 00 00 00 mov $0x1,%edx 400640: 48 c7 c6 f6 ff ff ff mov $0xfffffffffffffff6,%rsi <- 64-битов регистър 400647: 89 c7 mov %eax,%edi 400649: e8 62 fe ff ff callq 4004b0 <lseek@plt> А ако го направя без unistd.h се извиква така ( дори и да компилирам с gcc -m64 ): 400642: 8b 45 fc mov -0x4(%rbp),%eax 400645: ba 01 00 00 00 mov $0x1,%edx 40064a: be f6 ff ff ff mov $0xfffffff6,%esi <- 32битов код 40064f: 89 c7 mov %eax,%edi 400651: b8 00 00 00 00 mov $0x0,%eax 400656: e8 55 fe ff ff callq 4004b0 <lseek@plt> В кода на ядрото четем: http://lxr.free-electrons.com/source/fs/ext4/file.c#L218 http://lxr.free-electrons.com/source/fs/read_write.c#L69 http://lxr.free-electrons.com/source/fs/read_write.c#L38 Забележете, че е точно както са написали в open group - няма проверка дали offset е отрицателен от началото на файла - ако тази заявка е невалидна, то следва върната стойност да мине през glibc и да се зададе стойност на errno=-върната стойност. Т.е. най-вероятно, ако offset-а е невалиден, то glibc вдига errno. Нека сега да разширим задачата. Долу е кода на skynet2 : Код
Компилираме и изпълняваме. Хайде да отгатнем какво има във file? Код
А къде точно е "12345" :) Интересно е как може да разцъкаме тази ситуация... Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 29, 2012, 21:01 Бас държа че пак е поради същата причина която изтъкнах по-горе то точно затова и не работи заради неявното конвертиране от (по подразбиране литералите са) int който си е 32 бита към off_t който е 32 или 64 и понеже предполагам машината ти е 64 битова т.е. със и без -m64 все като 64 бита ти го компилира затова няма разлика все гърми - за да не ти гърми я го компилирай като 32 бит там няма да има конверсия от подразбиращия се тип за литералите който е int т.е. 32 бит към 32 битов off_t или както и преди казах добра практика е литералите да се указват със явен cast или суфикс който указва типа инак следва неявно конвертиране по подразбиране
Само информативно може ли да дадеш изхода от следния пример при теб какво връща със и без unistd.h Код
Код
//Добавка: А също така кажи и sizeof(ptrdiff_t) какво връща Добавка: stddef.h дефинира size_t, ptrdiff_t и пр. няма значение кое - просто за сигурност да се убедим че кода е 32 или 64 битов - т.е. ще върне 4 или 8 байта размер съответно. Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 29, 2012, 21:07 Без:
$ gcc -E sizes.c | grep __off_t typedef long int __off_t; __off_t __pos; __off_t _old_offset; typedef __off_t off_t; extern int fseeko (FILE *__stream, __off_t __off, int __whence); extern __off_t ftello (FILE *__stream) ; Със: $ gcc -E sizes.c | grep __off_t typedef long int __off_t; __off_t __pos; __off_t _old_offset; typedef __off_t off_t; extern int fseeko (FILE *__stream, __off_t __off, int __whence); extern __off_t ftello (FILE *__stream) ; extern __off_t lseek (int __fd, __off_t __offset, int __whence) __attribute__ ((__nothrow__)); __off_t __offset) ; __off_t __offset) ; extern int truncate (__const char *__file, __off_t __length) extern int ftruncate (int __fd, __off_t __length) __attribute__ ((__nothrow__)) ; extern int lockf (int __fd, int __cmd, __off_t __len) ; Добавка: Това prtdiff_t не го знам къде е. Аз много не разбирам от C... Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 29, 2012, 21:13 точно така long int и бас държа че под уиндоус това работи без проблем щото те ползват LLP64/IL32P64 модела при който int и long int са 32 бит докато линукс ползва LP64/I32LP64 където int е 32 а long int 64 бита
т.е. при теб long int или off_t на 64 битова машина са 64 бит, докато гол литерал като -10 е по-подразбиране int т.е. винаги 32 бит. Затова off_t(-10) го оправя също както и -10L Пробвай само за експеримента да го компилираш като 32 (НЕ като 64) тогава всичко ще си е int и 32 бита и НП и това би трябвало да оправи нещата ..и публикувай резултата :-) Справка (секцията 64-bit data models) http://en.wikipedia.org/wiki/64-bit_computing Полезни четива по темата 32/64 бита (особено последното) - за тези които знаят английски (нищо че автора е руснак) http://www.codeproject.com/KB/architecture/20ISSUES64BIT.aspx?msg=3485399 http://www.viva64.com/en/l/ http://www.viva64.com/en/a/0043/ Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 29, 2012, 21:26 Thanks,
За сега ще лягам и ше разгелдам утре пак какво ще отркия :) Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 29, 2012, 21:53 Нещо прикаченият ти файл е празен
Последно уточняване от моя страна 0xFFFF FFF6 == -10 (signed int) 0xFFFF FFF6 == 4,294,967,286 (unsigned int) което е неправилно - би трябвало да се размножи знаковия бит "1" и да е 0xFFFF FFFF FFFF FFF6 4,294,967,286 + 10 (от "abcde" + "12345") = 4,294,967,296 точно колкото ти е размера на файла дето cat нямаше търпение да го дочакаш я пусни и tail на тоя файл - може би ще видим там на края и 12345 ;-) Може да са там а може и да не са - ако ги е записало на отрицателно отместване тогава за да видим нещо в края запиши низ по-дълъг от 10 символа втория път :-) П.П. Сама по себе си lseek не променя размера на файла а последващия запис на "12345" т.е. все трябва да ги е записало някъде та просто от любопитсво питам опашката на файла какво съдържа. Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 29, 2012, 22:11 Така е, по късно се сетих и проверих. Явно файла е с дупка :)
Тая дупка смята ли се за заето пространство? Щото аз толкова място на диска нямам :). Сещам се че и lastlog файла има подобно поведение като винаги показва максимален възможен размер. Защо и как? du показва 8 байта. аз бях написал 10. защо след като направя такъв счупен файл, флаговете на файла се сменят при всяко пускане на програмата? Прикачения файл е празен да :) Много интересно - ако пробваш да прикачиш /dev/zero и да го upload-неш (смех!), нищо няма да стане, но ако пробваш да прекачиш такъв файл който е генериран с тая програма... тъй де, сигурно има и по лесен начин на флудене, но това е доста компактно. BTW, споменах го само вяло, но наистина ми е доста интересно как се управлява тая дупка? Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 29, 2012, 22:31 А защо смени SEEK_SET на SEEK_CUR?
Google: Sparse files "Sparse files do not have disk space allocated for every block in the whole address space, leading to holes within the file. Sparse files are basically just like any other file except that blocks that only contain zeros (i.e., nothing) are not actually stored on disk. This means you can have an apparently 16G file – with 16G of “data” – only taking up 1G of space on disk." http://lwn.net/Articles/357767/ http://administratosphere.wordpress.com/2008/05/23/sparse-files-what-why-and-how/ http://stackoverflow.com/questions/5315428/how-to-create-a-file-with-file-holes http://linuxgazette.net/174/misc/lg/compressing_sparse_file_s_while_still_maintaining_their_holes.html Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 29, 2012, 22:50 Цитат А защо смени SEEK_SET на SEEK_CUR? Без особена причина. Има ли значение, предвид това, че не следвам някакъв определен алгоритъм? Ок, значи стигаме до такъв извод: signed long го превръща в unsigned long когато по подам на lseek. По тази причина каквото пиша отива в края. Само че има един проблем - off_t не е казано че е unsigned. не виждам как (off_t)(-10) ще промени нещо. Утре ке тестваме и ке разбереме :) Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 29, 2012, 23:34 (off_t)(-10) ще ти го направи 64 битово -10 т.е.
(off_t)(-10) == 0x FFFF FFFF FFFF FFF6 (int)(-10) == 0x FFFF FFF6 тогава gcc ще не ще - ще извика правилната функция (64 битова вместо 32) т.е. вместо %еsi ще ползва %rsi което няма да го отреже поради незнание от 0x FFFF FFFF FFFF FFF6 на 0x FFFF FFF6 което предполагам после неправилно се разширява до 0x 0000 0000 FFFF FFF6 (това нещо и със и без знак все си е 4,294,967,286) понеже signed register нема там всичко е unsigned :-) т.е. най-вероятно въобще не се разширява (пак добре че регистъра явно изначално е занулен при/преди викането на функцията) - прочете ли статията за коня дето можел да говори, а?! :-) правилното разширение би било отново до 0x FFFF FFFF FFFF FFF6 но понеже процесора не знае за какво иде реч.. Затова се използва SAR вместо SHR напр. защото прави същото което ще направи и (off_t)(-10) в сл. запазва и размножава знаковия бит и освен това и кара gcc да свърже правилната 64 битова функция - цяло чудо е че намира с нещо да свърже без явно указан unistd.h - за съжаление явно това не е правилното нещо - нормално ако вижда 2 варианта на lseek единствения параметър по който да ги различи ми се вижда баш отместването т.е. предолагам при следните прототипи на функцията: lseek(int, int, int) // 32 bit lseek(int, long int, int) // 64 bit и ти му излизаш със следната постановка (при 64 битова ОС) lseek(int, int, int) // -10 или lseek(int, long int, int) // (off_t)(-10) Как мислиш кога коя функция ще match-ва ;-) Естествено пробвах преди да пиша първия път и при мен даде резултат - с Федора 17 64 бит съм (int32_t)(-10) == 0x FFFF FFF6 == -10 (uint32_t)(-10) == 0x FFFF FFF6 == 4,294,967,286 обаче интерпретацията на последните 2 е различна -10 / 4,294,967,286 П.П. Една причина поради която 32 битова функция () може да ползва 64 битов или unsigned offset e LFS gcc -D_FILE_OFFSET_BITS=64 http://en.wikipedia.org/wiki/Large_file_support Many old interfaces, especially C-based ones, explicitly specified argument types in a way that did not allow straightforward nor transparent transition to 64-bit types. For example, the C functions fseek and ftell operate on file positions of type long int, which is typically 32 bits wide on 32-bit architecture, and cannot be made larger without sacrificing backward compatibility. (This was resolved by introducing new functions fseeko and ftello. On Windows machines, under Visual C++, functions _fseeki64 and _ftelli64 are used.) Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 30, 2012, 03:58 Струва ми се че малко превратно сте разчели асемблерния код. Няма "32-битова" и "64-битова" версия на функцията в двата листинга, става въпрос за една и съща функция и за 64-битов код. Според AMD64 calling конвенцията, първия аргумент на функцията трябва да е в rdi, втория - в rsi, третия - в rdx и така нататък. Това което предполагам е изрязано от копи-пейста, някъде по-нагоре е зануляването на rsi,rdi и rdx. Предполагам ако shoshon погледне пак ще открие нещо от сорта на xor %rsi,%rsi и xor %rdi,%rdi по-нагоре.
Сега защо във втория случай параметъра се предавал през esi, а не през rsi - esi е просто младшата "половинка" на rsi. Сега няма значение ако присвояваме на rsi някаква 32-битова стойност дали ще правим (при занулен rsi) mov xxx,rsi или mov xxx,esi, но второто си има предимства. Има няколко причини за това - инструкцията е "по-къса" заради по-"късата" литерална стойност и следователно се кодира с по-малко байта, мисля че това е видимо в листинга. Това си има performance предимства - по-добра утилизация на instruction кеша, по-добра утилизация на pipeline-а и така нататък. Допълнително, "по-късите" инструкции дават на компилатора повече свобода да си align-ва кода и това също има значение. Ако все още се съмнявате в горното, вижте как се предава третия аргумент в rdx - флага, който е unsigned int, демек 32-битов. И в двата случая се присвоява на edx, не на rdx. Алтернативно същото става и с първия аргумент на lseek() - 32-битовия файлов дескриптор - предава се през edi, не през rdi. Така че не става въпрос за 32-битова и 64-битова функция, и двете са една и съща 64-битова. Това което не мога да разбера е защо без unistd.h, компилатора решава че off_t аргумента е 32-битов, а не както трябва да е - 64-битов. Това което мога да кажа със сигурност е че компилаторът не прави имплицитно кастване на литералната стойност към unsigned такава, това би било безумно, най-малкото при това положение, щеше да е много добре известен проблем (много хора щяха да изреват с отрицателни литерални стойности и константи, не мислите ли? :) ). Предполагам отговора се крие някъде из unistd.h или по-скоро някъде из включените от него хедъри нейде из asm/<arch>/bits/... Обаче това са толкова добре навързани спагети, че човек трябва да има извънредно здрави нерви да се зарови в това :) Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 30, 2012, 19:33 Да де и аз това имах предвид че се вика функция с 32 битов параметър - което ако беше 32 битова ОС нямаше да е проблем щото нямаше да се налага разширяване на числовия литерал който се подава. Просто може-би малко по-неясно съм се изразил ама то от бързане щото съм доста зает тия дни ..и от старост вече може-би... :'(
Не смятам че оптимизацията на gat3way е валидна в случай като нашия когато се вика код от друг модул /библиотечна функция/ по принцип такива оптимизации са възможни само в/върху един и същи т.нар. translation unit или 1 файл или хайде проект да го наречем - само когато и извикващата и извикваната функция се компилират от същия компилатор и той знае какво да очаква и от двете страни. В случая с викането на библиотечна ф-я или изобщо код от друг translation unit компилатора няма как да знае онова чудо(вище) отсреща как е компилирано (дори и да знае че е пак gcc то не знае с какви опции и оптимизации) - може да е друг компилтор напр. icc, msvc, asm или даже аз да съм си го набичил бинарен код и няма как да очаква че моя бинарен код ще му разшири правилно параметъра - затова обик. това разширяване се прави преди извикването на функцията защото след това не се знае как. напр. 8 към 16 бита че по лесно да речем мойта асемблер функция получи 0xFF как ще знае до какво да го разшири правилно 0xFFFF 0x00FF защото не знае какъв е бил смисъла на тези битове преди извикването на функцията т.е. ако е било число със знак то първото е правилно ако е било без знак то второто... Затова мойто изначално предположение беше че - 1) имаме бъг в gcc - много малко вероятно при положение че препратката която открих е от 1995 досега все щяха да го оправят :o 2) нямаме бъг в gcc обаче поради някаква причина не match-ва правилната ф-я или библиотека - който се решава много лесно - даже има два начина: или включваме правилния header или поне подсказваме на компилатора с type cast точно коя ф-я и как да match-не ..и аз заложих на второто (че няма бъг в gcc) тъй като първото ми се видя много малко вероятно. Изводът е - Винаги включвайте правилните хедъри и желателно е явно/експлицитно да преобразувате типовете а не да оставяте компилатора да си развява... и после до го дебъгвате. Лек ден ви желая и успехи в раследването П.П. За съжаление нямам време теди дни да разследвам повече и аз... П.П.П. Дори и модули компилирани с един с същ компилатор може да имат проблеми заради напр. различни опции по време на компилация напр. даже Майкрософт предупреждаватза "модули" компилирани и от двете страни със същия т.е. техния компилатор не се препоръчва да се link-ват когато са компилирани с различни опции напр. едно и също нещо да ползват като напр. STL (string) защото може да го видят различно :-) според зависи напр. дали DLL е компилиран като много нишков или не и дали е с поддръжка на др. библиотеки от майкросфт напр. MSVCRT*.DLL и с какви оптимизации такак че дори и просто нещо като sizeof(string) да връща различен резултат или дори и да е еднакъв то напр. масив от низове да се подравнява различно. Извинявайте но в момента се боря с Уиндоус програмиране а и... време e да дадем път и повод и на по-млaдите вече да се изяват. Очакваме резултата шошонче >:D Последна редакция: при мен ldd показа това linux-vdso.so.1 => (0x00007fff7a573000) libc.so.6 => /lib64/libc.so.6 (0x00000030a7200000) /lib64/ld-linux-x86-64.so.2 (0x00000030a6e00000) при следните резултати от strace: no unistd type cast lseek(3, 18446744073709551606, SEEK_SET) = -1 EINVAL (Invalid argument) no unistd no type cast lseek(3, 4294967286, SEEK_SET) = 4294967286 unistd no type cast lseek(3, 18446744073709551606, SEEK_SET) = -1 EINVAL (Invalid argument) unistd type cast lseek(3, 18446744073709551606, SEEK_SET) = -1 EINVAL (Invalid argument) също забелязах и това че винаги присъства т.е. O_LARGEFILE възможно да и ма пръст в цялата работа... fcntl(4, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE) Титла: Re: Малък въпрос за lseek() Публикувано от: appmaster в Aug 30, 2012, 21:01 А добре де, гледам че има lseek64(). Интересно ми е при нея дали поведението ще е еквивалентно?
Да не се окаже, че когато го няма header-a, тогава да вика lseek64() или нещо подобно. Но определено има проблем и с linker-a. На мен ми е интересно от къде ти хрумна пък да пробваш без header :D Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 30, 2012, 21:40 Цитат Не смятам че оптимизацията на gat3way е валидна в случай като нашия когато се вика код от друг модул /библиотечна функция/ по принцип такива оптимизации са възможни само в/върху един и същи т.нар. translation unit или 1 файл или хайде проект да го наречем - само когато и извикващата и извикваната функция се компилират от същия компилатор и той знае какво да очаква и от двете страни. В случая с викането на библиотечна ф-я или изобщо код от друг translation unit компилатора няма как да знае онова чудо(вище) отсреща как е компилирано (дори и да знае че е пак gcc то не знае с какви опции и оптимизации) - може да е друг компилтор напр. icc, msvc, asm или даже аз да съм си го набичил бинарен код и няма как да очаква че моя бинарен код ще му разшири правилно параметъра - затова обик. това разширяване се прави преди извикването на функцията защото след това не се знае как. Ммммне, знае как. Има ABI което специфицира как се викат функции, как се разполагат аргументите, в кои регистри или в стека, съответно върнатата стойност от функцията къде и как се пази, дали в регистър, дали в стека. При x86_64 може да се каже че е доста стандартизирано, при x86 може да има разновидности, но на практика дори там под линукс нещата стават предимно по един начин. Това е що се отнася до линукс. Така че такива оптимизации са напълно възможни между различни object файлове, както и в случая когато ползваш външна библиотека. Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 30, 2012, 21:47 Хехех как-как ясно е как... учебникарските примери почти винаги забравят по някой и друг хедър (па и не само те)
После се оправадат че било нарочно оставено за "Упражнение" на четящите... та човекът по едно време се усетил че нещо липсва и явно е наблюдателен та... ще стане програмист от него щом е забелязал разликата 8) gat3way Без да съм ескперт по ABI не бих се съгласил че напр. f(32, 32, 32) ще match-не f(32, 64, 32) нарочно не пиша int а кое колко бита заема щото нали съкращението означава бинарен интерфейс т.е. докато подаваш 32,32,32 ще върви но на 32,64,32 или др. различно няма и .. те затова обик сменят API а пък ABI НЕ БАРАТ Инак... разгледай следната учебникарска постановка напр. с т.нар function overloading f(int32_t, int32_t, int32_t) f(int32_t, int64_t, int32_t) f(-10, -10, -10) - коя функция ще извика според теб - ще извика втората на куково лято - каквото и ABI да има - докато вижда и първата без type cast няма как да извика другата. http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr312.htm Та затова и според мен везните наклониха повече към линкера Аз пък все си мисля че преобразувнаето ако се налага такова по Стандарт трябва да става от викащата страна - мисля че правилото беше първо се разширява до съответния по-голям тип със знак и след това се взима без знак ако трябва т.е. 0xFF -> 0xFFFF (-1 или 65535 зависи от другата страна дали е със знак или без параметъра) което е добре дошло ако имаме предвид -1, но ако сме подали 255(а излиза 65535) жална му майка - озадачава ме че нашият случай е точно обратният П.П. Уточнявам че скоро не съм барал линукс - само джамове и пр. та не съм много навътре как стоят нещата на ниво kernel, ABI и т.п. - карам по стари спомени П.П.П. Може би аз намесвам повече с++ признавам си. Може би има ABI на ниво kernel но напр. ако си пиша и програмата аз и библиотеката (статична или не) аз никой не може да ме задължи да ползвам каквото и да е - дали в стека или в регистри - особено па ако и пиша на асемблер :-) но да не се отклоняваме - практически пример - библиотеката подава/очаква std::string обаче не експортва статичен такъв или дефиницията тогава какво правим ако приложението е компилирано с различни опции или пък с друга версия на STL т.е. разминават им се вижданията за класа std::string (c++ only) Ще ти кажа какво става - едното подава обект което другото не знае какво да прави, но най-лошото е че си мисли че знае. Стига му и един байт разлика отместване и итератора почва да итерира каквото си иска. Хедъра е правилен т.е. същият и при двете компилации - просто вижданията на компилатор, линкер, опции на предните две и т.н. са различни. Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 30, 2012, 22:02 Това е въпрос на прототип на функцията, не на calling конвенцията. Независимо дали ще предаваш 32-битова или 64-битова стойност, аргумента се предава в 64-битов регистър. Дали това е ефективно или не е, е отделен въпрос, но е факт че това работи много добре без много значение какъв тип са аргументите.
Това което искам да кажа е че ти на практика без значение дали го осъзнаваш, винаги подаваш f(64,64,64), без значение дали прототипа на функцията не е f(32,32,64) примерно. Това че функцията очаква 32-битова стойност или че ти подаваш 32-битова стойност, по никакъв начин не променя начина по който викаш функцията - няма специален случай описващ 32-битовите и друг специален случай описващ 64-битовите аргументи, това би било ужасно сложно. Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 30, 2012, 22:07 По мои спомени при 64 бита могат да се подават параметри в регистрите макс. до 4 параметъра/регистъра ако функцията има повече задължително стек.
Аз говорех по принцип - за конкретната функция - може да се ползва подаване чрез регистрите но може и да не се - зависи от опциите при компилиране - предполагам повечето компилатори ще опитмизират и ще опитат да ползват регистри но няма начин и да няма начин изрично да се укаже с опция при компилация дори и един парам да има - През СТЕКА и толкоз. Но всичко това няма отношение по темата ако извикващата страна е разширила типовете правилно - защото извикваната функция не знае нищо - тя си мисли че подаденто отговаря на т.нар. ABI и ползва напр. само еди коя си половина от 1 и 3 регистър пък регистър 2 целия т.е. 64 бита но не и е работа да знае т.е. тя даже не знае при ст-т в регистъра 2 напр. 0000FFF6 това FFF6 ли е та да го разпъна FFFF FFF6 или е вече разширено и си е 0000FFF6 т.е. ABI се дава за да се спазва от викащата страна - откъм виканата страна - там разработчиците си знаят нали те го правят този ABI :-) (А да верно и те също си го спазват) т.е. ако ABI нещо не отговаря или не се спазва откъм библиотеката която гое декларирала... ами обикновено скоро тая библиотека излиза с нов ABI ^-^ и всички дето я ползваме или оставаме въразни към старата версия или си оправяме софтуера да работи с новата библиотека/ABI така де откъм тяхна страна това не е бъг (неспaзването на собствен ABI), докато ако е от наша си е - колкото и витиевато да звучи си е така. Реакциите обик. са: - разработчик на либ/ABI : А-а ще го сменяме скоро и пак ще отговаря докато отношението спрямо разработчик потребител е - не ползвате правилно ABI оправете си кода - т.е. ако е при нас (ползващите либа) е бъг, а ако е при тях (баш дивелъпърите) не е - [_]3 за новите features дет се вика П.П. gat3way да приключим спора: Доколкото разбирам ти твърдиш че при ABI и ф-я с парам. 32, 64, 32 компилатора може да подаде и 32 битова ст-т на втория парам а виканата фунцкия от либ компилиран с незнаен компилатор (може и да не е компилиран ами напр. аз бичим машинен код направо) ще знае как да го разшири правилно и всичко това щото било имало ABI. Аз изхождам от тезата че подаден числов литерал -10 в сл. от викащата страна виканата няма как да знае как да го разшири - ок виканата страна ще знае да очаква не 32 ами 64, може дори да знае (макар че тук навлизаме в сферата на ненаучната фантастика) че "аз чекам 64 бит ама ми е подаден 32 бит парам ама през регистър който винаги е 64 бит та не мога да съм сигурен щото не знам колегата 'различен компилатор' как е решил да го подаде, но да кажем че знае че в тия подадени му 64 бита само едните 32 бита са валидни т.е. ще требе импилицитно разширяване до 64 бит - как? като не знаем изначалното -10 ли е било или FFFF FFF6; та да си остане -10 -> -10 и FFFF FFF6 -> 0000 0000 FFFF FFF6; В повечето случаи ще стане (както е по стандарт) FFFF FFFF FFFF FFF6 т.е. първо се разширява към по-големия тип със знак после се каства като без знак (ако се налага) Спор няма че нашия конкретен случай е различен не става FFFF FFFF FFFF FFF6 ами е точно орязаното FFFF FFF6 Инак гледам и двамата клоним към това че не требе да е 32 бита нещо там тоя средния парам. и че не трябва да е без знак т.е. спор няма :-) Мир! Титла: Re: Малък въпрос за lseek() Публикувано от: appmaster в Aug 30, 2012, 22:08 gateway Ема това не е ли случая, когато процесора е х86_64.
Така ли е или още при х86 си ползва >=64 битови регистри (естествено това при процесорите с SSE инструкциите). Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 30, 2012, 22:52 Значи да обобщя (докато сме още в кратко примирие)
Засега варинатите са 1. бъг в gcc вижда правилната функция lseek (64 бита) но не разширява правилно параметъра 2. бъг gcc (или linker) не вижда правилната функция ами ползва друга с (32 бит) параметър като последния има и още един 3. бъг в gcc или либа който ако параметъра правилно е 32 битов би трябвало да приеме параметъра като -10 и да върне грешка а не да го ползва все-едно е приблизително 4G и да прави голям sparse file П.П. Не знам под линукс това O_LARGEFILE може ли да го разшири параметъра на без знак ..мисля че под линукс направо го направиха 64 битов дори и за 32 битовия либ... По мои спомени джамовете по едно време вдигнаха лимита от 2Г само на 4Г та там може и да има и 32 бит signed и unsigned вариант. Въпросът ми е O_LARGEFILE 64 бит парам прави ли го без знак и каква роля играе когато изначално сме 64 бит ОС, либ и пр. защото това показа strace при мен (64 битово ми е всичко) П.П.П. Изводът е Винаги ползвайте правилните хедъри и конвертирайте типовече явно и.. по-горе съм дал препратки по темата 64 битов код, портване 32 към 64 бита, коне който могат да говорят и пр. - интересни нещица, стига да има време и желание човек да се образова. И малко встрани от темата - това че пиша под Уидноус не значи че не ползваме сводобен и товорен софтуер.. Само не ме питайте как се портва за Уиндоус 64 бит - повечето проекти с отворен код казват да имаме версия за Уиндоус и тя... обик. е само 32 бита а то в днешно време няма откъде да си купи човек такова желязо и в дейстовтелност е абсолютно не портабъл кода - да под линукс работи (да не забравяме че модела е различен - затова LLP64/IL32P64 модела) но в действителност не им пука за уиндоус версията (ей тъй само колкото да кажат че имат) Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 30, 2012, 23:04 Абе ей! :)
Има само една calling конвенция за линукс, x86_64, която е специфицирана от sysv amd64 abi-то. И параметрите се подават по един и същ начин, компилаторът не може да си решава самоинициативно как да го прави. Първите 6-7 параметъра се предават през регистри (floating point аргументите се предават през SSE регистри), останалите - в стека, резултатът се връща в rax. Цитат Аз изхождам от тезата че подаден числов литерал -10 в сл. от викащата страна виканата няма как да знае как да го разшири - ок виканата страна ще знае да очаква не 32 ами 64, може дори да знае (макар че тук навлизаме в сферата на ненаучната фантастика) че "аз чекам 64 бит ама ми е подаден 32 бит парам ама през регистър който винаги е 64 бит та не мога да съм сигурен щото не знам колегата 'различен компилатор' как е решил да го подаде, Това е точно така - не знае. Единственият начин програмата ти да знае какво и как да подаде е да има правилния прототип на функцията. Ако това не е налице - примерно ще викне lseek с offset от 4 милиарда вместо -10. Цитат 64 бита само едните 32 бита са валидни т.е. ще требе импилицитно разширяване до 64 бит - как? като не знаем изначалното -10 ли е било или FFFF FFF6; та да си остане -10 -> -10 и FFFF FFF6 -> 0000 0000 FFFF FFF6; Никой имплицитно няма нищо да разширява. В смисъл хубаво е човек да вярва че някой се грижи за него, обаче реалността е сурова. Какво ще му подадеш е едно, какво ще се интерпретира е друго. Няма наготово някакъв механизъм който да прави това по-безопасно. Като му подадеш 32-битовата стойност 0xfffffff6, а той е очаквал 64-битова стойност, ще го интерпретира като 0x00000000fffffff6 и естествено ще се наака, мисля че това се видя вече. Обаче честно казано не знам за какво спорим :) Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 30, 2012, 23:14 На C (и C++ като наследник) има едно нещо наречено the Standard което казва че всяко нещо има тип - когато не е указан явно както е в сл. напр. с литерала -10 (-10L и (off_t)(-10) са друго нещо типа им е указан) то за това -10 се приема че е тип int т.е. цяло число със знак и когато имаш
int64_t t = -10 или fun(int64_t param) -> fun (-10); то в тези случаи -10 което е int се разширява имплицитно първо до по-големия тип със знак и после каст до без знак ПРЕДИ да се присвои или извика функцията. За да не стане това аз виждам следните възможности: или gcc вижда неправилната функция или вижда правилната но поради бъг не прави правилно имплицитното конвертиране - понеже поне от публикувания асембелерен код се видя че още преди извикването на ф-ята параметъра не е очаквания. Затова й предположих че не свързва кода с правилната функция от либ-а. т.е. match-ва нещо дурго ПРЕДИ правилното нещо - защо тогава пак БЕЗ unistd.h но с (off_t)(-10) работи правилно (нямам време сега да пробвам и с -10L) Па може и да сме нацелили бъг няма само джавата да води класациите я :-) http://www.h-online.com/open/news/item/Java-0Day-Turn-off-Java-applets-now-1678618.html Титла: Re: Малък въпрос за lseek() Публикувано от: gat3way в Aug 31, 2012, 00:00 Мисля, че gcc не е виновен - просто прототипа на функцията е "грешен" (не отговарящ на lseek() в glibc) поради някаква причина когато не се include-не unistd.h. В смисъл сигурно ще е вълнуващо преживяване да се проследи какво се случва в системните хедъри и свинщината от препроцесорни директиви там, но нямам много мотивация да го правя в момента :)
Това което мога да кажа със сигурност е че това не се случва на ubuntu 12.04 (в момента нямам достъп до железарията в София, за да проверя как се държи с други инсталации). Титла: Re: Малък въпрос за lseek() Публикувано от: bvbfan в Aug 31, 2012, 13:26 Няма никакъв проблем в компилатора и функциите наистина са 2 lseek и lseek64, инклудването на unistd.h е абсолютно необходимо:
Код иначе ще се викне 32 битовата версия. Функциите с 2 прототипа (без и с 64 отзад) да ви заблудят много сериозно ;) Ето и файлът от glibc 2.16.0 -> http://fossies.org/dox/glibc-2.16.0/posix_2unistd_8h_source.html Титла: Re: Малък въпрос за lseek() Публикувано от: appmaster в Aug 31, 2012, 14:45 Хахахаха знаех си. :D
Еми поуката деца е: Да внимавате какво и къде има, защото иначе стават подобни карамболи :) Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 31, 2012, 18:35 bvbfan не си схванал нещо - нали ако го вижда точно този файл ще е същото като ако го включим явно т.к. и двата прототипа са в ЕДИН и същ файл компилатора ще избере правилния... та така-а-а... явно не ги взема оттук прототипите или ако е оттук тогава линкера има бъг и не линква правилната функция и то само когато хедъра не е включен явно.
Един translation unit си е един не може да го видиш само половината - независимо как - явно , неявно или през крив макарон. Какво ще ползваш от него и какво не е отделен въпрос Предполагам че въобще не вижда нищо оттук ами става нещо друго, но както вече обявих - нямам време вече за такива "забавления". Титла: Re: Малък въпрос за lseek() Публикувано от: remotex в Aug 31, 2012, 19:07 Понеже на никой друг явно не му се занимава - изпозагуби се нещо интерес към темата вкл. и от питащия...
Шошоне до къде стигна? Аз днес пробвах с -Wall и познай какво се случи //без unistd.h Код
Интересното е че (без unistd.h) минава даже и това т.е. колкото си искам параметъра мога да сложа Код
щото то си мисли че аз имплицитно тука я дефинирам :-) интересното е обаче що не минава тогава тва Код
Код
Явно бъг в gcc toolkit към bvbfan по-горе - а на бас че ако gat3way си публикува неговия unistd.h от убунтото на което твърди че го няма тоя проблем и аз моя от Федора-та ми 17 64 бит обновена до последно и ще са еднакви - Тогава защо при него работи както се очаква а при мен не? ..и старите кучета кат gcc имат бубулечки понякога ;D Титла: Re: Малък въпрос за lseek() Публикувано от: appmaster в Aug 31, 2012, 21:51 Код: Invoking: GCC C Compiler Еми и при мен дава това под Eclipse/Ubuntu 12/gcc4.7. Но отговора след пускане на exe-то пак си е: Код: lseek returned error! Явно има някакъв проблем в по-стaрата версия на GCC. И изобщо защо автора не e споделил с какъв setup e? Титла: Re: Малък въпрос за lseek() Публикувано от: spec1 в Aug 31, 2012, 22:44 Не знам как никой не се сети да напердаши в скоби какъв
точно тип е подавания аргумент т.е. func( (short) -10, ....) подава 16 -битово число със знак, func( (long) -10, ....) подава 32 -битово число със знак и т.н. В тоя случай е много малко вероятно нещо да се обърка. Ако се съмнявате в типовете ,винаги може да се изп: sizeof(...) И разбира се ,следите какво ви казва компилатора : предупреждения,грешки ... Титла: Re: Малък въпрос за lseek() Публикувано от: shoshon в Aug 31, 2012, 22:47 Първо искам сърдечно да благодаря на всички умни хора взели участие!
Хора, не съм загубил итнерес - на ден проверявам 10 пъти, но нямам време да пиша - в поправителна сесия съм и ми е яко спечен гъзъ. А и аз от програмиране много не разбирам :). Просто ми направи леко впечатление, че задачката от лекциите не се получи точно както е по книжка :) Или поне така ми се струваше. Имам нужда от ден два (поне до неделя вечер) да се поуспокоят нещата за да мога да обърна внимания на писанията ви. Setup е както следва(Fedora 16): # rpm -q gcc glibc glibc-devel kernel-headers gcc-4.6.3-2.fc16.x86_64 glibc-2.14.90-24.fc16.9.x86_64 glibc-2.14.90-24.fc16.9.i686 glibc-devel-2.14.90-24.fc16.9.x86_64 kernel-headers-3.4.9-1.fc16.x86_64 # После ще систематизирам какво съм научил, ще пусна един bug репорт и ще видим другите умни хора какво ще кажат :) Титла: Re: Малък въпрос за lseek() Публикувано от: bvbfan в Sep 01, 2012, 08:22 Цитат на: remotex Аве, ти нещо не вдяваш от С, ми се струва. Няма никакъв бъг, ако искаш си напиши в кода lseek64 да си викнеш директно функцията. Тук във файла има, ако 64 битова ОС на която се компилира, lseek се заменя с с lseek64р кое не разбра ??? |