进程user态下的分段
User态中将进程空间分为text、data、stack 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”分区。(我们只探讨sep为0的情况)
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最多128个block,进程user态最多8个page
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最多128个block
1669: *ap++ = a; /text segment是只读的,故为RO
1670: a =+ 128;
1671: nt =- 128;
1672: }
1673: if(nt) { /不够128个block的部分,
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]) { /剩下的page清0
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: }
从代码中得知:
(1)unix会将text、data、stack映射到不同的page上去;
(2)uisd[]中记录的似乎就是相应的page description register的值;
(3)uisa[]却不是page address register的值—— text和data segment有重叠。
它是什么呢?我们接着往下看。
sureg()会根据u_uisa[]和u_uisd[]数组来设置相应的UISA和UISD 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_caddr为text 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 segment的page address register
1764: } /rp现在指向UISD,通过UISA和UISD地址计算
1765: } /出偏移量,直接通过rp操作UISA
显然:
(1)u_uisd[]中记录的就是相应的page description register的值;
(2)data segment和stack segment均被映射到进程的“私有空间”的PPDA之后;
对这两种segment,u_uisa[](protype)记录的为相对于PPDA的偏移block数;
【注】:这两个user态的segment被映射到进程的“私有空间”之后,也变成了进程的“私有空间”的一
部分。也就是说,进程拥有连续的私有空间,且可以分为两部分:一部分在kernel使用,一部分
在user态使用。连续的空间为swap提供了方便。
(3)text segment如已有地址,则从原来地址开始映射,否则(如我们的#1进程),从第0个block开始映射。
对text segment,u_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。
博客地址:http://blog.youkuaiyun.com/cszhao1980
博客专栏地址: http://blog.youkuaiyun.com/column/details/lions-unix.html