组成原理

一、实验目的

(1)将双端口通用寄存器堆和双端口存储器模块联机;

(2)进一步熟悉计算机的数据通路;

(3)掌握数字逻辑电路中故障的一般规律,以及排除故障的一般原则和方法;

(4)锻炼分析问题与解决问题的能力,在出现故障的情况下,独立分析故障现象,并排除故障。

 

二、实验电路

图8示出了数据通路实验电路图,它是将双端口存储器实验模块和一个双端口通用寄存器堆模块(RF)连接在一起形成的。双端口存储器的指令端口不参与本次实验。通用寄存器堆连接运算器模块,本实验涉及其中的操作数寄存器DR2。

由于双端口存储器RAM是三态输出,因而可以将它直接连接到数据总线DBUS上。此外,DBUS上还连接着双端口通用寄存器堆。这样,写入存储器的数据可由通用寄存器堆提供,而从存储器RAM读出的数据也可送到通用寄存器堆保存。

双端口存储器RAM已在第三节做过介绍, DR2在第三节的实验中使用过。通用寄存器堆RF(U32)由一个ISP1016实现,功能上与两个4位的MC14580并联构成的寄存器堆类似。RF内含四个8位的通用寄存器R0、R1、R2、R3,带有一个写入端口和两个输出端口,从而可以同时写入一路数据,读出两路数据。写入端口取名为WR端口,连接一个8位的暂存寄存器(U14)ER,这是一个74HC374。输出端口取名为RS端口(B端口)、RD端口(A端口),连接运算器模块的两个操作数寄存器DR1、DR2。RS端口(B端口)的数据输出还可通过一个8位的三态门RSO(U15)直接向DBUS输出。

双端口通用寄存器堆模块的控制信号中,RS1、RS0用于选择从RS端口(B端口)读出的通用寄存器,RD1、RD0用于选择从RD端口(A端口)读出的通用寄存器。而WR1、WR0则用于选择从WR端口写入的通用寄存器。WRD是写入控制信号,当WRD=1时,在T2上升沿时刻,将暂存寄存器ER中的数据写入通用寄存器堆中由WR1、WR0选中的寄存器;当WRD =0时,ER中的数据不写入通用寄存器中。LDER信号控制ER从DBUS写入数据,当LDER=1时,在T4的上升沿,DBUS上的数据写入ER。RS_BUS#信号则控制RS端口到DBUS的输出三态门,是一个低有效信号。以上控制信号各自连接一个二进制开关K0—K15。

 

三、实验设备

(1)TEC-4计算机组成原理实验仪一台

(2)双踪示波器一台

(3)直流万用表一只

(4)逻辑测试笔一支

 

四、实验任务

(1)将实验电路与控制台的有关信号进行线路连接,方法同前面的实验。

(2)用8位数据开关向RF中的四个通用寄存器分别置入以下数据:R0=0FH,R1=0F0H,R2 =55H,R3=0AAH。

给R0置入0FH的步骤是:先用8位数码开关SW0—SW7将0FH置入ER,并且选择WR1=0、WR0 = 0、WRD = 1,再将ER的数据置入RF。给其他通用寄存器置入数据的步骤与此类似。

 

(3)分别将R0至R3中的数据同时读入到DR2寄存器中和DBUS上,观察其数据是否是存入R0至R3中的数据,并记录数据。其中DBUS上的数据可直接用指示灯显示,DR2中的数据可通过运算器ALU,用直通方式将其送往DBUS。

(4)用8位数码开关SW0—SW7向AR1送入一个地址0FH,然后将R0中的0FH写入双端口RAM。

用同样的方法,依次将R1至R3中的数据写入RAM中的0F0H、55H、0AAH单元。

(5)分别将RAM中0AAH单元的数据写入R0,55H单元的数据写入R1,0F0H单元写入R2,0FH单元写入R3。然后将R3、R2、R1、R0中的数据读出到DBUS上,通过指示灯验证读出的数据是否正确,并记录数据。

