• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 操作系統的實現細節(2)

    發表于:2007-07-04來源:作者:點擊數: 標簽:
    在第一篇文章里面詳細說了一下系統啟動和文件系統的一些差別,這篇文章我想理清一下思路,想象最初的引導程序獲得了系統地引導權以后,后面所應該做的事情? 現在做個假設,如果自己用c語言寫一段hello world程序,用arm linux -gcc編譯器編譯好,然后用jtag燒
    在第一篇文章里面詳細說了一下系統啟動和文件系統的一些差別,這篇文章我想理清一下思路,想象最初的引導程序獲得了系統地引導權以后,后面所應該做的事情?

    現在做個假設,如果自己用c語言寫一段hello world程序,用armlinux-gclearcase/" target="_blank" >cc編譯器編譯好,然后用jtag燒寫到板子得flash當中,程序是否可以運行?答案是相當否定的。因為這個時候,沒有對底層的硬件做任何的初始化,可以通過匯編,直接對串口進行操作,但是對于gcc的printf,畢竟也是一個高級的包裝函數,現在根據gcc的源代碼順藤摸瓜,看看具體是怎么實現的?

    //printf.c

    int
    printf (const char *string, ...)
    {
      va_list ap;
      int r;
    #ifdef __OPTIMIZE__
      if (inside_main)
        abort();
    #endif
      va_start (ap, string);
      r = vprintf (string, ap);
      va_end (ap);
      return r;
    }

    //vprintf.c

    nt
    vprintf (format, ap)
         const char *format;
         va_list ap;
    {
      return vfprintf (stdout, format, ap);
    }
    //FILE *stdout

    typedef struct _iobuf
    {
     char* _ptr;
     int _cnt;
     char* _base;
     int _flag;
     int _file;
     int _charbuf;
     int _bufsiz;
     char* _tmpfname;
    } FILE;

    //vfprintf.c

    int
    vfprintf (stream, format, ap)
      FILE * stream;
      const char * format;
      va_list ap;
    {
      return _doprnt (format, ap, stream);
    }

    //_doprnt.c


    int
    _doprnt (format, ap, stream)
      const char * format;
      va_list ap;
      FILE * stream;
    {
      const char * ptr = format;
      char specifier[128];
      int total_printed = 0;
     
      while (*ptr != '')
        {
          if (*ptr != '%') /* While we have regular characters, print them.  */
     PRINT_CHAR(*ptr);
          else /* We got a format specifier! */
     {
       char * sptr = specifier;
       int wide_width = 0, short_width = 0;
      
       *sptr++ = *ptr++; /* Copy the % and move forward.  */

       while (strchr ("-+ #0", *ptr)) /* Move past flags.  */
         *sptr++ = *ptr++;

       if (*ptr == '*')
         COPY_VA_INT;
       else
         while (ISDIGIT(*ptr)) /* Handle explicit numeric value.  */
           *sptr++ = *ptr++;
      
       if (*ptr == '.')
         {
           *sptr++ = *ptr++; /* Copy and go past the period.  */
           if (*ptr == '*')
      COPY_VA_INT;
           else
      while (ISDIGIT(*ptr)) /* Handle explicit numeric value.  */
        *sptr++ = *ptr++;
         }
       while (strchr ("hlL", *ptr))
         {
           switch (*ptr)
      {
      case 'h':
        short_width = 1;
        break;
      case 'l':
        wide_width++;
        break;
      case 'L':
        wide_width = 2;
        break;
      default:
        abort();
      }
           *sptr++ = *ptr++;
         }

       switch (*ptr)
         {
         case 'd':
         case 'i':
         case 'o':
         case 'u':
         case 'x':
         case 'X':
         case 'c':
           {
      /* Short values are promoted to int, so just copy it
                       as an int and trust the C library printf to cast it
                       to the right width.  */
      if (short_width)
        PRINT_TYPE(int);
      else
        {
          switch (wide_width)
            {
            case 0:
       PRINT_TYPE(int);
       break;
            case 1:
       PRINT_TYPE(long);
       break;
            case 2:
            default:
    #if defined(__GNUC__) || defined(HAVE_LONG_LONG)
       PRINT_TYPE(long long);
    #else
       PRINT_TYPE(long); /* Fake it and hope for the best.  */
    #endif
       break;
            } /* End of switch (wide_width) */
        } /* End of else statement */
           } /* End of integer case */
           break;
         case 'f':
         case 'e':
         case 'E':
         case 'g':
         case 'G':
           {
      if (wide_width == 0)
        PRINT_TYPE(double);
      else
        {
    #if defined(__GNUC__) || defined(HAVE_LONG_DOUBLE)
          PRINT_TYPE(long double);
    #else
          PRINT_TYPE(double); /* Fake it and hope for the best.  */
    #endif
        }
           }
           break;
         case 's':
           PRINT_TYPE(char *);
           break;
         case 'p':
           PRINT_TYPE(void *);
           break;
         case '%':
           PRINT_CHAR('%');
           break;
         default:
           abort();
         } /* End of switch (*ptr) */
     } /* End of else statement */

    }

    return total_printed;
    }

    #define PRINT_CHAR(CHAR) \
      do { \
      putc(CHAR, stream); \
      ptr++; \
      total_printed++; \
      continue; \
         } while (0)

    //System.h

    #  define putc(C, Stream) putc_unlocked (C, Stream)

    //2005-06-22

    剛才下載了2.95的gcc,發現了可以繼續探索下去

    //putc_u.h

    int
    putc_unlocked (c, fp)
         int c;
         _IO_FILE *fp;
    {
      CHECK_FILE (fp, EOF);
      return _IO_putc_unlocked (c, fp);
    }

    //libio.h
    struct _IO_FILE {
      int _flags;  /* High-order word is _IO_MAGIC; rest is flags. */
    #define _IO_file_flags _flags

      /* The following pointers correspond to the C++ streambuf protocol. */
      /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
      char* _IO_read_ptr; /* Current read pointer */
      char* _IO_read_end; /* End of get area. */
      char* _IO_read_base; /* Start of putback+get area. */
      char* _IO_write_base; /* Start of put area. */
      char* _IO_write_ptr; /* Current put pointer. */
      char* _IO_write_end; /* End of put area. */
      char* _IO_buf_base; /* Start of reserve area. */
      char* _IO_buf_end; /* End of reserve area. */
      /* The following fields are used to support backing up and undo. */
      char *_IO_save_base; /* Pointer to start of non-current get area. */
      char *_IO_backup_base;  /* Pointer to first valid character of backup area */
      char *_IO_save_end; /* Pointer to end of non-current get area. */

      struct _IO_marker *_markers;

      struct _IO_FILE *_chain;

      int _fileno;
      int _blksize;
    #ifdef _G_IO_IO_FILE_VERSION
      _IO_off_t _old_offset;
    #else
      _IO_off_t _offset;
    #endif

    #define __HAVE_COLUMN /* temporary */
      /* 1+column number of pbase(); 0 is unknown. */
      unsigned short _cur_column;
      char _unused;
      char _shortbuf[1];

      /*  char* _save_gptr;  char* _save_egptr; */

    #ifdef _IO_LOCK_T
      _IO_LOCK_T _lock;
    #endif
    #if defined(_G_IO_IO_FILE_VERSION) && _G_IO_IO_FILE_VERSION == 0x20001
      _IO_off64_t _offset;
      int _unused2[16]; /* Make sure we don't get into trouble again.  */
    #endif
    };


    #define _IO_putc_unlocked(_ch, _fp) \
       (((_fp)->_IO_write_ptr >= (_fp)->_IO_write_end) \
        ? __overflow (_fp, (unsigned char) (_ch)) \
        : (unsigned char) (*(_fp)->_IO_write_ptr++ = (_ch)))
    繼續...

    int
    __overflow (f, ch)
         _IO_FILE *f;
         int ch;
    {
      return _IO_OVERFLOW (f, ch);
    }

    //libiop.h

    /* The 'overflow' hook flushes the buffer.
       The second argument is a character, or EOF.
       It matches the streambuf::overflow virtual function. */
    typedef int (*_IO_overflow_t) __PMT ((_IO_FILE *, int));
    #define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)

    //go on...

    # define JUMP1(FUNC, THIS, X1) _IO_JUMPS(THIS)->FUNC (THIS, X1)

    #define _IO_JUMPS(THIS) ((struct _IO_FILE_plus *) (THIS))->vtable

    struct _IO_FILE_plus
    {
      _IO_FILE file;
      const struct _IO_jump_t *vtable;
    };

    struct _IO_jump_t
    {
        JUMP_FIELD(_G_size_t, __dummy);
    #ifdef _G_USING_THUNKS
        JUMP_FIELD(_G_size_t, __dummy2);
    #endif
        JUMP_FIELD(_IO_finish_t, __finish);
        JUMP_FIELD(_IO_overflow_t, __overflow);
        JUMP_FIELD(_IO_underflow_t, __underflow);
        JUMP_FIELD(_IO_underflow_t, __uflow);
        JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
        /* showmany */
        JUMP_FIELD(_IO_xsputn_t, __xsputn);
        JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
        JUMP_FIELD(_IO_seekoff_t, __seekoff);
        JUMP_FIELD(_IO_seekpos_t, __seekpos);
        JUMP_FIELD(_IO_setbuf_t, __setbuf);
        JUMP_FIELD(_IO_sync_t, __sync);
        JUMP_FIELD(_IO_doallocate_t, __doallocate);
        JUMP_FIELD(_IO_read_t, __read);
        JUMP_FIELD(_IO_write_t, __write);
        JUMP_FIELD(_IO_seek_t, __seek);
        JUMP_FIELD(_IO_close_t, __close);
        JUMP_FIELD(_IO_stat_t, __stat);
    #if _G_IO_IO_FILE_VERSION == 0x20001
        JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
        JUMP_FIELD(_IO_imbue_t, __imbue);
    #endif
    #if 0
        get_column;
        set_column;
    #endif
    };

    哈,終于找到了最終的函數_IO_seek_t ,__write,根據此處的經驗來看,應該為操作系統實現的。

    費了這么大一半天,只是想說明,gcc編譯出來的東西,其依賴于操作系統的實現,也即是說,gcc和操作系統是相輔相成的,操作系統負責提供一些最底層的基本的函數,而gcc則把這些東西包裝起來。

    具體細節,在編操作系統的時候,例如定制自己的mystdio.h,里面實現__swbuf函數,然后就可以調用printf啦

    不過有點迷惑,是如何商量好__swbuf函數的?莫非還能繼續分解下去?還請通曉之人指教以下~

    其實說到最后,無論elf還是exe格式,說到底都是2進制文件,它們的差別,就在于操作系統的實現差別,操作系統裝入這些可執行文件的text段啊,動態連接庫等等的不同,導致了各個文件只能在其自己的系統上運行,而linux下面的wine軟件,正是看到了這點,把windows對于exe文件的前期配置按照linux配置完畢,然后么,跳轉到可執行代碼段,剩下的就是機器自己的事情了。

    為完待續。。。

    下回將分析一下,操作系統是如何運行一個文件的?需要做哪些前期工作?可執行文件里面包含了什么秘密?

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>