1. 程式人生 > >(萊昂氏unix原始碼分析導讀-15) 系統初啟(8)

(萊昂氏unix原始碼分析導讀-15) 系統初啟(8)

程序user態下的分段

User態中將程序空間分為textdatastack segment三部分。

estabur(nt, nd, ns,sep)根據各個segment的大小,為各段分配page,引數如下:

1 nt—— text segment的長度(block

2 nd—— data segment的長度(block

3 ns—— stack segment的長度(block

4 sep——是否“i”、“d”分割槽。(我們只探討sep0的情況

1650: estabur(nt, nd, ns, sep)

1651: {

1652:      register a, *ap, *dp;

1653:

1654:      if(sep) {

                           ……

1659:     } else

1660:          if(nseg(nt)+nseg(nd)+nseg(ns) > 8)       /函式nseg會將block數轉化為page數,

1661:                 goto err;                                          /1 page最多128block,程序user態最多8page

1662:     if(nt+nd+ns+USIZE > maxmem)            

1663:             goto err;

1664:     a = 0;                                                          /uisa[]0開始計數,單位是block

1665:     ap = &u.u_uisa[0];                          

1666:     dp = &u.u_uisd[0];

1667:     while(nt >= 128) {                                   /對映text segment

1668:          *dp++ = (127<<8) | RO;                   /1

page最多128block

1669:          *ap++ = a;                                           /text segment是隻讀的,故為RO

1670:           a =+ 128;

1671:           nt =- 128;

1672:      }

1673:      if(nt) {                                                   /不夠128block的部分,

1674:          *dp++ = ((nt-1)<<8) | RO;               /也必須對映到一個單獨的page

1675:          *ap++ = a;

1676:      }

1677:      if(sep)

             ……

1681:          }

1682:       a = USIZE;                                            /a回滾了,與Text segment的設定有重疊

1683:       while(nd >= 128) {

1684:            *dp++ = (127<<8) | RW;                /除了狀態為RW外,其他設定與text類似

1685:            *ap++ = a;                          

1686:            a =+ 128;

1687:            nd =- 128;

1688:       }

1689:       if(nd) {

1690:           *dp++ = ((nd-1)<<8) | RW;

1691:           *ap++ = a;

1692:           a =+ nd;

1693:       }

1694:       while(ap < &u.u_uisa[8]) {                  /剩下的page0

1695:           *dp++ = 0;

1696:           *ap++ = 0;

1697:        }

1698:        if(sep)

               ……

1702:        }

1703:        a =+ ns;                                            /a指向stack segment的最後

1704:        while(ns >= 128) {

1705:              a =- 128;                                    /設定stack segment

1706:              ns =- 128;

1707:              *--dp = (127<<8) | RW;

1708:              *--ap = a;

1709:        }

1710:        if(ns) {

1711:              *--dp = ((128-ns)<<8) | RW | ED;

1712:              *--ap = a-128;

1713:        }

1714:       if(!sep) {                                             /如未進行“i”、“d”分割槽

1715:            ap = &u.u_uisa[0];                    

1716:            dp = &u.u_uisa[8];

1717:            while(ap < &u.u_uisa[8])              /uisa[8~15] = uisa[0~7]

1718:                 *dp++ = *ap++;

1719:            ap = &u.u_uisd[0];

1720:            dp = &u.u_uisd[8];

1721:            while(ap < &u.u_uisd[8])              /uisd[8~15] = uisd[0~7]

1722:                  *dp++ = *ap++;

1723:      }

1724:      sureg();                                                /稍後討論

1725:      return(0);

1726:

1727: err:

1728:      u.u_error = ENOMEM;

1729:      return(-1);

1730: }

從程式碼中得知:

1unix會將textdatastack對映到不同的page上去;

2uisd[]中記錄的似乎就是相應的page description register的值;

3uisa[]卻不是page address register的值—— textdata segment有重疊。

它是什麼呢?我們接著往下看。

sureg()會根據u_uisa[]u_uisd[]陣列來設定相應的UISAUISD register,通過這個函式,

我們可以清楚的知道u_uisa[]u_uisd[]陣列的真正含義。

1739: sureg()

1740: {

1741:     register *up, *rp, a;

1742:

1743:     a = u.u_procp->p_addr;          /a為程序私有空間的開始block

1744:     up = &u.u_uisa[16];

1745:     rp = &UISA->r[16];

1746:     if(cputype == 40) {                                         /PDP-11/40,未進行“i”、“d”分割槽

1747:         up -= 8;                                                        /故,u_uisa[8~15]是無效的

1748:         rp -= 8;

1749:     }

1750     while(rp > &UISA->r[0])                                 /UISA[ ] = u_uisa[ ] + a

1751:         *--rp = *--up + a;

1752:     if((up=u.u_procp->p_textp) != NULL)          /text segment已經分配了地址,text segment

1753:         a -= up->x_caddr;                                        /需要特殊調整。up->x_caddrtext segment

1754:     up = &u.u_uisd[16];                                        /core load地址,對#1程序,a值未變(可認其

1755:     rp = &UISD->r[16];                                        /core load地址為0

1756:     if(cputype == 40) {  

1757:         up -= 8;

1758:         rp -= 8;

1759:     }

1760:     while(rp > &UISD->r[0]) {

1761:         *--rp = *--up;                                           /UISA[ ] = u_uisd[ ]

1762:         if((*rp & WO) == 0)                               /只有text segment是不可寫的

1763:            rp[(UISA-UISD)/2] -= a;                    /重新調整text segmentpage address register

1764:     }                                                                 /rp現在指向UISD,通過UISAUISD地址計算

1765: }                                                                    /出偏移量,直接通過rp操作UISA

顯然:

1u_uisd[]中記錄的就是相應的page description register的值;

2data segmentstack segment均被對映到程序的“私有空間”的PPDA之後;

對這兩種segmentu_uisa[]protype)記錄的為相對於PPDA的偏移block數;

        【注】:這兩個user態的segment被對映到程序的“私有空間”之後,也變成了程序的“私有空間”的一

                       部分。也就是說,程序擁有連續的私有空間,且可以分為兩部分:一部分在kernel使用,一部分

                       在user態使用。連續的空間為swap提供了方便。

3text segment如已有地址,則從原來地址開始對映,否則(如我們的#1程序),從第0block開始對映。

text segmentu_uisa[]記錄的為相對於core Load地址(#1程序為0)的偏移block數。

u中還有一些變數與這三個segment有關,如:

0440: int u_tsize; /* text size (*64) */ 記錄的是block

0441: int u_dsize; /* data size (*64) */

0442: int u_ssize; /* stack size (*64) */

protype再分析】:

為什麼會設計protype Address呢?很簡單,程序在其生存期內是有可能被換出記憶體的,當再次被換進記憶體時,

     它佔據的物理位置就會發生變化——而其protype Address不變。這樣通過呼叫sureg()就可以方便的設定好

     user Address register