(6)进行RF并行输入输出试验。

  • 选择RS端口(B端口)对应R0,RD端口(A端口)对应R1,WR端口对应R2,并使WRD=1,观察并行输入输出的结果。选择RS端口对应R2,验证刚才的写入是否生效。记录数据。

2.保持RS端口(B端口)和WR端口同时对应R2,WRD=1,而ER中置入新的数据,观察并行输入输出的结果,RS端口输出的是旧的还是新的数据?

(7)在数据传送过程中,发现了什么故障?如何克服的?

 

五、实验步骤与实验结果

  • 接线

IAR_BUS#接VCC,禁止中断地址寄存器IAR向数据总线DBUS送数据。CER接GND,禁止存储器右端口工作。AR1_INC接GND,禁止AR1加1。S2接GND,S1接GND,S0接VCC,使运算器ALU处于直通方式。M2接GND,使DR2选择寄存器堆RF作为数据来源。置DP = 1,DZ = 0,DB = 0,使实验系统开机后处于单拍状态。

K0接SW_BUS#,K1接RS_BUS#,K2接ALU_BUS,K3接CEL#,K4接LRW,K5接LDAR1,K6接LDDR2,K7接LDER,K8接RS0,K9接RS1,K10接RD0,K11接RD1,K12接WR0,K13接WR1,K14接WRD。

合上电源。按CLR#按钮,使实验系统处于初始状态。

 

(2)向RF中的四个通用寄存器分别置入数据

