乐乐破解全模式~__极速前进中国版模式~__全局自动~ 暴力上分~__经典模式~ __全局自动~

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
圈子列表加载中...
this.p={b:2,c:10};
日志分类列表加载中...
this.p={b:2};
{if defined('c')&&c.length>0} {list c as x}{/list} {else} 没有日志分类 {/if}
this.p={b:2,s:9,a:true};
{if !defined('ml')||!ml.length} 专辑 ${an|escape} 没有音乐! {else} {list ml as x}{/list} {/if}
{if !defined('ll')||!ll.length} 网易博客音乐盒 {else} {list ll as x}${x.v|default:'&'}{/list} {/if}
{if !defined('al')||!al.length} 没有专辑! {else} {list al as x}{/list} {/if}
我要留言 & &
& 留言列表加载中...
this.p={b:2,nv:false,cn:5,ct:5};
评论列表加载中...
this.p={b:2,bn:5,bt:5,pn:5,pt:5};
最后登录加载中...
this.p={b:2,ua:25, ub:'http://img.bimg.126.net/photo/FlgTeXrSJzeQH7SiKrVNhw==/6325012.jpg',us:'他', friendstatus:'none',followstatus:'unFollow',hmcon:'1',aShowT:'1',guideId:6};
积分 ${data.totalScore} 分,距离下一等级还有 ${data.nextGradeNeedScore}分
数据列表加载中...
数据列表加载中...
网易新闻资讯
this.p={b:2,c:'0|0,1,2,3,4,5;1|10,11,12,13,14,15;2|20,21,22,23,24,25;3|30,31,32,33,34,35;4|40,41,42,43,44,45;5|50;6|60,61,62,63,64;7|70,71,72,73,74;8|80,81,82,83,84,85;9|90,91,92,93,94,95,96;11|110;'};
{if !defined('nl')} 新闻资讯加载失败! {elseif !nl.length} 目前没有新闻! {else} {list nl as x} · {/list} {/if}
{list nl as x} {if index == x_index}
{/if} {/list}
#include&stdio.h&void main(){& long int f1,f2;&& f1=1;f2=1;& for(i=1;i&=20;i++)& {&& printf("%12ld %12ld",f1,f2);&& if(i%2==0)printf("\n");&& f1=f1+f2;&& f2=f2+f1; & } & getch(); & }
网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。 什么是Socket Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket类型有两种:流式Socket (SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。 Socket建立 为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。socket函数原型为:int socket(int domain, int type, int protocol); domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP协议族);type参数指定socket的类型: SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;protocol通常赋值 "0"。 Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上 "建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。 Socket配置 通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket。面向连接的socket客户端通过 调用Connect函数在socket数据结构中保存本地和远端信息。无连接socket的客户端和服务端以及面向连接socket的服务端通过调用 bind函数来配置本地信息。Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。Bind函数原型为:int bind(int sockfd,struct sockaddr *my_addr, int addrlen); Sockfd是调用socket函数返回的socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。struct sockaddr结构类型是用来保存socket信息的: struct sockaddr { unsigned short sa_ /* 地址族, AF_xxx */ char sa_data[14]; /* 14 字节的协议地址 */};sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该socket的IP地址和端口号。 另外还有一种结构类型: struct sockaddr_in { short int sin_ /* 地址族 */ unsigned short int sin_ /* 端口号 */ struct in_addr sin_ /* IP地址 */ unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ }; 这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向 sockaddr_in的指针转换为指向sockaddr的指针;或者相反。使用bind函数时,可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号: my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */ my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */ 通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序;而sin_addr则不需要转换。计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。下面是几个字节顺序转换函数: ·htonl():把32位值从主机字节序转换成网络字节序·htons():把16位值从主机字节序转换成网络字节序·ntohl():把32位值从网络字节序转换成主机字节序·ntohs():把16位值从网络字节序转换成主机字节序Bind()函数在成功被调用时返回0;出现错误时返回"-1"并将errno置为相应的错误号。需要注意的是,在调用bind函数时一般不要将端口号置为小于1024的值,因为1到1024是保留端口号,你可以选择大于1024中的任何一个没有被占用的端口号。 连接建立 面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接,其函数原型为:int connect(int sockfd, struct sockaddr *serv_addr,int addrlen); Sockfd 是socket函数返回的socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。 Connect函数在出现错误时返回-1,并且设置errno为相应的错误码。 进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到 打断口。Connect函数启动和远端主机的直接连接。只有面向连接的客户程序使用socket时才需要将此socket与远端主机相连。无连接协议从不建立直接连接。面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客户的请求。Listen函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。 int listen(int sockfd, int backlog); Sockfd 是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们(参考下文)。Backlog对队列中等待 服务的请求的数目进行了限制,大多数系统缺省值为20。如果一个服务请求到来时,输入队列已满,该socket将拒绝连接请求,客户将收到一个出错信息。当出现错误时listen函数返回-1,并置相应的errno错误码。accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。 int accept(int sockfd, void *addr, int *addrlen); sockfd是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某 台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。首先,当accept函数监视的 socket收到连接请求时,socket执行体将建立一个新的socket,执行体将这个新socket和请求连接进程的地址联系起来,收到服务请求的 初始socket仍可以继续在以前的 socket上监听,同时可以在新的socket描述符上进行数据传输操作。 数据传输 Send()和recv()这两个函数用于面向连接的socket上进行数据传输。Send()函数原型为: int send(int sockfd, const void *msg, int len, int flags); Sockfd是你想用来传输数据的socket描述符;msg是一个指向要发送数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0(关于该参数的用法可参照man手册)。Send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。在程序中应该将send()的返回值与欲发送的字节数进行比较。当send()返回值与len不匹配时,应该对这种情况进行处理。char *msg = "Hello!";int len, bytes_……len = strlen(msg);bytes_sent = send(sockfd, msg,len,0);……recv()函数原型为: int recv(int sockfd,void *buf,int len,unsigned int flags); Sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应的errno值。Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址。sendto()函数原型为:int sendto(int sockfd, const void *msg,int len,unsigned int flags,const structsockaddr *to, int tolen);该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。Recvfrom()函数原型为: int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr*from,int *fromlen);from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof(struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或 当出现错误时返回-1,并置相应的errno。如果你对数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。 结束传输 当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:close(sockfd);你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。int shutdown(int sockfd,int how); Sockfd是需要关闭的socket的描述符。参数 how允许为shutdown操作选择以下几种方式: ·0-------不允许继续接收数据 ·1-------不允许继续发送数据 ·2-------不允许继续发送和接收数据, ·均为允许则调用close () shutdown在操作成功时返回0,在出现错误时返回-1并置相应errno。 Socket编程实例 代码实例中的服务器通过socket连接向客户端发送字符串"Hello, you areconnected!"。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。该服务器软件代码如下: #include &stdio.h&#include &stdlib.h&#include &errno.h&#include &string.h&#include &sys/types.h&#include &netinet/in.h&#include &sys/socket.h&#include &sys/wait.h&#define SERVPORT 3333 /*服务器监听端口号 */#define BACKLOG 10 /* 最大同时连接请求数 */main(){int sockfd,client_ /*sock_fd:监听socket;client_fd:数据传输socket */ struct sockaddr_in my_ /* 本机地址信息 */ struct sockaddr_in remote_ /* 客户端地址信息 */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror( "socket创建出错!"); exit(1);} my_addr.sin_family=AF_INET; my_addr.sin_port=htons(SERVPORT); my_addr.sin_addr.s_addr = INADDR_ANY; bzero( &(my_addr.sin_zero),8);if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) ==-1) {perror( "bind出错!");exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror( "listen出错!");exit(1); } while(1) { sin_size = sizeof(struct sockaddr_in); if ((client_fd = accept(sockfd, (struct sockaddr *) &remote_addr,&sin_size)) == -1) {perror( "accept出错"); } printf( "received a connection from %s\n",inet_ntoa(remote_addr.sin_addr));if (!fork()) { /* 子进程代码段 */ if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)perror( "send出错!");close(client_fd); exit(0); } close(client_fd); } } }服务器的工作流程是这样的:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用 listen在相应的socket上监听,当accpet接收到一个连接服务请求时,将生成一个新的socket。服务器显示该客户机的IP地址,并通过 新的socket向客户端发送字符串 "Hello,you are connected!"。最后关闭该socket。代码实例中的fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。 客户端程序代码如下:#include&stdio.h&#include &stdlib.h&#include &errno.h&#include &string.h&#include &netdb.h&#include &sys/types.h&#include &netinet/in.h&#include &sys/socket.h&#define SERVPORT 3333#define MAXDATASIZE 100 /*每次最大数据传输量 */main(int argc, char *argv[]){int sockfd, char buf[MAXDATASIZE]; struct hostent * struct sockaddr_in serv_ if (argc & 2) {fprintf(stderr,"Please enter the server's hostname!\n");exit(1);}if((host=gethostbyname(argv[1]))==NULL) { herror("gethostbyname出错!");exit(1);}if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket创建出错!");exit(1);}serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(SERVPORT); serv_addr.sin_addr = *((struct in_addr *)host- &h_addr);bzero( &(serv_addr.sin_zero),8);if (connect(sockfd, (struct sockaddr *) &serv_addr, \sizeof(struct sockaddr)) == -1) { perror("connect出错!");exit(1);}if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) { perror("recv出错!");exit(1);}buf[recvbytes] = '\0'; printf( "Received: %s",buf);close(sockfd); }客户端程序首先通过服务器域名获得服务器的IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket。函数gethostbyname()是完成域名转换的。由于IP地址难以记忆和读写,所以为了方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。函数原型为:struct hostent *gethostbyname(const char *name); 函数返回为hosten的结构类型,它的定义如下: struct hostent { char *h_ /* 主机的官方域名 */ char **h_ /* 一个以NULL结尾的主机别名数组 */ int h_ /* 返回的地址类型,在Internet环境下为AF-INET */ int h_ /* 地址的字节长度 */ char **h_addr_ /* 一个以0结尾的数组,包含该主机的所有地址*/ }; #define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/ 当 gethostname()调用成功时,返回指向structhosten的指针,当调用失败时返回-1。当调用gethostbyname时,你不能使用perror()函数来输出错误信息,而应该使用herror()函数来输出。   无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远端机的地址。 阻塞和非阻塞 阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。当服务器运行到accept语句时,而没有客户连接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这种情况称为阻塞(blocking)。而非阻塞操作则可以立即完成。比如,如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接,否则就继续做其他事情,则可以通过将Socket设置为非阻塞方式来实现。非阻塞socket在没有客户在等待时就使accept调用立即返回。#include &unistd.h&#include &fcntl.h&…… sockfd = socket(AF_INET,SOCK_STREAM,0);fcntl(sockfd,F_SETFL,O_NONBLOCK);……通过设置socket为非阻塞方式,可以实现 "轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返 回,返回值为-1,并置errno值为EWOULDBLOCK。但是这种"轮询"会使CPU处于忙等待方式,从而降低性能,浪费系统资源。而调用 select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费 CPU开销。Select函数原型为:int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值 是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件 描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:FD_ZERO(fd_set *set)----清除一个文件描述符集; FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中; FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除; FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。 Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为:struct timeval { int tv_ /* seconds */ int tv_ /* microseconds */ }; POP3客户端实例 下面的代码实例基于POP3的客户协议,与邮件服务器连接并取回指定用户帐号的邮件。与邮件服务器交互的命令存储在字符串数组POPMessage中,程序通过一个do-while循环依次发送这些命令。#include&stdio.h&#include &stdlib.h&#include &errno.h&#include &string.h&#include &netdb.h&#include &sys/types.h&#include &netinet/in.h&#include &sys/socket.h&#define POP3SERVPORT 110#define MAXDATASIZE 4096 main(int argc, char *argv[]){struct hostent *struct sockaddr_in serv_char *POPMessage[]={"USER userid\r\n","PASS password\r\n","STAT\r\n","LIST\r\n","RETR 1\r\n","DELE 1\r\n","QUIT\r\n",NULL};int iLint iMsg=0;int iEnd=0;char buf[MAXDATASIZE]; if((host=gethostbyname("your.server"))==NULL) {perror("gethostbyname error");exit(1);}if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket error");exit(1);}serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(POP3SERVPORT);serv_addr.sin_addr = *((struct in_addr *)host-&h_addr);bzero(&(serv_addr.sin_zero),8);if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(structsockaddr))==-1){perror("connect error");exit(1);} do {send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);printf("have sent: %s",POPMessage[iMsg]); iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);iEnd+=iLbuf[iEnd]='\0';printf("received: %s,%d\n",buf,iMsg); iMsg++;} while (POPMessage[iMsg]); close(sockfd);}
以下是学习windows内存管理时整理的内容。一、基本概念地址空间 系统中所有可用的内存地址的集合称为地址空间。例如:4GB的内存地址空间就是0xxFFFFFFFF。物理内存 硬件系统中真实存在的存储空间称为物理内存,物理内存的访问是通过硬件系统总线进行的。但并不是所有的32位系统都具有4GB的物理地址空间,因此物理地址不一定是0xxFFFFFFFF,例如1GB的系统上物理地址就是0xx3FFFFFFF。虚拟地址空间 为了访问内存的统一和方便,32位操作系统允许其上运行程序访问所有的4GB内存空间中的地址,因此操作系统必须进行一些必要的地址转换工作,将程序访问的地址转换成物理内存中的真实地址。然后进行数据存取。操作系统进行转换后,供程序使用的地址空间称为虚拟地址空间。在32位系统上,可以使用的虚拟地址空间大小是4GB。由于虚拟地址空间可能比真实物理地址空间要大,系统会将部分虚拟地址空间中的地址转换为硬盘中的数据,必要的时候将物理内存中的数据与硬盘中的数据进行交换。这种地址转换和数据交换是通过分页和分段机制实现的。(稍后介绍)进程的内存空间:用户内存空间与内核空间。 windows操作系统中的每一个进程都有属于自己的虚拟地址空间。32位的windows操作系统将4GB的虚拟内存划分为两个部分,进程使用2GB,称为用户进程空间;内核使用2GB称为系统地址空间或内核地址空间(也可以设置用户进程空间为3GB,内核使用1GB。),用户空间的地址范围为0xx7FFFFFFF。内核空间的地址范围为0xxFFFFFFFF。虚拟地址空间在进程上是封闭的,进程只能访问属于自己的地址空间,如果要访问其他进程的地址空间需要特殊的机制。保护模式 保护模式是相对实模式而言的,他们是处理器的两种工作方式。dos就是运行在实模式下,大部分的现今 x86 操作系统 都在保护模式下运行,包含 Linux、FreeBSD、以及 微软 Windows 2.0 和之后版本。 实模式由于是由发展而来因此他更像是一个运行单片机的简单模式。计算机启动后首先进入的就是实模式,通过只有20根地址线所以它的寻址范围只有2的20次幂,即1M。内存的访问方式就是我们熟悉的seg:offset("选择器"+"偏移"即"逻辑地址"方式)。实模式在后续的cpu中被保留了下来,但实模式的局限性是很明显的,由于使用逻辑地址只能访问1M多一点的内存空间,在拥有32根地址线的cpu中访问1M以上的空间则变得很困难。而且随着计算机的不断发展实模式的工作方式越来越不能满足计算机对资源(存储资源和cpu 资源等等)的管理,由此产生了新的管理方式——保护模式。 在保护模式下,全部32根地址线有效,可寻址4G的物理地址空间;扩充的存储分段机制和可选的存储器分页机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持;支持多任务;4个特权级和完善的特权级检查机制,实现了数据的安全和保密。计算机启动后首先进入的就是实模式,通过设置相应的寄存器才能进入保护模式。二、分段与分页机制 存储方式主要体现在内存访问方式上,由于兼容和IA32框架的限制,保护模式在内存访问上延用了实模式下的seg:offset的形式(即:逻辑址),其实seg:offset的形式在保护模式下只是一个躯壳,内部的存储方式与实模式截然不同。在保护模式下逻辑地址并不是直接转换为物理地址,而是将逻辑地址首先转换为线性地址,再将线性地址转换为物理地址。如图一: 线性地址只是一个抽象的概念,并不是实际存在的。例如32位系统的线性地址就是0xxFFFFFFFF。 线性地址地址主要是为分页机制而产生的。 处理器在得到逻辑地址后首先通过分段机制转换为线性地址,线性地址再通过分页机制转换为物理地址最后读取数据。& & & &如图二:& 分段机制是必须的,分页机制是可选的,当不使用分页的时候线性地址将直接映射为物理地址。 设立分页机制的目的主要是为了实现虚拟存储。分段机制 逻辑地址转换为线性地址。 分段机制在保护模式中是不能被绕过得,回到我们的seg:offset地址结构,在保护模式中seg有个新名字叫做“段选择子”(seg.. selector)。段选择子、GDT、LDT构成了保护模式的存储结构,如图三`,GDT、LDT分别叫做全局描述符表和局部描述符表,描述符表是一个线性表(数组),表中存放的是描述符。& “描述符”是保护模式中的一个新概念,它是一个8字节的数据结构,它的作用主要是描述一个段,用描述表中记录的段基址加上逻辑地址(sel:offset)的offset转换成线性地址。描述符主要包括三部分:段基址(Base)、段限制(Limit)、段属性(Attr)。一个任务会涉及多个段,每个段需要一个描述符来描述,为了便于组织管理,80386及以后处理器把描述符组织成表,即描述符表。在保护模式中存在三种描述符表 “全局描述符表”(GDT)、“局部描述符表”(LDT)和中断描述符表(IDT)。全局描述符表GDT GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。段选择子 段选择子(Selector)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的,如图三①步。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)如图四& 段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符(如图三①步)。然后用描述符表中的段基址加上逻辑地址(SEL: OFFSET)的OFFSET就可以转换成线性地址(如图三②步),段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在 LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。例如给出逻辑地址:21h:h转换为线性地址& & & a. 选择子SEL=21h=0 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1& & & b. OFFSET=h若此时GDT第四个描述符中描述的段基址(Base)为h,则线性地址=456789h。局部描述符表LDT 局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图五 LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。LDTR可以在程序中随时改变,通过使用lldt指令。如图五,如果装载的是Selector 2则LDTR指向的是表LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址h。& & & & 1. 首先需要装载LDTR使它指向LDT2 使用指令lldt将Select2装载到LDTR& & & & 2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。OFFSET=h。逻辑地址为1C:h& & & & 3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是h,则线性地址=456789h& & & & 4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)& & & & 总的思想就是首先通过段选择子在描述符表中找到相应段的描述符,根据描述符中的段基址首先确定段的位置,再通过OFFSET加上段基址计算出线性地址。分页机制 线性地址转换成物理地址。存储器分页管理机制 在保护模式下,控制寄存器CR0中的最高位PG位控制分页管理机制是否生效。如果PG=1,分页机制生效,把线性地址转换为物理地址。如果PG=0,分页机制无效,线性地址就直接作为物理地址。必须注意,只有在保护方式下分页机制才可能生效。只有在保证使PE位为1的前提下,才能够使PG位为1,否则将引起通用保护故障。 分页机制把线性地址空间和物理地址空间分别划分为大小相同的块。这样的块称之为页。通过在线性地址空间的页与物理地址空间的页之间建立的映射,分页机制实现线性地址到物理地址的转换。线性地址空间的页与物理地址空间的页之间的映射可根据需要而确定,可根据需要而改变。线性地址空间的任何一页,可以映射为物理地址空间中的任何一页。采用分页管理机制实现线性地址到物理地址转换映射的主要目的是便于实现虚拟存储器。不象段的大小可变,页的大小是相等并固定的。根据程序的逻辑划分段,而根据实现虚拟存储器的方便划分页。 在80386中,页的大小固定为4K字节,每一页的边界地址必须是4K的倍数。因此,4G大小的地址空间被划分为1M个页,页的开始地址具有“XXXXX000H”的形式。为此,我们把页开始地址的高20位XXXXXH称为页码。线性地址空间页的页码也就是页开始边界线性地址的高20位;物理地址空间页的页码也就是页开始边界物理地址的高20位。可见,页码左移12位就是页的开始地址,所以页码规定了页。 由于页的大小固定为4K字节,且页的边界是4K的倍数,所以在把32位线性地址转换成32位物理地址的过程中,低12位地址保持不变。也就是说,线性地址的低12位就是物理地址的低12位。假设分页机制采用的转换映射把线性地址空间的XXXXXH页映射到物理地址空间的YYYYYH页,那么线性地址XXXXXxxxH被转换为YYYYYxxxH。因此,线性地址到物理地址的转换要解决的是线性地址空间的页到物理地址空间的页的映射,也就是线性地址高20位到物理地址高20位的转换。线性地址到物理地址的转换。 线性地址空间的页到物理地址空间的页之间的映射用表来描述。由于4G的地址空间划分为1M个页,因此,如果用一张表来描述这种映射,那么该映射表就要有1M个表项,若每个表项占用4个字节,那么该映射表就要占用4M字节。为避免映射表占用如此巨大的存储器资源,所以80386把页映射表分为两级。页映射表的第一级称为页目录表,存储在一个4K字节的物理页中。页目录表共有1K个表项,其中,每个表项为4字节长,包含对应第二级表所在物理地址空间页的页码。页映射表的第二级称为页表,每张页表也安排在一个4K字节的页中。每张页表都有1K个表项,每个表项为4字节长,包含对应物理地址空间页的页码。由于页目录表和页表均由1K个表项组成,所以使用10位的索引就能指定表项,即用10位的索引值乘以4加基地址就得到了表项的物理地址。 下图显示了由页目录表和页表构成的页映射表结构。从图中可见,控制寄存器CR3指定页目录表;页目录表可以指定1K个页表,这些页表可以分散存放在任意的物理页中,而不需要连续存放;每张页表可以指定1K个物理地址空间的页,这些物理地址空间的页可以任意地分散在物理地址空间中。需要注意的是,存储页目录表和页表的基地址是对齐在4K字节边界上的。具体转换
控制寄存器CR3的高20位作为页目录表所在物理页的 页码。首先把线性地址的最高10位(即位22至位31)作为页目录表的索引,对应表项所包含的页码指定页表;然后,再把线性地址的中间10位(即位12至 位21)作为所指定的页目录表中的页表项的索引,对应表项所包含的页码指定物理地址空间中的一页;最后,把所指定的物理页的页码作为高20位,把线性地址 的低12位不加改变地作为32位物理地址的低12位。& &
为了避免在每次存储器访问时都要访问内存中的页表,以便提高访问内存的速度,80386处理器的硬件把最近使用的线性—物理地址转换函 数存储在处理器内部的页转换高速缓存中。在访问存储器页表之前总是先查阅高速缓存,仅当必须的转换不在高速缓存中时,才访问存储器中的两级页表。页转换高 速缓存也称为页转换查找缓存,记为TLB。三、进程的内存空间进程虚拟地址空间的实现 & Windows系统中每个进程都有一个私有的虚拟地址空间,系统需要将每个进程的虚拟地址都映射到物理内存地址上。为了实现系统中每个进程都有一个私有的虚拟地址空间,系统为每一个进程都创建一个页目录( Directory)和一组页表。每个进程的页表是独立的,而内核空间的页表是所有进程共享的。 在x86平台上,CR3寄存器标识了页目录所在的物理地址。Windows系统上的第一个进程都有独立的页目录,因此系统运行时,CR3寄存器中保存的页目录地址是会变化的,其值为当前运行的进程的页目录地址。当系统要进行进程切换时,会将CR3寄存器中保存的页目录地址设置为将要切换过去并开始运行的进程的页目录地址。Windows平台的每一个进程都有一个KPROCESS结构的数据块,其中包括了其进程页目录的地址。数据共享与保护& 有一些进程间共享的数据、系统的可执行代码(系统DLL)等,在各个进程间都是一致的,因此没有必要在物理内存中为这些数据保存多份,不同进程的虚拟内存分页可以映射为同样的物理内存分页,这样可以节省物理内存的使用。 同时,为了保证这种在映射到相同物理内存页上的内存分页在进程上仍然是私有的,系统还提供了一些保护机制。如果某个进程将某个系统DLL加载入进程内存空间以后,对该DLL中的数据进行了写操作,系统就会监视到该操作,并在数据写入之前,将要写入的进程虚拟内存分页映射到另一个新的物理分页,并将原分页中系统DLL的内容复制到这个分页中,进程间不共享这个新物理内存分页,最后进程完成写操作,将数据写入这个新分页中。这种对内存中系统DLL数据的写入操作不会影响到其他进程,因此保护了各个进程中数据的独立性,这种机制叫Copy-On-Write。如果系统确实需要在进程间共享数据,也可以对特定的页面不使用Copy-On-Write,如图5-3与图5-4所示。&& 应用程序开发人员需要了解的内容 & 在编写应用程序时,程序员不用过多地考虑系统底层是如何实现这种机制的。在进行应用程序开发时,开发人员只需要了解以下内容。 & (1)虚拟地址空间中的数据是分页管理的。 (2)应用程序不用考虑系统中其他应用程序的内存使用情况,如占用了多少内存、占用了哪些内存等。& (3)虚拟地址并不是物理地址空间中的地址,不是数据在内存中真实存在的地址,操作系统会将进程的虚拟地址映射到真实的物理内存的地址。& (4)进程也不用考虑真实的物理内存有多大,只需要了解可以使用2GB(一般情况下)的内存,操作系统负责转换。& (5)如果系统中没有足够的物理内存供使用,那么操作系统会将当前没有使用的内存分页“调度”到硬盘上保存起来。页面调度不会造成内存中的数据在虚拟地址空间中地址的改变,所以进程不需要知道内存分页是如何调度的,不需要知道内存中保存的数据是在内存中还是在硬盘上,只需要知道其虚拟地址就可以了。 四、虚拟内存布局、内存的分工、堆与栈 进程的虚拟内存空间分为两个部分,低2GB(或3GB_)由应用程序使用,高2GB(或1GB)由系统内核程序使用。 系统内核的内存空间中包括驱动程序,系统内核可执行程序、用于内存管理的数据结构(页目录、页表等),用于进程管理、线程调度的数据结构、各种中断处理程序,系统缓存等。应用程序可使用的低地址空间中包括了应用程序的代码、数据(全局变量等)、系统和用户DLL的代码、各线程的栈、堆等。 栈和堆是两种重要的内在管理形式。 进程的每个线程都有自己的栈,栈与函数的调用、执行和返回及局部变量的保存相关,一般情况下,栈中保存着函数的参数、返回地址和局部变量,调用函数将参数入栈,调用指令将函数返回地址入栈,被调用函数负责保存调用函数的相关栈指针,为局部变量分配空间等,而堆是一种可以动态分配和释放的内存,由堆管理器进行管理,用户在程序中使用的内存分配函数都是通过堆进行分配的。五、内存的保护属性和存取权限 系统为每个内存分页提供保护属性和存取权限,内存的保护属性和存取权限的最小单位是分页,也就是说同一个分页中的内存必然具有相同的保护属性和存取权限。用户地址空间中内存分页有多种保护属性和存取权限。如图。&参考资料来源于网络,以及书籍。
女大学生“两年8次人流”谁该反思?风青杨&&文&&&一老大夫介绍,有对大学生情侣,女孩18岁,二人上大学后同居。几乎隔两个月就来找她一次,两年做了8次人工流产。每次女孩儿都一脸泪,男孩儿都一脸歉意。(10月27日《新文化报》)&稍微有一点常识的人或许都知道,人流特别是多次人流,其危害性是相当大的。按照专业的说法,人流可造成宫颈不同程度的损伤,易引发早产;会妨碍今后妊娠时胎盘的正常植入,引发产后大出血;有碍胎儿正常发育和生长,并可使产程中胎儿窘迫症或窒息机率增加……但据国家计生委发布的数据显示,中国每年人工流产女性多达1300万人次,位居世界第一。其中25岁以下人数占一半,在校大学生已成为人流的“主力军”。&性知识普及的缺乏,尤其是“羞于谈性”这一文化潜意识的无处不在,使得包括大学生在内的青年人群体都对一些基本的性知识闻所未闻,曾经有媒体调查发现,如果一名学生在国内接受教育,恐怕直到博士毕业,都无缘真正意义上的性教育,即便是获得了一些认知的,其获取途径也不是正规的教育渠道,而是多半源自旁门左道。于是乎,我们一边看到“xx大学生同居流产xx次”,一边看到“误以为婚后‘同房’就是在一张床上睡觉,博士丈夫和硕士妻子结婚三年都没怀孕。专家询问他们平时的‘夫妻生活’后,哭笑不得。原来,这位妻子竟还是处女!”这样类似的新闻。&与此相反,很多专科医院和门诊为了增加客源,甚至打出学生做人流享受半价或是七折等优惠招牌。“三分钟无痛人流,安全、可靠……”实际上应该提倡如何避孕,而不是大肆宣扬怎样做人流才不痛。更进一步的说,如何让青年人形成正确的性观念,并懂得如何去规避性伤害,如何预防意外怀孕、避免人流损伤才应该是“性知识”普及的本意所在,也应该是医疗机构去努力传递的正面信息。但是这一点,学校没有做,医院也没有做,大部分家长更没有做。&在一份&“女大学生生殖健康知识现状调查及分析”报告中,在对5所高校在校3149名女学生进行的生殖健康知识问卷调查发现,在校女学生的生殖健康知识比较匮乏。82.4%的学生不知晓人流的危害以及人流手术后的注意事项。更让人感到心痛的是,某高校一名大四女生,已怀孕3个月了,自己居然不确定是否怀孕。当医生告诉她必须住院手术时,她这才感到事情的严重性。在医生的询问下,她表示和男朋友相处时,根本不知道采取避孕措施。&大学宿舍的“产房悲剧”固然要归咎于当事人,但也说明备受重视的青春期性教育依然停留在“既不动口、又不动手”的阶段。避孕知识作为最基础的常识,却很少被公开言说,通常只能以“秘笈”的形式在学生之中流传。学生也无法便捷地得到安全套、避孕药等物品,即使他们对避孕知识了如指掌也只是纸上谈兵。另外一点,父母也有责任。比比外国父母,中国家长在性教育方面做得明显不够好。外国家长会主动、大方地告诉孩子一些性知识,而不会遮遮掩掩地避免跟儿女交谈这类话题。&&当然,作为女大学生,也要学会爱护自己,如果自己都不懂的爱自己,那么激情过后,所有的伤痛和眼泪都只能潜藏在心底。有多少个堕胎的女人知道,其实每做一次流产,你患各种妇科疾病的概率就会比健康人上升数倍。如果一个男人真的爱你,就不要把流产做为你验证他对你的爱有多少的理由,尽管“两年做了8次人工流产。每次女孩儿都一脸泪,男孩儿都一脸歉意。”但与其让他在你卧床的时候煲汤、说笑话给你,甚至男方把他的父母请到医院来照顾你,不如在激情之前准备好保护自己的措施。试问你有多少可以肆意挥霍的青春和体力在无谓的手术上?(文/风青杨&微信关注:qingyang7788)&版权声明网络转载务必标明作者并给出原文链接,报纸杂志用稿需微博私信取得授权。侵权必究法律责任!喜欢读书、爱跑步者,请免费订阅微信:fengqingyang8964
//ChopStick.javapublic class ChopStick { private S public ChopStick(String name){ this.name=
} public String getNumber() {
}//Philosopher.javaimport java.util.*;public class Philosopher extends Thread{& &private ChopStick leftChopS& &private ChopStick rightChopS& && &private S& &private static Random random=new Random();& &
public Philosopher(String name,ChopStick leftChopStick, ChopStick rightChopStick)
{ this.leftChopStick = leftChopS this.rightChopStick = rightChopS this.name =}public String getNumber(){& }& public void run()& {
&sleep(random.nextInt(10));
} catch (InterruptedException e) {
} synchronized (leftChopStick) { System.out.println(this.getNumber()+ " has " + leftChopStick.getNumber()+ " and wait for "+ rightChopStick.getNumber()); synchronized (rightChopStick) { System.out.println(this.getNumber()+ &" eating ");
&& } public static void main(String[] args) { //建立三个筷子对象 ChopStick chopStick1=new ChopStick("ChopStick1"); ChopStick chopStick2=new ChopStick("ChopStick2"); ChopStick chopStick3=new ChopStick("ChopStick3");
//建立哲学家对象,并在其两边摆放筷子
Philosopher philosopher1=new Philosopher("philosopher1", chopStick1, chopStick2); Philosopher philosopher2=new Philosopher("philosopher2", chopStick2, chopStick3); Philosopher philosopher3=new Philosopher("philosopher3", chopStick3, chopStick2);
philosopher1.start(); philosopher2.start(); philosopher3.start();
心情随笔列表加载中...
this.p={b:2,n:5,r:'/l_ps/blog/#m=1&c=',mset:'000',mcon:'',srk:-100};
{if defined('fl')&&fl.length>0} {list fl as x}
${x.content|xescape:x.id,x.moveFrom} ${x.publishTime|xtime}
{if x.moveFrom&&x.moveFrom=='wap'} && {/if} {if x.moveFrom&&x.moveFrom=='mobile'} && {/if}
{/list} {else} 暂无心情随笔记录! {/if}
模块内容加载中...
this.p={b:2,c:'54511',r:'http://b.bst.126.net/common/weather/'};
{if defined('w')} ${w.province}{if w.province!=w.cityName},${w.cityName}{/if}
{var ne = w.weatherFromCode!=w.weatherToCode}
{if ne}{/if}
今:${w.temperatureLow}℃~${w.temperatureHigh}℃
{var ne = w.weatherFromCode48!=w.weatherToCode48}
{if ne}{/if}
明:${w.temperatureLow48}℃~${w.temperatureHigh48}℃
{else} 天气服务器当前不可用! {/if}
模块内容加载中...
this.p={b:2,maxb:5,maxp:6}
{if !defined('b')} 日志更新列表加载中... {elseif !b.length} 没有日志更新! {else} {list b as x}·{/list} {/if}
{if !defined('p')} 相片更新列表加载中... {elseif !p.length} 没有相片更新! {else} {list p as x}{/list} {/if}
{list 0..6 as x}${dv[x]}{/list} {list 1..6 as x}{list 1..7 as y}{/list}{/list}
{list df..dt as x}{/list}
发现好博客
列表加载中...
this.p={b:2,cn:12,ct:12};
博友列表加载中...
this.p={b:2,m:0};
列表加载中...
this.p={b:2,cn:15};
& & & & & &
网易公司版权所有&&
{list x.l as y}
{/list} {/list}
{if defined('wl')} {list wl as x}{/list} {/if}

我要回帖

更多关于 极速前进 的文章

 

随机推荐