Linux 版 (精华区)

发信人: Baggio (傻瓜牌生活), 信区: Linux
标  题: lisoleg周刊(99/4/25) (转寄)
发信站: 紫 丁 香 (Tue Apr 27 22:23:15 1999), 转信

寄信人: HeartRain.bbs@diamond.ncic.ac.cn 
标  题: lisoleg周刊(99/4/25) (转寄)
发信站: 哈工大紫丁香 BBS 信差
来  源: from bbs.ncic.ac.cn ([159.226.41.165])
日  期: Sun Apr 25 22:12:49 1999

发信人: fzhang (老铁), 信区: linux
标  题: lisoleg周刊(99/4/25) 
发信站: BBS 曙光站 (Sun Apr 25 19:29:45 1999)


◆回复此帖 ◆阅读前一帖 
  主 题:  lisoleg周刊(99/4/25) 
  作 者:  老铁 <lao_tie@263.net>
  日 期:  4/25/99 19:35 
  论 坛:  BJLC 源码论坛 
  内 容:
Welcome to Lisoleg.yeah.net! 

LisoLeg利索的脚 
---北京Linux 俱乐部S计划 协调人:老 铁(mailto:lao_tie@263.net) 
研读学习Linux源码,练就一双利索的脚,奔跑在计算机学科的高原 

Lisoleg CopyLeft 

(1) 这篇稿子的目的在于帮助那些对Linux核心代码感兴趣的朋友 
更好的理解核心。lisoleg不能够保证它的“完全正确”,但是lisoleg 
将尽最大的努力来做到这一点。如果它直接或间接的给您造成了经 
济或其他方面的损失,lisoleg不能承担法律上的责任。 

(2) 你拥有在任何地方引用这篇文章的权利。lisoleg并不强迫您在 
引用的时候一定要提及lisoleg的名字以及联系方法,但是如果您可 
以做到这一点,lisoleg将十分感激。如果您因为引用了这篇文章而 
获得了经济上的利益,并且您愿意让lisoleg分享一点,lisoleg也十 
分感激,但是lisoleg也不强迫这一点。 

(3)如果您发现的这篇文章中存在错误,请您及时的通知lisoleg。 
lisoleg也十分欢迎你对这篇文章做出进一步的改进,特别是那些标 
志着“疑问”的地方。 

(4)lisoleg的Email地址是lisoleg@egroups.com,欢迎感兴趣的与 
lisoleg联系和探讨,也请给lisoleg一点鼓励。 