令K1(RS_BUS#)= 1, K2(ALU_BUS)= 0,K3(CEL#)= 1,K4(LRW)= 1,K5(LDAR1)= 0,K6(LDDR2)= 0,K8(RS0)= 0,K9(RS1)= 0,K10(RD0)= 0,K11(RD1)= 0,K12(WR0)= 0,K13(WR1)= 0,K14(WRD)= 0。

  1. 令K0(SW_BUS#)= 0,K7(LDER)= 1。置SW7—SW0为0FH,按一次QD按钮,将0FH写入暂存寄存器ER。

令K7(LDER)= 0,K14(WRD)= 1,K12(WR0)= 0,K13(WR1)= 0,按一次QD按钮,将0FH(在ER中)写入R0寄存器。

b.令K0(SW_BUS#)= 0,K7(LDER)= 1。置SW7—SW0为F0H,按一次QD按钮,将0F0H写入暂存寄存器ER。

令K7(LDER)= 0,K14(WRD)= 1,K12(WR0)= 1,K13(WR1)= 0,按一次QD按钮,将F0H(在ER中)写入R1寄存器。

c.令K0(SW_BUS#)= 0,K7(LDER)= 1。置SW7—SW0为55H,按一次QD按钮,将55H写入暂存寄存器ER。

令K7(LDER)= 0,K14(WRD)= 1,K12(WR0)= 0,K13(WR1)= 1,按一次QD按钮,将55H(在ER中)写入R2寄存器。

  1. 令K0(SW_BUS#)= 0,K7(LDER)= 1。置SW7—SW0为0AAH,按一次QD按钮,将0AAH写入暂存寄存器ER。

令K7(LDER)= 0,K14(WRD)= 1,K12(WR0)= 1,K13(WR1)= 1,按一次QD按钮,将0AAH(在ER中)写入R0寄存器。

 

  • 分别将R0至R3中的数据同时读入到DR2寄存器中和DBUS上,观察其数据是否是存入R0至R3中的数据。

1.令K0(SW_BUS#)= 1,K2(ALU_BUS)= 0,K3(CEL#)= 1,K4(LRW)= 1,K5(LDAR1)= 0,K6(LDDR2)= 0,K7(LDER)= 0,K10(RD0)= 0,K11(RD1)= 0,K12(WR0)= 0,K13(WR1)= 0,K14(WRD)= 0。

将开关IR/DBUS至于DBUS位置。令K1(RS_BUS#)= 0,使寄存器堆中的数据送DBUS总线。令K8(RS0)= 0,K9(RS1)= 0,R0中的数据通过B端口送DBUS ,数据指示灯应显示0FH。令K8(RS0)= 1,K9(RS1)= 0,R1中的数据通过B端口送DBUS,数据指示灯应显示0F0H。令K8(RS0)= 0,K9(RS1)= 1,R2中的数据通过B端口送DBUS,数据指示灯应显示55H。令K8(RS0)= 1,K9(RS1)= 1,R3中的数据通过B端口送DBUS,数据指示灯应显示0AAH。

 

2.令K0(SW_BUS#)= 1,K1(RS_BUS#)= 1, K3(CEL#)= 1,K4(LRW)= 1,K5(LDAR1)= 0,K7(LDER)= 0,K8(RS0)= 0,K9(RS1)= 0,K12(WR0)= 0,K13(WR1)= 0,K14(WRD)= 0。

将开关IR/DBUS至于DBUS位置。令K2(ALU_BUS)= 1,使运算器ALU的运算结果送DBUS总线。由于S2接GND,S1接GND,S0接VCC,ALU做直通运算,因此DBUS数据指示灯显示的是DR2寄存器的值。令K10(RD0)= 0,K11(RD1)= 0,K6(LDDR2)= 1,按一次QD按钮,R0中的数据通过A端口送入DR2,DBUS数据指示灯应显示0FH。令K10(RD0)= 1,K11(RD1)= 0,K6(LDDR2)= 1,按一次QD按钮,R1中的数据通过A端口送入DR2,DBUS数据指示灯应显示0F0H。令K6(LDDR2)= 1,K10(RD0)= 0,K11(RD1)= 1,按一次QD按钮,R2中的数据通过A端口送入DR2,DBUS数据指示灯应显示55H。令K10(RD0)= 1,K11(RD1)= 1,K6(LDDR2)= 1,按一次QD按钮,R3中的数据通过A端口送入DR2,DBUS数据指示灯应显示0AAH。

 

(4)将R0、R1、R2、R3中的数据依次送入存储器0FH、0F0H、55H、0AAH单元。

令K2(ALU_BUS)= 0,K5(LDAR1)= 0,K6(LDDR2)= 0,K7(LDER)= 0,K10(RD0)= 0,K11(RD1)= 0,K12(WR0)= 0,K13(WR1)= 0,K14(WRD)= 0。

  1. 置AR1/AR2开关到AR1位置。令K0(SW_BUS#)= 0,K1(RS_BUS#)= 1, K3(CEL#)= 1,K5(LDAR1)= 1,置SW7—SW0为0FH,按一次QD按钮,将AR1置为0FH,地址指示灯应显示0FH。

令K0(SW_BUS#)= 1,K1(RS_BUS#)= 0,禁止数据开关SW7—SW0送DBUS,允许寄存器堆送数据总线DBUS。令K3(CEL#)= 0, K4(LRW)= 0,K5(LDAR1)= 0,K8(RS0)= 0,K9(RS1)= 0,按一次QD按钮,将R0中的数据写入存储器0FH单元。

  1. 置AR1/AR2开关到AR1位置。令K0(SW_BUS#)= 0,K1(RS_BUS#)= 1,K3(CEL#)= 1, K5(LDAR1)= 1,置SW7—SW0为0F0H,按一次QD按钮,将AR1置为0F0H,地址指示灯应显示F0H。

令K0(SW_BUS#)= 1,K1(RS_BUS#)= 0,禁止数据开关SW7—SW0送DBUS,允许寄存器堆送数据总线DBUS。令K3(CEL#)= 0,K4(LRW)= 0,K5(LDAR1)= 0,

SMTP

Files

Type
Name
Latest commit message
Commit time
3 years ago
2 years ago
2 years ago

介绍

​ 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

过程介绍

​ 服务器端和客户端通信过程如下所示:

socket通信过程

服务端

​ 服务端的过程主要在该图的左侧部分,下面对上图的每一步进行详细的介绍。

1. 套接字对象的创建

	/*
     * _domain 套接字使用的协议族信息
     * _type 套接字的传输类型
     * __protocol 通信协议
     * */
     int socket (int __domain, int __type, int __protocol) __THROW;

socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种”打开—读/写—关闭”模式的实现,可以将该函数类比常用的open()函数,服务器和客户端各自维护一个”文件”,在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

参数介绍

第一个参数:关于协议族信息可选字段如下,只列出一般常见的字段。

地址族 含义
AF_INET IPv4网络协议中采用的地址族
AF_INET6 IPv6网络协议中采用的地址族
AF_LOCAL 本地通信中采用的UNIX协议的地址族(用的少)

第二个参数:套接字类型。常用的有SOCKET_RAW,SOCK_STREAM和SOCK_DGRAM。

套接字类型 含义
SOCKET_RAW 原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议。
SOCK_STREAM SOCK_STREAM是数据流,一般为TCP/IP协议的编程。
SOCK_DGRAM SOCK_DGRAM是数据报,一般为UDP协议的网络编程;

第三个参数:最终采用的协议。常见的协议有IPPROTO_TCP、IPPTOTO_UDP。如果第二个参数选择了SOCK_STREAM,那么采用的协议就只能是IPPROTO_TCP;如果第二个参数选择的是SOCK_DGRAM,则采用的协议就只能是IPPTOTO_UDP。

2. 向套接字分配网络地址——bind()

/* 
* __fd:socket描述字,也就是socket引用
* myaddr:要绑定给sockfd的协议地址
* __len:地址的长度
*/
int bind (int __fd, const struct sockaddr* myaddr, socklen_t __len)  __THROW;

第一个参数:socket文件描述符__fd即套接字创建时返回的对象,

第二个参数:myaddr则是填充了一些网络地址信息,包含通信所需要的相关信息,其结构体具体如下:

struct sockaddr
  {
    sa_family_t sin_family;	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

在具体传参的时候,会用该结构体的变体sockaddr_in形式去初始化相关字段,该结构体具体形式如下,结构体sockaddr中的sa_data就保存着地址信息需要的IP地址和端口号,对应着结构体sockaddr_insin_portsin_addr字段。

struct sockaddr_in{
   sa_family_t sin_family;		//前面介绍的地址族
   uint16_t sin_port;			//16位的TCP/UDP端口号
   struct in_addr sin_addr;	//32位的IP地址
   char sin_zero[8];			//不使用
}

in_addr 结构定义如下:

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{
	in_addr_t s_addr;
};

sin_zero 无特殊的含义,只是为了与下面介绍的sockaddr结构体一致而插入的成员。因为在给套接字分配网络地址的时候会调用bind函数,其中的参数会把sockaddr_in转化为sockaddr的形式,如下:

struct sockaddr_in serv_addr;
...
bind(serv_socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr);

需要注意的是s_addr是一种uint32_t类型的数据,而且在网络传输时,统一都是以大端序的网络字节序方式传输数据,而我们通常习惯的IP地址格式是点分十进制,例如:“219.228.148.169”,这个时候就会调用以下函数进行转化,将IP地址转化为32位的整数形数据,同时进行网络字节转换:

in_addr_t inet_addr (const char *__cp) __THROW;
//或者
int inet_aton (const char *__cp, struct in_addr *__inp) __THROW;	//windows无此函数

如果单纯要进行网络字节序地址的转换,可以采用如下函数:

/*Functions to convert between host and network byte order.

   Please note that these functions normally take `unsigned long int' or
   `unsigned short int' values as arguments and also return them.  But
   this was a short-sighted decision since on different systems the types
   may have different representations but the values are always the same.  */

// h代表主机字节序
// n代表网络字节序
// s代表short(4字节)
// l代表long(8字节)
extern uint32_t ntohl (uint32_t __netlong) __THROW __attribute__ ((__const__));
extern uint16_t ntohs (uint16_t __netshort)
     __THROW __attribute__ ((__const__));
extern uint32_t htonl (uint32_t __hostlong)
     __THROW __attribute__ ((__const__));
extern uint16_t htons (uint16_t __hostshort)

3. 进入等待连接请求状态

给套接字分配了所需的信息后,就可以调用listen()函数对来自客户端的连接请求进行监听(客户端此时要调用connect()函数进行连接)

/* Prepare to accept connections on socket FD.
   N connection requests will be queued before further requests are refused.
   Returns 0 on success, -1 for errors.  */
extern int listen (int __fd, int __n) __THROW;

第一个参数:socket文件描述符__fd,分配所需的信息后的套接字。

第二个参数:连接请求的队列长度,如果为6,表示队列中最多同时有6个连接请求。

这个函数的fd(socket套接字对象)就相当于一个门卫,对连接请求做处理,决定是否把连接请求放入到server端维护的一个队列中去。

4. 受理客户端的连接请求

listen()中的sock(__fd : socket对象)发挥了服务器端接受请求的门卫作用,此时为了按序受理请求,给客户端做相应的回馈,连接到发起请求的客户端,此时就需要再次创建另一个套接字,该套接字可以用以下函数创建:

/* Await a connection on socket FD.
   When a connection arrives, open a new socket to communicate with it,
   set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
   peer and *ADDR_LEN to the address's actual length, and return the
   new socket's descriptor, or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int accept (int __fd, struct sockaddr *addr, socklen_t *addr_len);

函数成功执行时返回socket文件描述符,失败时返回-1。

第一个参数:socket文件描述符__fd,要注意的是这个套接字文件描述符与前面几步的套接字文件描述符不同。

第二个参数:保存发起连接的客户端的地址信息。

第三个参数: 保存该结构体的长度。

5. send/write发送信息

linux下的发送函数为:

/* Write N bytes of BUF to FD.  Return the number written, or -1.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
 ssize_t write (int __fd, const void *__buf, size_t __n) ;

而在windows下的发送函数为:

ssize_t send (int sockfd, const void *buf, size_t nbytes, int flag) ;

第四个参数是传输数据时可指定的信息,一般设置为0。

6. recv/read接受信息

linux下的接收函数为

/* Read NBYTES into BUF from FD.  Return the
   number read, -1 for errors or 0 for EOF.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
ssize_t read (int __fd, void *__buf, size_t __nbytes);

而在windows下的接收函数为

ssize_t recv(int sockfd, void *buf, size_t nbytes, int flag) ;

7 .关闭连接

/* Close the file descriptor FD.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
int close (int __fd);

退出连接,此时要注意的是:调用close()函数即表示向对方发送了EOF结束标志信息

客户端

​ 服务端的socket套接字在绑定自身的IP即 及端口号后这些信息后,就开始监听端口等待客户端的连接请求,此时客户端在创建套接字后就可以按照如下步骤与server端通信,创建套接字的过程不再重复了。

1. 请求连接

/* Open a connection on socket FD to peer at ADDR (which LEN bytes long).
   For connectionless socket types, just set the default address to send to
   and the only address from which to accept transmissions.
   Return 0 on success, -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
int connect (int socket, struct sockaddr* servaddr, socklen_t addrlen);

几个参数的意义和前面的accept函数意义一样。要注意的是服务器端收到连接请求的时候并不是马上调用accept()函数,而是把它放入到请求信息的等待队列中。

套接字的多种可选项

可以通过如下函数对套接字可选项的参数进行获取以及设置。

/* Put the current value for socket FD's option OPTNAME at protocol level LEVEL
   into OPTVAL (which is *OPTLEN bytes long), and set *OPTLEN to the value's
   actual length.  Returns 0 on success, -1 for errors.  */
extern int getsockopt (int sock, int __level, int __optname,
		       void *__optval, socklen_t *optlen) __THROW;

/* Set socket FD's option OPTNAME at protocol level LEVEL
   to *OPTVAL (which is OPTLEN bytes long).
   Returns 0 on success, -1 for errors.  */
extern int setsockopt (int sock, int __level, int __optname,
		       const void *__optval, socklen_t __optlen) __THROW;

参数说明:

scok: 套接字的文件描述符

**__level **:可选项的协议层,如下:

协议层 功能
SOL_SOCKET 套接字相关通用可选项的设置
IPPROTO_IP 在IP层设置套接字的相关属性
IPPROTO_TCP 在TCP层设置套接字相关属性

__optname :要查看的可选项名,几个主要的选项如下

选项名 说明 数据类型 所属协议层
SO_RCVBUF 接收缓冲区大小 int SOL_SOCKET
SO_SNDBUF 发送缓冲区大小 int SOL_SOCKET
SO_RCVLOWAT 接收缓冲区下限 int SOL_SOCKET
SO_SNDLOWAT 发送缓冲区下限 int SOL_SOCKET
SO_TYPE 获得套接字类型(这个只能获取,不能设置) int SOL_SOCKET
SO_REUSEADDR 是否启用地址再分配,主要原理是操作关闭套接字的Time-wait时间等待的开启和关闭 int SOL_SOCKET
IP_HDRINCL 在数据包中包含IP首部 int IPPROTO_IP
IP_MULTICAST_TTL 生存时间(Time To Live),组播传送距离 int IPPROTO_IP
IP_ADD_MEMBERSHIP 加入组播 int IPPROTO_IP
IP_OPTINOS IP首部选项 int IPPROTO_IP
TCP_NODELAY 不使用Nagle算法 int IPPROTO_TCP
TCP_KEEPALIVE TCP保活机制开启下,设置保活包空闲发送时间间隔 int IPPROTO_TCP
TCP_KEEPINTVL TCP保活机制开启下,设置保活包无响应情况下重发时间间隔 int IPPROTO_TCP
TCP_KEEPCNT TCP保活机制开启下,设置保活包无响应情况下重复发送次数 int IPPROTO_TCP
TCP_MAXSEG TCP最大数据段的大小 int IPPROTO_TCP

__optval :保存查看(get)/更改(set)的结果

optlen : 传递第四个参数的字节大小

这里只对几个可选项参数进行说明:

idea破解

一、前言

本文记录下个人 IntelliJ IDEA 2019.3激活破解教程~

说实话,IDEA 更新是真滴快,还以为 IDEA 2019.2.4 后面会更新 IDEA 2019.2.5 版本,谁知道 11 月份刚结束,官方直接就上了 2019.3 版本 …

据官方说 IDEA 2019.3 版本在启动速度以及 UI 交互上做了很大优化。

今天无事便更新一波,尝尝鲜,使用的方法还是之前 2019.2 的破解方法,同样可以破解成功,Happy~, 又可以愉快的玩耍了。

废话少说,开始激活破解 …

二、下载最新的 IDEA 2019.3

其实也可以从老版本直接升级,这里为了照顾大部分人可能第一次安装,我们选择从官网下载,下载地址为: https://www.jetbrains.com/idea/download/

点击下载,静待下载完成~

三、启动 IDEA 2019.3 安装包

这里笔者机器上,之前是安装过 IDEA 2019.2.3 版本的,所以提示是否删除老版本,这里我们选择删除老版本:

注意: 若你第一次安装,是没有这一步的,跳过看后面即可~

继续点击下一步,这里笔者的电脑是 64 位的,顺便将 64 位的 IDEA 启动图标生成到桌面上:

点击下一步,这里又弹出卸载老版本 IDEA 2019.2.3 可以做的操作,如果是第一次安装,则没有这一步,看下面步骤即可~

这里由于我之前安装有老版本的 IDEA, 选择将老版本的缓存、本地历史也删除掉:

注意:很多小伙伴激活失败,很可能是之前老版本的遗留缓存导致的,在升级的时候,一定记住要给删除掉,否则,可能导致激活失败的情况~

继续点击下一步,启动 IDEA 2019.3, 启动成功后,我们可以看到 IDEA 新的欢迎界面如下:

四、开始激活

如下图所示,我们先选择免费试用 30 天,先进去再说:

五、配置破解补丁

进去过后,我们随便建个项目,点击菜单栏 Help -> Edit Custom VM Options:

注意:切记一定要通过 IDEA 来修改 .vmoptions 文件,不要手动直接去修改,现在 IDEA 针对反破解已经越来越严格了~

下载破解补丁成功以后,笔者将它放置在了 IDEA 的安装目录 /bin 路径下,以防止后面被自己误删:

注意:补丁全路径中不要包含中文,否则,可能导致破解失败!