Lisoleg.yeah.net(http://mud.263.net.cn/~linux/project/lisoleg/index.html ) 计数器 2,059 (19:00 99/4/25) 
Lisoleg BJLC源码论坛(http://mud.263.net.cn/~linux/cgi-bin/webforum/source.cgi) 
本周所有文章 

源码论坛 
第 17247 次访问 欢迎回来,您,老铁,上次来访是在4/25/99 18:01.之后,没有新帖. 本次显示最近1周的帖子,共有49个帖子.显示风格:"话题展开". 

◆加新帖 ◆检索 ◆页面风格设定 ◆历史记录 ◆返回主页 

GNU CC 支持多平台的实现技术 (3151字节) 老铁 4/24/99 22:27 
GNU CC 2.7.2解剖和移植(1) (9742字节) 老铁 4/24/99 22:24 
uC/OS The Real-Time kernel (452字节) 老铁 4/14/99 21:20 
Re: uC/OS The Real-Time kernel (303字节) xieliang 4/24/99 11:26 
Re: uC/OS The Real-Time kernel (86字节) 老铁 4/24/99 22:00 
基于知识库的源代码 (984字节) daydream 4/23/99 12:28 
Re: 基于知识库的源代码 (50字节) netcard 4/23/99 21:31 
Re: 基于知识库的源代码 (70字节) 灵溪 4/24/99 08:24 
Re: 基于知识库的源代码 (6188字节) daydream 4/24/99 12:27 
题外话:让我们都来做傻瓜吧! (211字节) netcard 4/24/99 12:08 
How can we put a single floppy OS into ESDs (359字节) xieliang 4/24/99 12:01 
www.microsoff.com//hehe (3225字节) 老铁 4/23/99 22:31 
在yahoo上查了把kernel (0字节) 老铁 4/23/99 22:24 
www.cs.uit.no/linux-irda (4838字节) 老铁 4/23/99 22:22 
www.uk.linux.org/ELKS-Home/Findex.html (1312字节) 老铁 4/23/99 22:17 
www.crynwr.com/changes (1002字节) 老铁 4/23/99 22:14 
edge.linuxhq.com (1260字节) 老铁 4/23/99 22:13 
lxr.linux.no (5512字节) 老铁 4/23/99 22:05 
Re: lxr.linux.no(很好的源码学习站点) (391字节) 老铁 4/23/99 22:10 
对,但我.... (359字节) xuas 4/23/99 22:09 
www.kernel-panic.com (1365字节) 老铁 4/23/99 22:02 
www.kernel.org (2472字节) 老铁 4/23/99 22:02 
WINIX 操作系统计划 (4775字节) 前龙 4/17/99 15:05 
Re: WINIX 操作系统计划 (2314字节) 灵溪 4/19/99 18:43 
Re: WINIX 操作系统计划 (229字节) netcard 4/17/99 22:36 
你点到了问题核心 (228字节) 前龙 4/19/99 10:47 
Re: 你点到了问题核心 (3453字节) 老铁 4/23/99 21:53 
你应该多了解一下 Unix, X window, Linux 再提出你的计划 (14字节) = 4/19/99 12:57 
Is Linux's BUG ??? (779字节) walker 4/23/99 20:33 
Re: Is Linux's BUG ??? (1401字节) 老铁 4/23/99 21:36 
我们有必要开发一个新的操作系统吗? (183字节) xuas 4/23/99 16:51 
Re: 我们有必要开发一个新的操作系统吗? (1216字节) 灵溪 4/23/99 20:29 
我想从看源码开始学操作系统,该从那而开始呢? (117字节) fool 4/20/99 15:58 
Re: 我想从看源码开始学操作系统,该从那而开始呢? (23字节) teddy 4/21/99 12:33 
Re: 我想从看源码开始学操作系统,该从那而开始呢? (111字节) 灵溪 4/20/99 18:45 
Re: 我想从看源码开始学操作系统,该从那而开始呢? (163字节) fool 4/20/99 20:32 
Re: 我想从看源码开始学操作系统,该从那而开始呢? (293字节) 灵溪 4/21/99 13:56 
Re: 我想从看源码开始学操作系统,该从那而开始呢? (268字节) 老铁 4/21/99 21:08 
bbs.citf.net -- [Lisoleg讨论区]文章列表 (797字节) 老铁 4/21/99 13:47 
gCC的最新版本是2.8.1 (9106字节) 老铁 4/20/99 09:42 
GNU CC 2.7.2解剖和移植(多语种多平台) (187字节) 老铁 4/20/99 09:49 
Re: GNU CC 2.7.2解剖和移植(多语种多平台) (174字节) 灵溪 4/20/99 13:57 
SPIN Operating system (2522字节) 老铁 4/19/99 20:36 
Re: SPIN Operating system (201字节) 老铁 4/19/99 20:41 
Linux中的Socket接入层(Ver. 0.2) (24748字节) 灵溪 4/19/99 17:40 
Visual Tcl IDE (1581字节) lawrence 4/17/99 16:58 
翻译一下行不行? (0字节) = 4/19/99 13:01 

详细内容如下 

◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: Linux中的Socket接入层(Ver. 0.2) 
作 者: 灵溪 
日 期: 4/19/99 17:40 
论 坛: BJLC 源码论坛 
内 容: 
Linux中的Socket接入层(Ver. 0.2) 
一。关于Socket 
对于Socket,我想只要是学习过Unix环境下网络编程的朋友, 
都应该对它再熟悉不过了,说白了,可以把Socket看成一个 
高层的协议(也许不能用“协议”这个词,我找不到更好的 
名词了),它为各种通信协议,比如TCP,IP,链路层协议等, 
提供了统一的接口。这篇文章试图对Linux内核中Socket的实 
现机制进行分析,我尽量把各种实现细节描述的详细一点。 

二。基本介绍(参见《The Linux Kernel》) 
为了缩短篇幅,我省略了这一节的具体内容。有几本不错的参 
考书,建议各位可以一观: 

1。对于Unix环境中的Socket编程,建议阅读一下W. Richard 
Stevens编写的一本《Unix Network Programming-Network 
APIs:Socket and XTI》,这是一本相当不错的参考书,几乎 
囊括了Socket编程的所有细节,而且从中还可以学到很多Unix 
环境下的高级编程技巧。清华大学出版社推出了这本书的影印 
版,国内可以买到。 

2。对于Linux中Socket层的实现细节,可以参考由David A 
Rusling编写的《The Linux Kernel》第十章:Network。这是 
一本系统描述Linux内核技术的参考书,内容包括了Linux内核 
的各个方面,写得相当详细,对于那些想研究Linux源代码的 
朋友,这本书是一本很好的引路石。我写的这篇文章,很大程 
度上是对该书中相应内容的延续。这本书可以在网上Down到, 
感谢我们的David A Rusling。 

3。当然,好书不止我提到的这两本。有很多描述Linux内核的 
其他书籍,例如:《Linux Kernel Internals》,肯定也有这 
方面的内容,如果你搞得到话,相信也是一些很不错的参考。 
因为这种书在国内很难弄到手,所以我也只是“闻其名而未谋 
其面”。如果还有其他好书的话,希望好心人也推荐一些。 

三。Socket数据结构 
struct socket 


socket_state state; 

unsigned long flags; 

struct proto_ops *ops; 

struct inode *inode; 

struct fasync_struct *fasync_list; 

struct file *file; 

struct sock *sk; 

struct wait_queue *wait; 

short type; 

unsigned char passcred; 

unsigned char tli; 
}; 
上面的Socket数据结构中包含的数据主要有两种类型:一种用 
于描述这个Socket本身,例如state,flags,type等,还有一 
些数据(主要是指针数据)用于建立Kernel内部的各种相关数 
据结构之间的连接,例如file,sk,inode等。 

需要说明的是,Socket结构本身是包含在struct inode结构中 
的,我们可以看一些证据: 

1。struct inode { 

... 

union { 

struct pipe_inode_info pipe_i; 

struct minix_inode_info minix_i; 

struct ext2_inode_info ext2_i; 

struct hpfs_inode_info hpfs_i; 

struct ntfs_inode_info ntfs_i; 

struct msdos_inode_info msdos_i; 

struct umsdos_inode_info umsdos_i; 

struct iso_inode_info isofs_i; 

struct nfs_inode_info nfs_i; 

struct sysv_inode_info sysv_i; 

struct affs_inode_info affs_i; 

struct ufs_inode_info ufs_i; 

struct romfs_inode_info romfs_i; 

struct coda_inode_info coda_i; 

struct smb_inode_info smbfs_i; 

struct hfs_inode_info hfs_i; 

struct adfs_inode_info adfs_i; 

struct qnx4_inode_info qnx4_i; 

struct socket socket_i; 

void *generic_ip; 

} u; 

... 
}; 
inode结构中的成员inode.u是一个union数据,在inode初始化 
的时候,核心会根据不同的底层填入相应的内容。 

2。我们还可以看一下socki_lookup函数,这个函数根据参数 
指定的inode指针返回相应的socket指针。 
extern __inline__ struct socket *socki_lookup(struct inode *inode) 


return &inode->u.socket_i; 


四。为Socket数据结构分配内存:socket_alloc函数 
struct socket *sock_alloc(void) 


struct inode * inode; 

struct socket * sock; 

inode = get_empty_inode(); 

if (!inode) 

return NULL; 

sock = socki_lookup(inode); 

inode->i_mode = S_IFSOCK|S_IRWXUGO; 

inode->i_sock = 1; 

inode->i_uid = current->uid; 

inode->i_gid = current->gid; 

sock->inode = inode; 

init_waitqueue(&sock->wait); 

sock->fasync_list = NULL; 

sock->state = SS_UNCONNECTED; 

sock->flags = 0; 

sock->ops = NULL; 

sock->sk = NULL; 

sock->file = NULL; 

sockets_in_use++; 

return sock; 


1。调用get_empty_inode分配一个空的inode数据结构。 
get_empty_inode会负责在Kernel中分配内存。 

2。调用socki_lookup返回分配的inode结构中的相应的socket 
指针。 

3。对相应的inode结构以及socket结构中的一些数据成员进行 
初始化。 

4。全局变量sockets_in_use记录了系统已经分配的socket的 
总数。 

五。Socket上的操作 
1。如果你熟悉Unix下Socket编程,那么你就知道socket上的 
各种读写操作与各种文件的读写操作非常类似,这是实现Socket 
层的目的之一。为了实现这种机制,所有socket上的操作都是通 
过一个file_operations数据结构进行分配的(我使用了“分 
配”这个词,它的含义就是一个具体的操作到达某一个物理硬 
件设备一种机制,用一个英文单词的话,我想应该是 
“Dispatch”,如果你了解Linux的文件系统的话,应该可以 
明白“分配”这个词的具体内涵。另外,Linux中有很多的实 
现都是利用了这种“Dispatch”机制,往往用于实现某一个与 
下层硬件无关的抽象层。)我们看一下这个结构: 

static struct file_operations socket_file_ops = { 

sock_lseek, 

sock_read, 

sock_write, 

NULL, /* readdir */ 

sock_poll, 

sock_ioctl, 

NULL, /* mmap */ 

sock_no_open, /* special open code to disallow open via /proc */ 

NULL, /* flush */ 

sock_close, 

NULL, /* no fsync */ 

sock_fasync 
}; 

通过上面这张表,我们可以知道对于socket上的各种操作的具 
体分配到哪个函数,例如:socket上的“读”操作会被分配到 
sock_read函数。下面我们具体看一下socket上的各种操作: 

2。socket上的seek操作:sock_lseek 

static long long sock_lseek(struct file *file,long long offset, int whence) 


return -ESPIPE; 


由于socket上不支持seek操作,所有socket上的seek操作导致 
系统返回ESPIPE错误。 

3。socket上的读操作:sock_read 

static ssize_t sock_read(struct file *file, char *ubuf, 

size_t size, loff_t *ppos) 


struct socket *sock; 

struct iovec iov; 

struct msghdr msg; 

① if (ppos != &file->f_pos) 

return -ESPIPE; 

if (size==0) /* Match SYS5 behaviour */ 

return 0; 

② sock = socki_lookup(file->f_dentry->d_inode); 

③ msg.msg_name=NULL; 

msg.msg_namelen=0; 

msg.msg_iov=&iov; 

msg.msg_iovlen=1; 

msg.msg_control=NULL; 

msg.msg_controllen=0; 

iov.iov_base=ubuf; 

iov.iov_len=size; 

return sock_recvmsg(sock, &msg, size, 

!(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT); 


①首先检查指定的某些参数的合理性。 

②然后根据指定的file结构中的inode指针找到相应的socket 
数据结构。 

③socket上的具体读操作是通过sock_recvmsg函数实现的,在 
这里根据指定的参数填充了一些传递给sock_recvmsg函数的数 
据结构,然后调用了这个函数。 

④好了,我们接着看一下这个sock_recvmsg函数: 

int sock_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags) 


struct scm_cookie scm; 

⑤ memset(&scm, 0, sizeof(scm)); 

⑥ size = sock->ops->recvmsg(sock, msg, size, flags, &scm); 

if (size >= 0) 

scm_recv(sock, msg, &scm, flags); 

return size; 


⑤scm是Socket Control Message的缩写。 

⑥在上面已经提到过,Socket层是一个为下层通信协议提供统 
一接口的抽象层,所以所有对Socket的操作都要分配到下层具 
体的协议中去,这种分配机制是通过Socket数据结构中的ops 
域来完成的。socket.ops是一张记录了下层协议各种具体操作 
的函数指针的列表。 

4。socket上的写操作:sock_write 

static ssize_t sock_write(struct file *file, const char *ubuf, 

size_t size, loff_t *ppos) 


struct socket *sock; 

struct msghdr msg; 

struct iovec iov; 

if (ppos != &file->f_pos) 

return -ESPIPE; 

if(size==0) /* Match SYS5 behaviour */ 

return 0; 

sock = socki_lookup(file->f_dentry->d_inode); 

msg.msg_name=NULL; 

msg.msg_namelen=0; 

msg.msg_iov=&iov; 

msg.msg_iovlen=1; 

msg.msg_control=NULL; 

msg.msg_controllen=0; 

msg.msg_flags=!(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT; 

iov.iov_base=(void *)ubuf; 

iov.iov_len=size; 

return sock_sendmsg(sock, &msg, size); 


①sock_write的实现与sock_read的实现非常类似,我们可以 
看到sock_write最终调用了sock_sendmsg函数。 

②下面是sock_sendmsg函数: 

int sock_sendmsg(struct socket *sock, struct msghdr *msg, int size) 


int err; 

struct scm_cookie scm; 

err = scm_send(sock, msg, &scm); 

if (err >= 0) { 

err = sock->ops->sendmsg(sock, msg, size, &scm); 

scm_destroy(&scm); 



return err; 


③对比sock_recvmsg与sock_sendmsg这两个函数,我们看到: 
在sock_recvmsg中,首先调用了下层操作ops->recvmsg,然后 
再调用scm_recv函数;而在sock_sendmsg函数中,函数调用的 
顺序正好相反:首先调用了scm_send函数,然后再调用下层协 
议的操作ops->sendmsg。 

到目前为止,我们对SCM的作用还不够了解。一个合理的猜测是: 
SCM完成Socket抽象层与下层协议之间的通信功能。 

5。socket上的poll操作:sock_poll 

static unsigned int sock_poll(struct file *file, poll_table * wait) 


struct socket *sock; 

sock = socki_lookup(file->f_dentry->d_inode); 

/* 

* We can't return errors to poll, so it's either yes or no. 

*/ 

return sock->ops->poll(file, sock, wait); 


sock_poll函数很简单:它只是把这个操作分配到下层协议就 
完事了。 

6。socket上的IO Control操作:sock_ioctl 

int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 

unsigned long arg) 


struct socket *sock = socki_lookup(inode); 

return sock->ops->ioctl(sock, cmd, arg); 


7。socket上的open操作:sock_no_open 

static int sock_no_open(struct inode *irrelevant, struct file *dontcare) 


return -ENXIO; 


源代码中有一段针对sock_no_open的注释: 

In theory you can't get an open on this inode, but 
/proc provides a back door. Remember to keep it shut 
otherwise you'll let the creepy crawlies in. 

8。socket上的close操作:sock_close 

int sock_close(struct inode *inode, struct file *filp) 


/* 

* It was possible the inode is NULL we were 

* closing an unfinished socket. 

*/ 

if (!inode) 



printk(KERN_DEBUG "sock_close: NULL inode\n"); 

return 0; 



sock_fasync(-1, filp, 0); 

sock_release(socki_lookup(inode)); 

return 0; 


9。socket上的fasync操作:sock_fasync 

static int sock_fasync(int fd, struct file *filp, int on) 


struct fasync_struct *fa, *fna=NULL, **prev; 

struct socket *sock; 

if (on) 



fna=(struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); 

if(fna==NULL) 

return -ENOMEM; 



sock = socki_lookup(filp->f_dentry->d_inode); 

prev=&(sock->fasync_list); 

lock_sock(sock->sk); 

for (fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev) 

if (fa->fa_file==filp) 

break; 

if(on) 



if(fa!=NULL) 



fa->fa_fd=fd; 

kfree_s(fna,sizeof(struct fasync_struct)); 

release_sock(sock->sk); 

return 0; 



fna->fa_file=filp; 

fna->fa_fd=fd; 

fna->magic=FASYNC_MAGIC; 

fna->fa_next=sock->fasync_list; 

sock->fasync_list=fna; 



else 



if (fa!=NULL) 



*prev=fa->fa_next; 

kfree_s(fa,sizeof(struct fasync_struct)); 





release_sock(sock->sk); 

return 0; 


我对fasync这种操作的具体含义还不够了解,所以并不十分理 
解上面这段代码的含义。 

到目前为止,我们至少对Socket层的抽象机制有了一定的认识: 
可以说Socket实现了网络连接上与文件操作类似的读写机制, 
我们把Socket的上层理解为VFS层,下层理解为各种通信协议。 
Socket与上层的联系是通过socket_file_ops函数表实现的, 
Socket与下层协议的联系则是通过Socket数据结构中的ops函 
数列表实现的。 

六。Socket上的一些其他操作 

1。sock_create函数 

int sock_create(int family, int type, int protocol, struct socket **res) 


int i; 

struct socket *sock; 

/* 

* Check protocol is in range 

*/ 
① if(family =NPROTO) 

return -EINVAL; 

#if defined(CONFIG_KMOD) && defined(CONFIG_NET) 

/* Attempt to load a protocol module if the find failed. 



* 12/09/1996 Marcin: But! this makes REALLY only sense, if the user 

* requested real, full-featured networking support upon configuration. 

* Otherwise module support will break! 

*/ 
② if (net_families[family]==NULL) 



char module_name[30]; 

sprintf(module_name,"net-pf-%d",family); 

request_module(module_name); 


#endif 

if (net_families[family]==NULL) 

return -EINVAL; 

/* 
* Check that this is a type that we know how to manipulate and 
* the protocol makes sense here. The family can still reject the 
* protocol later. 
*/ 

③ if ((type != SOCK_STREAM && type != SOCK_DGRAM && 

type != SOCK_SEQPACKET && type != SOCK_RAW && type != SOCK_RDM && 
#ifdef CONFIG_XTP 

type != SOCK_WEB && 
#endif 

type != SOCK_PACKET) || protocol type = type; 

if ((i = net_families[family]->create(sock, protocol)) type域 
可以有以下几种类型: 
SOCK_STREAM 
SOCK_DGRAM 
SOCK_SEQPACKET 
SOCK_RAW 
SOCK_RDM 
SOCK_WEB 
SOCK_PACKET 

④调用sock_alloc为新的socket分配内存。 

⑤调用记录在net_families表中相应下层协议的create函数, 
进行下层协议的一些初始化工作。 

⑥返回指向新的socket数据结构的指针。 

2。sockfd_lookup 

extern struct socket *sockfd_lookup(int fd, int *err) 


struct file *file; 

struct inode *inode; 

struct socket *sock; 

if (!(file = fget(fd))) 



*err = -EBADF; 

return NULL; 



inode = file->f_dentry->d_inode; 

if (!inode || !inode->i_sock || !(sock = socki_lookup(inode))) 



*err = -ENOTSOCK; 

fput(file); 

return NULL; 



if (sock->file != file) { 

printk(KERN_ERR "socki_lookup: socket file changed!\n"); 

sock->file = file; 



return sock; 


sockfd_lookup根据指定的文件描述符,返回与该描述符对应 
的socket结构的指针。 

七。Socket抽象层为下层协议模块提供的接口函数 

1。sock_register 

int sock_register(struct net_proto_family *ops) 


if (ops->family >= NPROTO) { 

printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family, NPROTO); 

return -ENOBUFS; 



net_families[ops->family]=ops; 

return 0; 


sock_register函数通常是由下层协议模块在模块初始化的时 
候调用,其作用很简单:就是在net_families表中填入相应协 
议的有关信息。 

2。sock_unregister 

int sock_unregister(int family) 


if (family = NPROTO) 

return -1; 

net_families[family]=NULL; 

return 0; 


sock_unregister函数的作用与sock_register正相反。 

八。Socket抽象层与下层协议的配合(举例) 
在这个小节中,我们以一个具体的例子分析一下Socket层与其 
下层协议之间互相配合。我选择了一个相对简单的PF_PACKET 
模块进行了分析。(源文件为net/packe/af_packet.c) 

我们看一下PF_PACKET模块的初始化: 

int init_module(void) 
#else 
void __init packet_proto_init(struct net_proto *pro) 
#endif 

① sock_register(&packet_family_ops); 
② register_netdevice_notifier(&packet_netdev_notifier); 
#ifdef MODULE 

return 0; 
#endif 


①调用sock_register函数,为PF_PACKET模块进行注册。我们 
来看一下packet_family_ops的具体内容: 

static struct net_proto_family packet_family_ops = { 

PF_PACKET, 

packet_create 
}; 

我们可以知道PF_PACKET模块的net_proto_family.create函数 
注册为packet_create,我们会在下面分析一下这个函数的具体 
内容。 

②register_netdevice_notifier函数与我们讨论的重点关系 
不大,暂时不去理它。 

③让我们看一下关心的packet_create函数: 

static int packet_create(struct socket *sock, int protocol) 


struct sock *sk; 

int err; 

④ if (!capable(CAP_NET_RAW)) 

return -EPERM; 

if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW 
#ifdef CONFIG_SOCK_PACKET 

&& sock->type != SOCK_PACKET 
#endif 



return -ESOCKTNOSUPPORT; 

sock->state = SS_UNCONNECTED; 

MOD_INC_USE_COUNT; 

err = -ENOBUFS; 
⑤ sk = sk_alloc(PF_PACKET, GFP_KERNEL, 1); 

if (sk == NULL) 

goto out; 

sk->reuse = 1; 
⑥ sock->ops = &packet_ops; 
#ifdef CONFIG_SOCK_PACKET 

if (sock->type == SOCK_PACKET) 

sock->ops = &packet_ops_spkt; 
#endif 

sock_init_data(sock,sk); 

sk->protinfo.af_packet = kmalloc(sizeof(struct packet_opt), GFP_KERNEL); 

if (sk->protinfo.af_packet == NULL) 

goto out_free; 

memset(sk->protinfo.af_packet, 0, sizeof(struct packet_opt)); 

sk->zapped=0; 

sk->family = PF_PACKET; 

sk->num = protocol; 

/* 

* Attach a protocol block 

*/ 

sk->protinfo.af_packet->prot_hook.func = packet_rcv; 
#ifdef CONFIG_SOCK_PACKET 

if (sock->type == SOCK_PACKET) 

sk->protinfo.af_packet->prot_hook.func = packet_rcv_spkt; 
#endif 

sk->protinfo.af_packet->prot_hook.data = (void *)sk; 

if (protocol) { 

sk->protinfo.af_packet->prot_hook.type = protocol; 
⑦ dev_add_pack(&sk->protinfo.af_packet->prot_hook); 

sk->protinfo.af_packet->running = 1; 



⑧ sklist_insert_socket(&packet_sklist, sk); 

return(0); 

out_free: 

sk_free(sk); 
out: 

MOD_DEC_USE_COUNT; 

return err; 


④首先检查一下一些安全性方面的问题,然后检查一下type参 
数是否合法,从中可以看出PF_PACKET只支持下面三种类型的 
Socket:SOCK_DGRAM,SOCK_RAW和SOCK_PACKET。 

⑤在Socket抽象层中,还有一个重要的数据结构:sock结构。 
根据《The Linux Kernel》书中所述:我们上面主要分析的内 
容属于BSD Socket层,而struct sock结构主要用于另外一个 
Socket抽象层:INET Socket Layer。下面的几段描述摘自于 
《The Linux Kernel》一书: 

This(BSD Socket Layer) is a general interface which 
not only supports various forms of networking but is 
also an inter-process communications mechanism. 

Linux supports the following socket address families 
or domains: 
UNIX Unix domain sockets. 
INET The Internet address family supports 

communications via TCP/IP protocols 
AX25 Amateur radio X25 
IPX Novell IPX 
APPLETALK Appletalk DDP 
X25 X25 

The INET socket layer supports the internet address 
family which contains the TCP/IP protocols. As 
discussed above, these protocols are layered, one 
protocol using the services of another. 

⑥好了,在这里sock->ops域指向相应下层协议的相关操作, 
因此所有Socket上的操作都会导向相应的下层函数。 

⑦dev_add_pack()函数的调用会在系统中的packet_type列表 
中为这个PF_PACKET的Socket加入一个入口。我可以大致描述 
一下一个数据包从网卡接收到以后的分发路径:首先网卡的中 
断处理程序激活,为接收到的数据包分配一个skb,将数据拷 
贝其中,然后将这个skb链入到一个全局的backlog链,并且激 
活net_bh例程(Bottom Half)。在某个合适的时间,Bottom 
Half例程net_bh被调用,它会根据系统的packet_type表将 
backlog链中的各个skb数据分配到相应的处理模块,也就是这 
个时候,我们的PF_PACKET模块中的相应函数会被调用。 

⑧sklist_insert_socket函数将这个struct sock结构插入到 
packet_sklist链中。 

2。我们继续分析一下上面的dev_add_pack()函数中注册的处 
理函数: 
dev_add_pack(&sk->protinfo.af_packet->prot_hook) 
在调用dev_add_pack()函数之前,sk->protinfo.af_packet-> 
prot_hook的成员初始化为: 
type = protocol 
func = packet_rcv或packet_rcv_spkt 
data = (void *)sk 
在net_bh分发skb数据包的时候,它会根据注册的type值决定 
是否将这个skb包发送给PF_PACKET模块。注意到protocol值 
最终是由用户在调用socket()函数申请一个新的socket的时候 
指定的,所以只有用户指定的protocol的数据包才会发送到这 
个模块。而注册的data参数将会作为一个附加参数传送给注册 
的处理函数func(即packet_rcv或packet_rcv_spkt),这样 
在相应的处理函数中我们很容易定位相关的数据结构。接着, 
看一下注册的那个函数:packet_rcv 

static int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) 


struct sock *sk; 

struct sockaddr_ll *sll = (struct sockaddr_ll*)skb->cb; 

/* 

* When we registered the protocol we saved the socket in the data 

* field for just this event. 

*/ 

sk = (struct sock *) pt->data; 

if (skb->pkt_type == PACKET_LOOPBACK) { 

kfree_skb(skb); 

return 0; 



skb->dev = dev; 

sll->sll_family = AF_PACKET; 

sll->sll_hatype = dev->type; 

sll->sll_protocol = skb->protocol; 

sll->sll_pkttype = skb->pkt_type; 

sll->sll_ifindex = dev->ifindex; 

sll->sll_halen = 0; 

if (dev->hard_header_parse) 

sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr); 

if (dev->hard_header) { 

/* The device has an explicit notion of ll header, 

exported to higher levels. 

Otherwise, the device hides datails of it frame 

structure, so that corresponding packet head 

never delivered to user. 

*/ 

if (sk->type != SOCK_DGRAM) 

skb_push(skb, skb->data - skb->mac.raw); 

else if (skb->pkt_type == PACKET_OUTGOING) { 

/* Special case: outgoing packets have ll header at head */ 

skb_pull(skb, skb->nh.raw - skb->data); 





/* 

* Charge the memory to the socket. This is done specifically 

* to prevent sockets using all the memory up. 

*/ 

③ if (sock_queue_rcv_skb(sk,skb) ops = &packet_ops, 
packet_ops[]中记录了PF_PACKET的Socket上的各种操作,现 
在我们来看一下这个packet_ops[]: 

struct proto_ops packet_ops = { 

PF_PACKET, // family 

sock_no_dup, // dup 

packet_release, // release 

packet_bind, // bind 

sock_no_connect, // connect 

sock_no_socketpair, // socketpair 

sock_no_accept, // accept 

packet_getname, // getname 

datagram_poll, // poll 

packet_ioctl, // ioctl 

sock_no_listen, // listen 

sock_no_shutdown, // shutdown 
#ifdef CONFIG_PACKET_MULTICAST // setsockopt 

packet_setsockopt, 
#else 

sock_no_setsockopt, 
#endif 

sock_no_getsockopt, // getsockopt 

sock_no_fcntl, // fcntl 

packet_sendmsg, // sendmsg 

packet_recvmsg // recvmsg 
}; 

4。PF_PACKET上的recvmsg操作:packet_recvmsg 

在上面已经提到,从网卡收到的skb数据包被连接到 
sk->receive_queue队列中,packet_recvmsg函数从这个队列 
中取出skb数据包,并将相关数据拷贝到用户空间。 

static int packet_recvmsg(struct socket *sock, struct msghdr *msg, int len, 

int flags, struct scm_cookie *scm) 


struct sock *sk = sock->sk; 

struct sk_buff *skb; 

int copied, err; 

#if 0 

/* What error should we return now? EUNATTACH? */ 

if (sk->protinfo.af_packet->ifindex type == SOCK_PACKET) 

msg->msg_namelen = sizeof(struct sockaddr_pkt); 

else 

msg->msg_namelen = sizeof(struct sockaddr_ll); 

/* 

* Call the generic datagram receiver. This handles all sorts 

* of horrible races and re-entrancy so we can forget about it 

* in the protocol layers. 



* Now it will return ENETDOWN, if device have just gone down, 

* but then it will block. 

*/ 

① skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err); 

/* 

* An error occurred so return it. Because skb_recv_datagram() 

* handles the blocking we don't see and worry about blocking 

* retries. 

*/ 

if(skb==NULL) 

goto out; 

/* 

* You lose any data beyond the buffer you gave. If it worries a 

* user program they can ask the device for its MTU anyway. 

*/ 

copied = skb->len; 

if (copied > len) 



copied=len; 

msg->msg_flags|=MSG_TRUNC; 



/* We can't use skb_copy_datagram here */ 
② err = memcpy_toiovec(msg->msg_iov, skb->data, copied); 

if (err) 

goto out_free; 

sk->stamp=skb->stamp; 

if (msg->msg_name) 

memcpy(msg->msg_name, skb->cb, msg->msg_namelen); 

/* 

* Free or return the buffer as appropriate. Again this 

* hides all the races and re-entrancy issues from us. 

*/ 

err = copied; 

out_free: 
③ skb_free_datagram(sk, skb); 
out: 

return err; 


①此处调用skb_recv_datagram从sk的receive_queue中获得一 
个skb数据包。 

②memcpy_toiovec将数据拷贝到一个iovec数据结构中,然后 
发送给用户。 

③释放skb占用的内存。 

④至此,我们可以看一下网络数据的流动路径:首先在上面的 
论述中已经提到,原始的网络数据包由网卡驱动程序接收,将 
其封装在一个skb数据结构中,然后插入到一个全局的skb链: 
backlog中。接着,在适当的时候,net_bh例程启动,将这个 
skb数据包发送到相应的协议模块,当数据包发送到PF_PACKET 
模块时,该模块将这个数据包链入sk->receive_queue中等待 
用户读取。好了,现在用户在代表这个socket的文件描述符上 
进行读操作,首先VFS通过inode中的socket_file_ops表将这 
个读操作发送到Socket抽象层,Socket抽象层又通过socket-> 
ops表将该操作发送到相应的下层协议,也就是我们所讨论的 
packet_recvmsg函数,packet_recvmsg将skb数据包从 
receive_queue中取出,发送给用户。OK,差不多了。从中我 
们可以了解到Socket层的主要机制了。 

九。INET Socket抽象层 
我们就以上面这个函数为着手点分析一下INET Socket Layer 
的情况。(相关的源文件为net/core/sock.c) 

1。sk_alloc函数:为struct sock结构分配内存 

struct sock *sk_alloc(int family, int priority, int zero_it) 


struct sock *sk = kmem_cache_alloc(sk_cachep, priority); 

if(sk) { 
① if (zero_it) 

memset(sk, 0, sizeof(struct sock)); 

sk->family = family; 



return sk; 


sk_alloc函数为一个新的struct sock结构分配内存。内存的 
分配是有核心中提供的cache管理函数来负责的。全局变量 
sk_cachep记录了这个cache的头部指针。 

①参数zero_it指示是否将分配的sock结构初始化为0。 

2。sock_init_data:对struct sock中的数据成员进行初始化 

void sock_init_data(struct socket *sock, struct sock *sk) 


skb_queue_head_init(&sk->receive_queue); 

skb_queue_head_init(&sk->write_queue); 

skb_queue_head_init(&sk->back_log); 

skb_queue_head_init(&sk->error_queue); 

init_timer(&sk->timer); 

sk->allocation = GFP_KERNEL; 

sk->rcvbuf = sysctl_rmem_default; 

sk->sndbuf = sysctl_wmem_default; 

sk->state = TCP_CLOSE; 

sk->zapped = 1; 
② sk->socket = sock; 

if(sock) 



sk->type = sock->type; 

sk->sleep = &sock->wait; 
② sock->sk = sk; 



sk->state_change = sock_def_wakeup; 

sk->data_ready = sock_def_readable; 

sk->write_space = sock_def_write_space; 

sk->error_report = sock_def_error_report; 

sk->destruct = sock_def_destruct; 

sk->peercred.pid = 0; 

sk->peercred.uid = -1; 

sk->peercred.gid = -1; 



①让我们看一下struct sock数据结构,这是一个相当大的数 
据结构,下面列出了一些有关的数据成员: 

receive_queue: Incoming packets 
write_queue: Packet sending queue 
back_log: Backlog packet queue 
error_queue: Error packet queue 

上面这四个成员都是指向skb队列的指针,记录了各种不同用 
途的skb队列。skb是Socket Buffer的缩写,这又是网络部分 
一个重要机制。简单介绍一下:从网卡收到的各种数据包,包 
括MAC层的Frame,IP层的Datagram,TCP层的Packet都是封装 
在一个skb数据结构中。当网卡收到数据的时候,驱动程序会 
负责分配一个skb数据结构,然后将收到的数据拷贝到其中。随 
着数据向上层协议的传递,skb中的控制信息将会改变,指示 
当前协议的相关数据的位置。简单而言,skb是封装网卡上原 
始网络数据的一种机制。 

timer: This is the sock cleanup timer. 

timer的类型为struct timer_list。 

allocation: struct sock结构的内存分配模式。 
rcvbuf: Receive buffer的大小,以字节计。 
sndbuf: Send buffer的大小,以字节计。 
state: 网络连接的状态。 
zapped: In ax25 & ipx means not linked。 
socket: 与这个struct sock相联系的socket结构。 

state_change 
data_ready 
write_space 
error_report 
destruct 

上面这五个成员都是一些回调函数的入口。 

protinfo: 这个成员记录了相关协议的信息。结构的声 

明如下: 

/* This is where all the private (optional) areas that don't 
* overlap will eventually live. 
*/ 

union { 

void *destruct_hook; 

struct unix_opt af_unix; 
#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) 

struct atalk_sock af_at; 
#endif 
#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) 

struct ipx_opt af_ipx; 
#endif 
#if defined (CONFIG_DECNET) || defined(CONFIG_DECNET_MODULE) 

struct dn_scp dn; 
#endif 
#if defined (CONFIG_PACKET) || defined(CONFIG_PACKET_MODULE) 

struct packet_opt *af_packet; 
#endif 
#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE) 

x25_cb *x25; 
#endif 
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) 

ax25_cb *ax25; 
#endif 
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) 

nr_cb *nr; 
#endif 
#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) 

rose_cb *rose; 
#endif 
#ifdef CONFIG_NETLINK 

struct netlink_opt af_netlink; 
#endif 
#if defined(CONFIG_ECONET) || defined(CONFIG_ECONET_MODULE) 

struct econet_opt *af_econet; 
#endif 

} protinfo; 

我们又一次看到union结构的使用。 

②注意到struct socket结构与struct sock结构之间通过这两 
个成员的建立互相联系。 

3。sock_queue_rcv_skb 

4。sklist_insert_socket 

--ober 1999.4.19 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: 我们有必要开发一个新的操作系统吗? 
作 者: xuas 
日 期: 4/23/99 16:51 
论 坛: BJLC 源码论坛 
内 容: 
我们有必要开发一个新的操作系统吗? 
到这里我们来讨论一下! 

操作系统开发者论坛 
http://www.netsh.com/wwwboardm/1097/myboard.html 

-------------------------------------------------------------------------------- 

与此相关的帖子 

我们有必要开发一个新的操作系统吗? (183字节) xuas 4/23/99 16:51 
Re: 我们有必要开发一个新的操作系统吗? (1216字节) 灵溪 4/23/99 20:29 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: Re: 我们有必要开发一个新的操作系统吗? 
作 者: 灵溪 
日 期: 4/23/99 20:29 
论 坛: BJLC 源码论坛 
回 复: 我们有必要开发一个新的操作系统吗? (xuas) 
内 容: 
我个人认为开发一个新的OS有几个好处: 
1。作为一个中国的电脑爱好者,自己开发的操作系统显然从头到尾 
都是支持中文的,如果真的成功的话,那么对广大的中国人来说那就 
是一个大福音了。这样的话,向其他多字节内码语言的转换也就比较 
容易了。 
2。从Linux操作系统本身而言,应该说他并不是一个以技术领先的操 
作系统,它的成功完全是一种新的软件开发模式的胜利。如果自己从 
头写OS的话,我认为可以摆脱这种低技术成分的束缚,完全去实现一 
种崭新的,支持新技术的操作系统。 

但是,要写一个完整的操作系统显然不是那么容易的: 
1。Linux从一开始显然不是像现在这样复杂的,但是毕竟Linus使它跑 
起来了,这就为后续的逐渐完善与发展打下了基础。这一步显然很重要, 
在目前的国内,我还没有听说有这样的操作系统。 
2。如果说有一小部分人首先写了一个能跑的操作系统,那么必须在一 
开始就考虑一些用于实现新的OS理论的安排。不然的话,按照Linux 
那种开发模式进行下去,充其量成为“Linux第二“,这显然就没有意思了。 
3。如果摆脱了Linux,也就失去了Linux的软件框架,在这种情况下, 
所有软件工程方面的问题就需要更多的考虑了。不然,一个稳定的OS 
是不可能产生的。 
4。Linux在目前的小小成功具有更多其他的因素。开发一个完全崭新的 
操作系统不能保证它一定能够获得支持,而那些完全依靠个人兴趣编写 
程序的Programmer如果不能得到一定的肯定的话,我想有很多人是会 
退出的。 

目前写一个崭新系统的想法还不成熟,我认为。 

--ober 

-------------------------------------------------------------------------------- 

与此相关的帖子 
3/99 16:51 
Re: 我们有必要开发一个新的操作系统吗? (1216字节) 灵溪 4/23/99 20:29 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: Re: lxr.linux.no(很好的源码学习站点) 
作 者: 老铁 
日 期: 4/23/99 22:10 
论 坛: BJLC 源码论坛 
回 复: lxr.linux.no (老铁) 
内 容: 
This page was automatically generated by the LXR engine. (好东西) 
Cross-Referencing Linux 
Linux/ [ source navigation ] 
[ identifier search ] 
[ freetext search ] 
[ file search ] 

Version: [ 1.0.9 ] [ 1.2.13 ] [ 2.0.35 ] [ 2.1.130 ] [ 2.1.131 ] [ 2.1.132 ] [ 2.2.0 ] [ 2.2.1 ] [ 2.2.2 ] [ 2.2.4 ] [ 2.2.5 ] [ 2.2.6 ](牛) 

Architecture: [ i386 ] [ alpha ] [ m68k ] [ mips ] [ ppc ] [ sparc ] [ sparc64 ] 

推荐:Go! Lisoleg 

-------------------------------------------------------------------------------- 

与此相关的帖子 

lxr.linux.no (5512字节) 老铁 4/23/99 22:05 
Re: lxr.linux.no(很好的源码学习站点) (391字节) 老铁 4/23/99 22:10 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: 基于知识库的源代码 
作 者: daydream 
日 期: 4/23/99 12:28 
论 坛: BJLC 源码论坛 
内 容: 
我在很久以前研究过minix的源代码,后来又看了一些linux的源代码,主要是文件系统部分,因为我最初的目的是将linux的文件系统driver从操作系统中分离出来,后来看到美国的oskit工程,很受启发,oskit使用Glue的概念将不同的软件资源组合成一个整体。 
但是,所有的这些简单化的努力当发展到真正具有一点使用价值的时候,其复杂性也就必然操出了个人所容易理解的范围,特别在unix和C语言的世界,复杂的宏和各种编译开关、可移植性代码等等使得源代码的理解极为困难。 
在我的主页中,我提出了多视图软件系统的概念,也许对这个问题有一些帮助,简单来说,软件系统包括很多视图,面向开发、硬件、分布、语言、用户等,如何从源代码中提取这些信息,如何组织这些信息是一个很有意思的工作。 
对于linux的源代码,我有这样一个设想: 
采用一种统一的体系结构来表达现有的源代码,分离出其中的算法核心、面向硬件的接口、面向策略的接口, 
采用一种树状,结构化,多视图的方法来表达和分析这些知识, 
基本的工作也许包括一个面向对象的知识库核心,一个从源代码抽取知识的自动工具 
某种抽取方法描述的脚本语言(也许可以用Java?) 
上面是一些很不成熟的想法,希望指正。 

99-4-23 

-------------------------------------------------------------------------------- 

与此相关的帖子 

基于知识库的源代码 (984字节) daydream 4/23/99 12:28 
Re: 基于知识库的源代码 (50字节) netcard 4/23/99 21:31 
Re: 基于知识库的源代码 (70字节) 灵溪 4/24/99 08:24 
Re: 基于知识库的源代码 (6188字节) daydream 4/24/99 12:27 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: Re: 基于知识库的源代码 
作 者: daydream 
日 期: 4/24/99 12:27 
论 坛: BJLC 源码论坛 
回 复: Re: 基于知识库的源代码 (灵溪) 
内 容: 
下面是一个简单的多视图语言的概述 
软件系统的视图和新型软件开发语言 

1软件系统的视图 

计算机软件系统不仅仅是源代码,可执行程序和其他的数据文件,根据与软件系统的所有相关成分的不同观点,软件系统可以看成下面的七个不同视图的统一. 

首先,我们使用面向对象的观点来考察软件系统,软件系统的所有部分都可以看成相互存在关系的对象(包括所有的数据和代码)的集合. 

我们使用下面的体系来表示软件系统的不同视图: 

(图略) 
我们对各个部分分别进行说明: 

软件系统本身: 

是系统的抽象模型,包括对象的组成结构,数据结构,状态转换图等,采用OOA方法,我们将其分为两个视图: 
对象行为模型 --- 对象的行为,表现为对象在外界刺激下产生的状态转换图 
对象交互模型 --- 对象之间的交互,消息的传送机制 

注意,在这一部分中,不出现类的概念,所有的分析目标都是对象。 

面向开发人员的观点 

基本上是软件系统的对象概念模型,包括类的体系,继承关系和对象的组合关系。 
系统分析人员使用高层次的描述语言,对系统的结构,体系框架对系统进行的描述,对应于传统开发方法中的系统设计(概要和详细设计中与语言无关的部分)部分. 

面向用户的观点 
系统的需求分析,包括用户的界面描述和面向用户的,以用户的操作模型为中心的任务描述 

面向支持环境的观点 
系统的运行环境,包括操作系统,数据库系统两个最主要的部分和其他需要提供和特别指明的支持环境(例如MFC需要的DLL,JAVA需要的Java Runtime等) 

面向语言的观点 
程序开发所使用的语言考虑,直接包括源代码和系统设计中与选择的开发语言相关的部分(例如,由于语言对递归的支持程度而在设计中采用的堆栈方法) 

面向硬件的观点 
软件系统运行的目标平台,包括关于CPU的假设(字节顺序,字长,寄存器的数量,寻址方式等),在常规程序设计中,如果采用可移植的高级语言,则这个观点被编译系统所隐藏(当然,字节顺序和字长常常是我们必须考虑的内容!) 

面向分布环境的观点 
在简单的单机应用中,不需要考虑这个视图,但是对于网络应用,必须考虑系统和其他分布对象之间的交互,例如,与网络通讯相关的应用必须考虑TCP/IP的接口,与邮件相关的应用必须考虑SMTP,POP的协议接口,与网络管理相关的应用必须考虑与SMTP的协议接口… 

面向调试的观点 
调试是软件开发的一个非常重要的内容,在调试的过程中,要求软件系统作为一个白箱完全暴露在调试人员的视野中,在调试中所需要的对象信息和执行信息组成系统的调试视图。 

2视图的统一表示 
在传统程序设计方法中,系统设计人员面对的是自然语言描述的用户需求,以及用户对界面的模糊概念,系统分析人员将自然语言的需求变成相对较为规范的系统高层描述,然后根据对目标系统的假设作出系统设计. 

程序设计人员面对系统分析人员提供的分析和设计文档,结合编程语言和目标系统硬件的假设,进行详细设计和编码,而测试人员将面对所有这些文档,源代码甚至还有目标代码,尝试找到所有的错误.维护人员在增加新的功能时,将重复上面所述的全部工作. 

在整个过程中,自然语言,分析语言,设计语言,数据的描述语言,状态图,等不同形式,不同结构的语言反复使用和转换.这就极大的提高了开发成本. 

同时对于用户的一个简单要求,例如 

“求工资总额不包括退休金”,在需求,系统设计,详细设计和源代码中都存在一个表示,如果在更改这个要求时,在软件系统的文档的任何部分存在不一致的地方,不可预见的错误将在将来的某个时候发生. 

造成这种错误的根源是在软件系统的所有组成部分中,在不同的成分中存在对同样事实的表述,而这些所有的表述之间的一致性是无法自动保证的,开发人员对于软件系统任何部分的改动(源代码,设计,数据关系图或者是需求分析),都必须同时对软件系统中所有相关的部分进行,而这一点往往?
潜缓雎缘?而在实际工作中也是难以作到的. 

为解决这些表述之间一致性,我们可以有两种方案,一种是开发大型的集成开发工具,通过对系统分析,设计和源代码生成的支持,实现表述之间的自动一致性保证,这种方案可以使用现有的大量软件资源和传统的开发方法和工具,具有很大的实用价值.商用的CASE工具就是这种方案的实现,这种?
桨傅母丛有蕴?从简化问题出发,我们考虑另外一种解决方案:即设计一种新型的软件开发语言来表述上面的所有视图,使整个软件系统具有统一的表述方法,通过对这种语言的不同使用,满足不同视图的不同观点. 

  

  

  

这种语言应该具有下面的几个特征. 

统一的面向对象体系,要实现所有视图的统一表述,我们必须采用一个统一的模型来描述语言的结构.鉴于面向对象体系所具有的良好的结构特征和强大的可扩充性,以及现有用户对面向对象技术的使用程度,我们选择面向对象体系来建立这种语言 

可以解释执行,虚拟执行,一般而言,”执行”是一个使用于目标代码的概念,但是鉴于这种语言适用于分析,设计和编码等不同的层次,所以我们要求语言的解释执行可以在任何层次和开发的阶段下进行. 

类和对象的层次化,多视图语义,传统语言中,语义的视图只有一个,即面向编译器的视图.由于这种语言的多视图特性,所以其语义必须具有多视图性.视图包括public-公共视图requirement-需求视图, design – 设计视图 protocol –协议视图 - 

  

  

3语言的对象体系 

一般的继承,聚集的类构造方法都可以被支持, 

几个新的特性加入语言中 

非形式化的定义,主要用于需求和高层设计中,允许用户”执行”需求分析,从而在需求分析阶段就发现错误. 

递归的数学化的类定义,例如,二叉树结构类可以定义为 

class Tree = Data + [Tree Left] + [Tree Right] 递归的定义了一个二叉树 

取消指针和标号等语言成分 

虚拟类和虚拟类的继承(即C++中的模板类) 

强类型设施 

变量是否可以取消? 

显式并行 

名字空? 

4 开发环境 

以知识库为中心的开发环境。 

考虑到语言本身的局限性,我们可以考虑不规定一种语言文本来表达所有的视图, 

相反,采用一种类似于知识库的形式,将所有的信息以相互关联的知识的形式存储在知识库中,可以输入或者输出某一个视图的模型, 

是否可以考虑将所有的开发成分变成知识库? 

例如,可以输入知识库一个CPU对象,该CPU对象包括两个层次,A.抽象的CPU对象,适用于所有的CPU。B.具体的CPU对象,适用于某一种具体的CPU,于是,编译的过程就变成将知识库中的具体内容表达为CPU对象的操作。 

如果该知识库可以自我学习和扩充,例如,CPU对象的方法可以增加,CPU的方法可以实现,CPU对象可以查看,则可以实现虚拟执行,这样,绝大多数代码可以虚拟执行,从而提高可靠性。对象方法的虚拟执行,方法可以作用在对象的属性上,当然,最终可以生成二进制的代码。 

对象的查看,对象的编辑和修改,包括对象的冻结,也就是生成可执行的代码。 

可以首先实现这样一个CPU类,抽象或者具体。 

可以首先实现一个简单的对象管理环境,包括对象的增加、删除、编辑,对象方法的编辑,对象的查看和其他基本的功能。 

包括对象的存储,激活和恢复功能,持续化对象的实现。 

在文本状态下实现,这样有助于开发新的操作系统。 

是否可以提供通过串行口的远程调试环境。 

自我学习的编译系统! 

减少对象的类层次,太多的层次服务理解。 

5实际的考虑 

面向虚拟机的编译和解释执行,软件系统中的可执行部分可以直接翻译成虚拟机的代码进行解释执行,虚拟机的代码可以是直接的表达式求值,也可以是某个对象的非形式化的状态转换. 

  

5虚拟机的实现 

对该语言的虚拟机要求: 

同时支持高层次和低层次的虚拟机执行 

容易直接转换为目标代码 

因此我们选择面向堆栈的虚拟机体系结构 

  

6怎样从现有的源代码中分析出所有的信息,重建系统的流程和对象之间的结构图,包括C代码的隐含语义,怎样从汇编、C代码中获取关于如何建造系统,如何完成某一项任务的全部知识? 

从简单语句中抽取信息 

从循环中抽取信息 

从结构中抽取信息。 

从宏定义中获取信息。 

从与外部相关的接口中获取信息。 

这种分析环境必须是可以扩充的,可以从实践中获得知识的 

-------------------------------------------------------------------------------- 

与此相关的帖子 

基于知识库的源代码 (984字节) daydream 4/23/99 12:28 
Re: 基于知识库的源代码 (50字节) netcard 4/23/99 21:31 
Re: 基于知识库的源代码 (70字节) 灵溪 4/24/99 08:24 
Re: 基于知识库的源代码 (6188字节) daydream 4/24/99 12:27 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: GNU CC 2.7.2解剖和移植(1) 
作 者: 老铁 
日 期: 4/24/99 22:24 
论 坛: BJLC 源码论坛 
内 容: 
第一部分 

使用GNU CC 

这一部分介绍如何运行,安装GNU C编译器,描述此编译器的新功能,它与标准C的非兼容部分,给出使用此编译器涉及的各种控制选项,提示当该编译器中存在的错误时应如何向GNU报告. 
"GNU CC"是GNU C Compiler的全称,通常它指的是整个GNU C编译系统并且特别指的是编译系统中与语言无关的部分. 
GNU C 编译器中集中了C,C++和Objective-C三个编译版本,该编译器可以编译用C,C++和Objective-C书写的源程序. 
GNU C编译器简称"GCC".GCC是本编译器的通用名字,也是在强调编译C程序时用的名字.当强调编译的是C++程序时,通常称本编译器为"G++".因为实际上只有一个编译器,因此无论所编译的源程序是上述三种语言中的哪一种书写的,都可称本编译器为"GCC". 
G++是一个编译器而不仅仅只是一个预处理器,G++直接由用户的C++源程序建立目标代码而不是将C++源程序改写成此程序的中间C版本.避免中间C版本意味着C++程序能具有更好的目标代码和更好的调试信息.GNU调试器GDB可接收此目标代码中的有关信息并提供给C++源程序级别的调试功能. 
其他语言(如Ada9x,FORTRAN,Modula-3,PASCAL)的前端目前正在开发中.这些前端类似于C++的前端,也建立在GNU CC的子目录中并与之有链接关系.最终结果是形成一个集成化的编译系统,它能够编译用C,C++和Objective-C编写的程序和其他任何已安装了其前端的语言编写的程序. 
我们将只介绍C,C++和Objective-C编译的各种选项. 
GNU CC命令选项 
当运行GNU CC时,其过程通常包括预处理(cpp),编译(cc1),汇编(as)和链接(ld)等步骤."全局选项"允许你停止此过程于某一步骤.例如:'-c'选项指出不要进行链接步骤.于是运行GNU CC产生的输出文件则为汇编步骤产生的目标文件. 
另外一些选项则被传送给上述过程中的某一步骤,一些选项用于控制预处理器而另一些选项则控制编译器本身,还有一些选项则用于控制汇编和链接.这些汇编和链接选项中的大部分都未列在本手册中,因为一般几乎不需要用到它们. 
GNU CC的大部分命令选项对C程序都是可用的,当一个选项仅仅只能用于某一种语言(通常为C++)时,本手册将明显地指出.若对一个具体选项的描述没有指明适应的源语言,则此选项可用于所支持的所有语言. 
Gcc程序将所有的选项和文件名统统作为参数接收,许多选项具有多字母名字,因此,多个单字母选项不能归并为一个选项,例如:'-dr'与'-d -r'是完全不同的形式. 
可以将各种选项和其他参数混合在一起使用,大部分情况下,其顺序并无影响,但当同时使用若干个相同类型的选项时,其出现的顺序先后则有关系.例如:当指定'-L'选项多次时,将按指定的顺序查询各个目录. 
许多选项是以'-f'和'-W'打头的长名字选项,例如:'-fforce-mem','-fsrength-reduce','-Wformat'等等.这类选项大部分都有正反两种形式,例如:'-ffoo'的相反形式是'-fno-foo'.本手册只描述两种形式中之一,即非缺省的那个选项形式. 
选项一览表 

Overall Options(全局选项) 

-c -S -E -o file -pipe -v -x language 

Language Options(C语言选项) 

-ansi -fallow-single-precision -fcond-mismatch 

-fno-asm -fno-builtin -fsigned-bitfields -fsigned-char 

-funsigned-bitfields -funsigned-char -fwritable-strings 

-traditional -traditional-cpp -trigraphs 

C++语言选项 

-fall-virtual -fdollars-in-identifiers -felide-constructors 

-fenum-int-equiv -fexternal-templates -ffor-scope 

-fno-for-scope -fhandle-signatures 

-fmemoize-lookups -fno-default-inline -fno-gnu-keywords 

-fnonull-objects -foperator-names -fstrict-prototype 

-fthis-is-variable -nostdinc++ -traditional +en 

Warning Options(警告信息选项) 

-fsyntax-only -pedantic -pedantic-errors -w -W -Wall 

-Waggregate-return _Wbad-function-cast -Wcast-align -Wcast-qual 

-Wchar-subscript -Wcomment -Wconversion -Wenum-clash 

-Werror -Wformat -Wid-clash-len -Wimplicit -Wimport -Winline 

-Wimplicit-int -Wimplicit-function-declaration -Winline -Wlarger-than-len 

-Wmain -Wmissing-prototypes -Wmissing-declarations 

-Wnested-externs -Wno-import -Woverloaded-virtual -Wparentheses 

-Wpointer-arith -Wredundant-decls -Wreorder -Wreturn-type 

-Wshadow -Wstrict-prototypes -Wswitch -Wsynth 

-Wtemplate-debugging -Wtraditional -Wtrigraphs 

-Wuninitialized -Wunused -Wwrite-strings 

Debugging Options(调试选项) 

-a -dletters -fpretend-float -g -glevel -gcoff -gxcoff 

-gxcoff+ -gdwarf -gdwarf+ -gstabs -gstabs+ -ggdb -p -pg 

-save-temps -print-file-name=library -print-search-dirs 

-print-libgcc-file-name -print-prog-name=program 

Optimization Options(优化选项) 

-fcaller-saves -fcse-follow-jumps -fcse-skip-blocks 

-fdelayed-branch -fexpensive-optimizations -ffast-math -ffloat-store 

-fforce-addr -fforce-mem -finline-functions -fkeep-inline-functions 

-fno-default-inline -fno-defer-pop -fno-function-cse 

-fno-inline -fno-peephole -fomit-frame-pointer 

-frerun-cse-after-loop -fschedule-insns 

-fschedule-insns2 -fstrength-reduce -fthread-jumps 

-funroll-all-loops -funroll-loops -O -O0 -O1 -O2 -O3 

Preprocessor Options(预处理选项) 

-Aassertion(answer) -C -dD -dM -dN -Dmacro[=defn] -E -H 

-idirafter dir -include file -imacros file -iprefix prefix 

-iwithprefix dir -iwithprefixbefore dir -isystem dir -M -MD 

-MM -MMD -MG -nostdinc -P -trigraphs -undef 

-Umacro -Wp,option 

Assembler Option(汇编选项) 

-Wa,option 

Linker Options(链接选项) 

Object-file-name -llibrary -nostartfiles -nodefaultlibs -nostdlib-s 

-static -shared -symbolic -Xlinker option -Wl,option -u symbol 

Directory Options(目录选项) 

-Bprefix -Idir -I- -Ldir 

Target Options(目标机和版本选项) 

-b machine -V version 

Configuration Dependent Options(机器相关选项) 

M680x0 Options(M680x0选项) 

-m68000 -m68020 -m68020-40 -m68030 -m68040 -m68881 

-mbitfield -mc68000 -mc68020 -mfpa -mnobitfield -mrtd 

-mshort -msoft-float 

VAX Options(VAX选项) 

-mg -mgnu -munix 

SPARC Options(SPARC选项) 

-mepilogue -mfpu -mhard-float -mno-fpu -mno-epilogue 

-msoft-float -msparclite -mv8 -msupersparc -mcypress 

Convex Options(Convex选项) 

-margcount -mc1 -mc2 -mnoargcount 

AMD29K Options(AMD29K选项) 

-m29000 -m29050 -mbw -mdw -mkernel-registers -mlarge 

-mnbw -mnodw -msmall -mstack-check -muser-registers 

M88K Options(M88K选项) 

-m88000 -m88100 -m88110 -mbig-pic -mcheck-zero-division 

-mhandle-large-shift -midentify-revision 

-mno-check-zero-division -mno-ocs-debug-info 

-mno-ocs-frame-position -mno-optimize-arg-area 

-mno-serialize-volatile -mno-underscores 

-mocs-debug-info -mocs-frame-position 

-moptimize-arg-area -mserialize-volatile 

-mshort-data-num -msvr3 -msvr4 -mtrap-large-shift 

-muse-div-instruction -mversion-03.00 

-mwarn-passed-structs 

RS6000 Options(RS6000和PowerPC选项) 

-mfp-in-toc -mno-fop-in-toc 

RT Options(RT选项) 

-mcall-lib-mul -mfp-arg-in-fpregs -mfp-arg-in-gregs 

-mfull-fp-blocks -mhc-struct-return -min-line-mul 

-mminimum-fp-blocks -mnohc-struct-return 

MIPS Options(MIPS选项) 

-mabicalls -mcpu=cpu type -mips2 -mips3 -mint64 -mlong64 

-mlonglong128 -mmips-as -mgas -mrnames -mno-rnames 

-mgpopt -mno-gpopt -mstats -mno-stats -mmemcpy 

-mno-memcpy -mno-mips-tfile -mmips-tfile -msoft-float 

-mhard-float -mabicalls -mno-abicalls -mhalf-pic 

-mno-half-pic -G num -nocpp 

i386 Options(i386选项) 

-m486 -m386 -mieee-fp -mno-fancy-math-387 

-msoft-float -mno-fp-ret-in-387 -msvr3-shlib 

HPPA Options(HPPA选项) 

-mpa-risc-1-0 -mpa-risc-1-1 -mkernel -mshared-libs 

-mno-shared-libs -mlong-calls -mdisable-fpregs 

-mdisable-indexing -mtrailing-colon 

i960 Options(Intel 960选项) 

-mcpu type -mnumerics -msoft-float -mleaf-procedures 

-mno-leaf-procedures -mtail-call -mno-tail-call 

-mcomplex-addr -mno-complex-addr -mcode-align 

-mno-code-align -mic-compat -mic2.0-compat 

-mic3.0-compat -masm-compat -mintel-asm -mstrict-align 

-mno-strict-align -mold-align -mno-old-align 

DEC Alpha Options(DEC Alpha选项) 

-mfp-regs -mno-fp-regs -mno-soft-float -msoft-float 

System V Options(SYS V选项) 

-Qy -Qn -Qn -YP,paths -Ym,dir 

Clipper选项 

-mc300 -mc400 

H8/300选项 

-mrelax -mh 

ARM选项 

-mapcs -m2 -m3 -m6 -mbsd -mxopen -mno-symrename 

Code Generation Options(代码生成选项) 

-fcall-saved-reg -fcall-used-reg -ffixed-reg 

-finhibit-size-directive -fnonnull-objects -fno-common 

-fno-ident -fno-gnu-linker -fpcc-struct-return -fpic 

-fPIC -freg-struct-return -fshared-data -fshort-enums 

-fshort-double -fvolatile -fvolatile-global 

-fverbose-asm 

选项说明 

全局选项 

编译过程按顺序一般有预处理,编译,汇编和链接四个步骤.前三个步骤作用于各类特定的源文件,并最终生成目标文件;最后一个步骤则将所有目标文件(那些由编译新生成的目标文件以及那些作为输入文件指定的目标文件)连接装配成一个可执行文件. 
对给定的输入文件,文件名后缀决定了源文件的类型以及编译过程必须经历的步骤: 
file.c C源代码,必须经过预处理 
file.i C源代码,不需经过预处理 
file.ii C++源代码,不需经过预处理 
file.m Objective-C源代码,注意,为了运行objective-C程序,必须连接'libobjc.a'库 
file.h 未经过编译或连接的C头文件 
file.cxx 
file.cpp 
file.C C++源代码,必须经过预处理,注意,'cxx'中的'x'均是字母x.'.C'中的C为大写字母C. 
File.s 汇编源代码,不经过预处理和编译 
File.S 汇编源代码, 必须预处理 
Other 直接进行链接的目标文件,任何非上述后缀的文件名都被认为属于此类目标文件 
可以用'-x'选项明确地指出输入文件是何种语言 

-x language 

指明输入文件的语言(而不是由编译根据文件名后缀确定其语言),这一选项 

作用于其后的所有输入文件直至遇到下一个'-x'选项为止 

language可选的值为: 

c objective-c c++ c-header cpp-output assembler assembler-with-cpp 

-x none 

关闭已指明的语言,使其后出现的文件根据文件名后缀确定其语言 (就好象 

`-x' 从未使用过一样). 

如果只需进行编译过程中的某一个或某几个步骤,可以使用'-x'选项(或文件名 

后缀原则)告知gcc从哪一个步骤开始编译,而'-c','-S'或'-E'选项则用来告知gcc 

停止于哪一个步骤之后.注意,对某些组合(如 '-x cpp-output -E '),gcc将不做 

任何事情. 

-c 对源文件进行编译或汇编,但不链接.仅仅不进行链接步骤,最后的输出文件类 

型是每个源文件的目标文件.每一种源文件的缺省目标文件名是用后缀 

'.o'代替源文件名的后缀".c",".i",".s".忽略不经过编译或汇编的不可识别的输 

入文件. 

-S 停止于编译步骤之后,不进行汇编步骤,最后的输出文件类型为汇编源代码文 

件. 

-E 停止于预处理之后, 不进行编译步骤,最后的输出文件类型为经过预处理之 

后的源代码文件.此文件被输出至标准输出文件. 

-o file 放置输出文件file中,它适用于所有类型的输出文件,即,可以是可执行文件, 

目标文件,汇编文件或经预处理之后的源代码文件. 

因为'-o'选项只能指明一个文件,所以,使用'-o'选项对于编译多个输入文件的 

情形将无意义,除非你要生成的输出文件是可执行文件,若未指明'-o',缺省约 

定是:将可执行文件放置在a.out中;若输入源文件为source.suffix,则目标文 

件放置在source.o中,汇编文件放置在source.s中;而所有预处理后的C源 

代码则输出至标准输出文件 

-v 显示运行编译过程所执行的各个编译步骤命令,同时显示编译驱绦?预处 

理器以及编译器的版本号. 

-pipe 在编译各步骤之间用管道而不是用临时文件进行通讯,这一选项在那些其编 

译器不能从管道输入的系统中无效,GNU汇编无此问题. 

推荐:Go! Lisoleg 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆回复此帖 ◆阅读前一帖 
主 题: GNU CC 支持多平台的实现技术 
作 者: 老铁 
日 期: 4/24/99 22:27 
论 坛: BJLC 源码论坛 
内 容: 
支持多平台的实现技术 
GNU CC编译程序是一个支持多种平台的编译系统,它所支持的处理机约有30多种,如VAX系列,convex c1,c2,sparc,i386,i486,MIPS系列,intel960,RS/6000,Alpha等等.GNU CC之所以能支持如此多的平台,是由于它成功地采用了一种将机器相关部分从编译中分离出来的独特方法.GNU 
CC的这种编译方法适应了自由软件覆盖大部分处理机平台的设计宗旨,在计算机更新换代如此迅速的今天,这种多平台的编译技术越发显得重要.它可以减少大量的重复劳动,缩短编译程序的开发期.GNU CC所采用的支持多平台的思想及其实现方法独具特色. 
GNU CC编译系统主要由三部分组成:与语言相关的前端,与语言无关的后端以及与机器相关的机器描述.GNU 
CC的前端由与源语言关系密切的语法分析,语义分析组成.它对源语言进行分析后将其转换为语法树.它的后端为编译系统的主体部分,它将语法树转换成中间语言,在此中间语言基础上进行各种优化并完成代码生成,机器描述部分则描述GNU 
CC所运行的宿主机和目标机的操作系统,体系结构,指令系统等有关系统与硬件的情况并提供给编译程序的后端使用.这样采用不同的体系机器描述,将得到适合于在不同机器与系统上运行的GNU CC版本. 
不同的处理器之间和不同的操作系统之间存在着相当大的差别,对于支持多平台的编译程序而言,面对这众多不同的系统与体系结构它必须解决三个根本的问题:(1)需要设计一种对目标机恰当的描述,这种描述既要能描述它们各自的特性,同时还要适合于编译程序的处理;(2)需要设计出一种较
好的中间语言,这种中间语言应当在适当的层次上,向上能支撑多语言的映射,向下能适应多平台转换并且适合于进行各种优化;(3)在机器描述与编译主体之间需要仔细地设计一种统一接口.GNU CC编译系统的独特设计解决了这三个问题. 
1. GNU CC的中间语言 
GNU CC的中间语言称为寄存器传递语言(Register Transfer Language),简称为RTL. 
RTL的语法结构类似于LISP语言的表达式结构.这种结构实际上表示的是一种树结构.利用这种类LISP的表示,RTL设计得十分简洁,灵活,它仅有115种操作码,其中真正用于编译内部的只有91种,另外的24种则只出现在机器描述中. 
RTL的基本元素是称为rtx的表达式.每个rtx都具有统一的内部数据结构与外部语法形式,这里,我们只涉及外部语法形式,rtx表达式的外部语法一般形式为: 
(code: m opn1 opn2 ... )其中, 
code-rtx操作码.该操作码指明rtx表示的操作类型,如表示一条rtx指令,进行某种算术运算,引用某个符号名或寄存器,表示某种说明等等.除此之外,code还确定rtx的操作个数和这些操作数的种类 
m-机器方式,机器方式表示数据和运算结果的类型,它反映了数据类型与字长两部分信息,数据类型分为整型,浮点和复型三种.机器字长则分8位,16位,32位,64位,双64位等,这两部分信息有条件组合所构成的机器方式反映了机器所能表示的各种数据类型.此外,GNU 
CC还提供了void方式,条件结果方式和块方式.并不是每一种rtx表达式都必须有机器方式,当不提供机器方式时,意味着其机器方式为void. 
Opn-操作数.每个rtx表达式的操作数个数以及操作数种类是各不相同的,这取决于rtx操作码,gcc编译内部有一套表格,由rtx的code可直接从这些表格中获取自身操作数个数以及各操作数所表示对象的种类.rtx的操作数可表示五种对象:一般整数,宽整数,字符串,rtx表达式和由指向rtx的指针
组成的向量. 
如(insn 12 11 13 (set (mem:SF (plus:S1 (reg::S! 58) (const_int 32))) (reg:SF 71))) 
它表示了一条将寄存器之值送至内存的指令,其中,操作码insn指出这是一条表示指令的rtx,它的第0,第1,第2操作数均为整数,分别表示该指令的编号,前一条指令的编号与后一条指令的编号;它的第三操作数为一rtx表达式,表示该指令的动作;这里省略了它的第四以后的操作数.第三操作数中
,set,mem,plus分别表示赋值,存储器引用和加法运算,它们的操作数均为rtx表达式;reg与const-int分别表示寄存器引用和整常数引用,它们的操作数分别为一般整型和宽整型 
... 

推荐:Go! Lisoleg 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆回复此帖 ◆阅读前一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 
主 题: Is Linux's BUG ??? 
作 者: walker 
日 期: 4/23/99 20:33 
论 坛: BJLC 源码论坛 
内 容: 
关于fork,我在Linux上做了一个简单测试,Source如下, 
当我"Press a key to exit Child Process"后, 
用ps aux | grep testfork 发现子进程还在, 
变成了 ---"僵尸", 除了占了进程号外不占用 
系统其它资源; 同样的程序在SUN OS上编译执行, 
没有出现这种情况. 这难道是Linux 的 BUG ?? 请问有什 
么办法解决这个问题?否则大家不敢在Linux上用fork了。 

/* testfork.c */ 
#include 
main() 

int childpid; 

for(;;) 


getchar(); 

printf("Have Created a new Child!\n"); 

if((childpid = fork()) 
日 期: 4/23/99 21:36 
论 坛: BJLC 源码论坛 
回 复: Is Linux's BUG ??? (walker) 
内 容: 
Maybe u can call it a bug 
When exit() complete, the process is in the zombie state.exit does not free the proc structure of the parent,because its parent may want to retrieve the exit status and resource usage information.The parent is responsible for freeing the child's proc 
structure. 
在父进程处应调用wait(0)to retrievethe information,which also frees the proc structure.If the parent dies before the child,the init process inherits the child. 
A problem may arise if a process dies before its parent,and the parent does not call wait.The child's proc structure is never released, and the child remains in the zombie state until the system is rebooted.This situation is rare,since the shells are 
written carefully to avoid this problem. It may happen,however,if a carelessly written application does not wait for all child processes. This is an annoyance,because such zombies are visible in the output of ps(and users are vexed to find that they 
cannot be killed-they are already dead).Furthermore, they use up a proc structure,thereby reducing the maximum number of processes that can be active. 
some newer UNIX variants allow a process to specify that it will not wait for its children.For instance, in SVR4, a process may specify the SA_NOCLDWAIT flag to the sigaction system call to specify the action for SIGCHLD signals. this asks the kernel 
not to create zombies when the caller's children terminate. 

推荐:Go! Lisoleg 

-------------------------------------------------------------------------------- 

与此相关的帖子 

Is Linux's BUG ??? (779字节) walker 4/23/99 20:33 
Re: Is Linux's BUG ??? (1401字节) 老铁 4/23/99 21:36 

-------------------------------------------------------------------------------- 
回复此帖 
标题:* 

笔名: * 推荐链接: 
密码: * 链接标题: 
Email: * 图象链接: 

注意:带"*"的必须填写.笔名首次使用,密码自动登记. 

◆看相关话题 ◆回复此帖 ◆阅读前一帖 ◆阅读后一帖 

BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3. 

Welcome to http://lisoleg.yeah.net 

-------------------------------------------------------------------------------- 

Show mom you love her. Check out our great Mother's Day Gifts! 14K Gold and gemstone jewelry, leather and cloth wallets and purses, gardening, gourmet, kitchen, more! Free Shipping in the US! http://clickhere.egroups.com/click/142 
eGroup home: http://www.eGroups.com/group/lisoleg 
www.eGroups.com - Simplifying group communications 


推荐:Go! Lisoleg 


--------------------------------------------------------------------------------
回复此帖 
标题:* 

笔名: * 推荐链接:  
密码: * 链接标题:  
Email: * 图象链接:  


注意:带"*"的必须填写.笔名首次使用,密码自动登记.


◆回复此帖 ◆阅读前一帖 


BJLC 源码论坛 采用了至尚网络工作室制作的: 中文Web论坛 2.3.


--
m;36m※ 来源:.BBS 曙光站 bbs.ncic.ac.cn.[IP: 159.226.41.99]m
--
m;32m※ 转寄:.紫 丁 香 bbs.hit.edu.cn.[FROM: Baggio.hit.edu.c]m

我们有必要开发一个新的操作系统吗? (183字节) xuas 4/2

--
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: Baggio.hit.edu.c]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:636.034毫秒