20155218 第八周学习总结+第八周测试+课下作业

20155218 第八周学习总结+第八周测试+课下作业

测试二:

comd.c

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main( int argc, char *argv[] ) {
int sum=0;
int i;
for(i=0;i<argc;i++){
sum += atoi(argv[i]);

}
printf("%d",sum);

}

otool -tV comd.o

汇编代码:

comd.o:
(__TEXT,__text) section
_main:
0000000000000000	pushq	%rbp
0000000000000001	movq	%rsp, %rbp
0000000000000004	subq	$0x20, %rsp
0000000000000008	movl	$_main, -0x4(%rbp)
000000000000000f	movl	%edi, -0x8(%rbp)
0000000000000012	movq	%rsi, -0x10(%rbp)
0000000000000016	movl	$_main, -0x14(%rbp)
000000000000001d	movl	$_main, -0x18(%rbp)
0000000000000024	movl	-0x18(%rbp), %eax
0000000000000027	cmpl	-0x8(%rbp), %eax
000000000000002a	jge	0x55
0000000000000030	movslq	-0x18(%rbp), %rax
0000000000000034	movq	-0x10(%rbp), %rcx
0000000000000038	movq	_main(%rcx,%rax,8), %rdi
000000000000003c	callq	_atoi
0000000000000041	addl	-0x14(%rbp), %eax
0000000000000044	movl	%eax, -0x14(%rbp)
0000000000000047	movl	-0x18(%rbp), %eax
000000000000004a	addl	$0x1, %eax
000000000000004d	movl	%eax, -0x18(%rbp)
0000000000000050	jmp	0x24
0000000000000055	leaq	0x18(%rip), %rdi ## literal pool for: "%d"
000000000000005c	movl	-0x14(%rbp), %esi
000000000000005f	movb	$0x0, %al
0000000000000061	callq	_printf
0000000000000066	movl	-0x4(%rbp), %esi
0000000000000069	movl	%eax, -0x1c(%rbp)
000000000000006c	movl	%esi, %eax
000000000000006e	addq	$0x20, %rsp
0000000000000072	popq	%rbp
0000000000000073	retq

y86:

0x0000:                        | comd.o:
0x0000:                        | (__TEXT,__text) section
0x0000:                        | _main:
0x0000:                        | 0000000000000000	pushq	%rbp
0x0000:                        | 0000000000000001	movq	%rsp, %rbp
0x0000:                        | 0000000000000004	subq	$0x20, %rsp
0x0000:                        | 0000000000000008	movl	$_main, -0x4(%rbp)
0x0000:                        | 000000000000000f	movl	%edi, -0x8(%rbp)
0x0000:                        | 0000000000000012	movq	%rsi, -0x10(%rbp)
0x0000:                        | 0000000000000016	movl	$_main, -0x14(%rbp)
0x0000:                        | 000000000000001d	movl	$_main, -0x18(%rbp)
0x0000:                        | 0000000000000024	movl	-0x18(%rbp), %eax
0x0000:                        | 0000000000000027	cmpl	-0x8(%rbp), %eax
0x0000:                        | 000000000000002a	jge	0x55
0x0000:                        | 0000000000000030	movslq	-0x18(%rbp), %rax
0x0000:                        | 0000000000000034	movq	-0x10(%rbp), %rcx
0x0000:                        | 0000000000000038	movq	_main(%rcx,%rax,8), %rdi
0x0000:                        | 000000000000003c	callq	_atoi
0x0000:                        | 0000000000000041	addl	-0x14(%rbp), %eax
0x0000:                        | 0000000000000044	movl	%eax, -0x14(%rbp)
0x0000:                        | 0000000000000047	movl	-0x18(%rbp), %eax
0x0000:                        | 000000000000004a	addl	$0x1, %eax
0x0000:                        | 000000000000004d	movl	%eax, -0x18(%rbp)
0x0000:                        | 0000000000000050	jmp	0x24
0x0000:                        | 0000000000000055	leaq	0x18(%rip), %rdi ## literal pool for: "%d"
0x0000:                        | 000000000000005c	movl	-0x14(%rbp), %esi
0x0000:                        | 000000000000005f	movb	$0x0, %al
0x0000:                        | 0000000000000061	callq	_printf
0x0000:                        | 0000000000000066	movl	-0x4(%rbp), %esi
0x0000:                        | 0000000000000069	movl	%eax, -0x1c(%rbp)
0x0000:                        | 000000000000006c	movl	%esi, %eax
0x0000:                        | 000000000000006e	addq	$0x20, %rsp
0x0000:                        | 0000000000000072	popq	%rbp
                               | 

测试三:

题目要求:

基于socket 使用教材的csapp.h csapp.c,实现daytime(13)服务器(端口我们使用13+后三位学号)和客户端
服务器响应消息格式是

客户端IP:XXXX
服务器实现者学号:XXXXXXXX
当前时间: XX:XX:XX

实现socket类似的代码在刘念老师的课上已经写过,但这次要求用课本的上的代码,所以我重新打了代码。
查看代码容易发现需要我们修改的地方是:

void echo(int connfd)
 44 {
 45     size_t n;
 46     char buf[MAXLINE];
 47     rio_t rio;
 48
 49     Rio_readinitb(&rio, connfd);
 50     while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
 51         printf("客户端IP:127.0.0.1
");
 52         printf("服务器实现学号:20155218
");
 53         printf("server received %d bytes
", n);
 54         time_t t;
 55         time(&t);
 56         printf("当前时间:%s
",ctime(&t));
 57         Rio_writen(connfd, buf, n);
 58     }
 59 }

遇到的问题:

在静态库连接的时候出现错误
在寻求同学的帮助后,得知,

结果:

课下作业1

.c:

void bubble(int *data, int count) { 

if(count == 0)
return;
int i, j;
int *p, *q; 
for(i=count-1; i!=0; i--){
p = data, q = data + 1;
for(j=0; j!=i; ++j)
{ 
if( *p > *q )
{ 
int t = *p;*p = *q;
*q = t;
} 
p++, q++;}
}}

汇编代码:

00000000 <bubble_p>:
   0:	56                   	push   %esi
   1:	53                   	push   %ebx
   2:	8b 44 24 10          	mov    0x10(%esp),%eax
   6:	8b 54 24 0c          	mov    0xc(%esp),%edx
   a:	8d 70 ff             	lea    -0x1(%eax),%esi
   d:	85 f6                	test   %esi,%esi
   f:	7e 2d                	jle    3e <bubble_p+0x3e>
  11:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi
  18:	31 c0                	xor    %eax,%eax
  1a:	8d b6 00 00 00 00    	lea    0x0(%esi),%esi
  20:	8b 4c 82 04          	mov    0x4(%edx,%eax,4),%ecx
  24:	8b 1c 82             	mov    (%edx,%eax,4),%ebx
  27:	39 d9                	cmp    %ebx,%ecx
  29:	7d 07                	jge    32 <bubble_p+0x32>
  2b:	89 5c 82 04          	mov    %ebx,0x4(%edx,%eax,4)
  2f:	89 0c 82             	mov    %ecx,(%edx,%eax,4)
  32:	83 c0 01             	add    $0x1,%eax
  35:	39 f0                	cmp    %esi,%eax
  37:	7c e7                	jl     20 <bubble_p+0x20>
  39:	83 ee 01             	sub    $0x1,%esi
  3c:	75 da                	jne    18 <bubble_p+0x18>
  3e:	5b                   	pop    %ebx
  3f:	5e                   	pop    %esi
  
Disassembly of section .text.startup:

00000000 <main>:
   0:	31 c0                	xor    %eax,%eax

y86:

0x0000:                        | Disassembly of section .text:
                               | 
0x0000:                        | 00000000 <bubble_p>:
0x0000:                        |    0:	56                   	push   %esi
0x0000:                        |    1:	53                   	push   %ebx
0x0000:                        |    2:	8b 44 24 10          	mov    0x10(%esp),%eax
0x0000:                        |    6:	8b 54 24 0c          	mov    0xc(%esp),%edx
0x0000:                        |    a:	8d 70 ff             	lea    -0x1(%eax),%esi
0x0000:                        |    d:	85 f6                	test   %esi,%esi
0x0000:                        |    f:	7e 2d                	jle    3e <bubble_p+0x3e>
0x0000:                        |   11:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi
0x0000:                        |   18:	31 c0                	xor    %eax,%eax
0x0000:                        |   1a:	8d b6 00 00 00 00    	lea    0x0(%esi),%esi
0x0000:                        |   20:	8b 4c 82 04          	mov    0x4(%edx,%eax,4),%ecx
0x0000:                        |   24:	8b 1c 82             	mov    (%edx,%eax,4),%ebx
0x0000:                        |   27:	39 d9                	cmp    %ebx,%ecx
0x0000:                        |   29:	7d 07                	jge    32 <bubble_p+0x32>
0x0000:                        |   2b:	89 5c 82 04          	mov    %ebx,0x4(%edx,%eax,4)
0x0000:                        |   2f:	89 0c 82             	mov    %ecx,(%edx,%eax,4)
0x0000:                        |   32:	83 c0 01             	add    $0x1,%eax
0x0000:                        |   35:	39 f0                	cmp    %esi,%eax
0x0000:                        |   37:	7c e7                	jl     20 <bubble_p+0x20>
0x0000:                        |   39:	83 ee 01             	sub    $0x1,%esi
0x0000:                        |   3c:	75 da                	jne    18 <bubble_p+0x18>
0x0000:                        |   3e:	5b                   	pop    %ebx
0x0000:                        |   3f:	5e                   	pop    %esi
                               |   
0x0000:                        | Disassembly of section .text.startup:
                               | 
0x0000:                        | 00000000 <main>:
0x0000:                        |    0:	31 c0                	xor    %eax,%eax
                               |   
                               |

课下作业2

要求:

把课上练习3的daytime服务器分别用多进程和多线程实现成并发服务器并测试

1.用多进程实现并发服务器:

在并发的服务器中,父进程派生一个子进程来处理每一个新的连接请求。
我们要包括一个SIGCHLD处理程序,来回收僵死进程的资源。父进程必须关闭它们各自的connfd副本。直到父子进程的connfd都关闭了,客户端的连接才会终止。

在原有的代码的基础上做如下修改:

while (1) {
	connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
	if (Fork() == 0) { 
	    Close(listenfd); /* Child closes its listening socket */
	    echo(connfd);    /* Child services client */
	    Close(connfd);   /* Child closes connection with client */
	    exit(0);         /* Child exits */
	}
	Close(connfd); /* Parent closes connected socket (important!) */
    }

运行结果:

2.用线程实现并发服务器

#include "csapp.h"

void echo(int connfd);
void *thread(void *vargp);

int main(int argc, char **argv) 
{
    int listenfd, *connfdp, port, clientlen=sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
    pthread_t tid; 

    if (argc != 2) {
	fprintf(stderr, "usage: %s <port>
", argv[0]);
	exit(0);
    }
    port = atoi(argv[1]);

    listenfd = Open_listenfd(port);
    while (1) {
	connfdp = Malloc(sizeof(int));
	*connfdp = Accept(listenfd, (SA *) &clientaddr, &clientlen);
	Pthread_create(&tid, NULL, thread, connfdp);
    }
}

/* thread routine */
void *thread(void *vargp) 
{  
    int connfd = *((int *)vargp);
    Pthread_detach(pthread_self()); 
    Free(vargp);
    echo(connfd);
    Close(connfd);
    return NULL;
}
void echo(int connfd)
{
    size_t n;
    char buf[MAXLINE];
    rio_t rio;
    
    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
        printf("客户端IP:127.0.0.1
");
        printf("服务器实现学号:20155218
");
        printf("server received %d bytes
", n);
        time_t t;
        time(&t);
        printf("当前时间:%s
",ctime(&t));
        Rio_writen(connfd, buf, n);
    }
}


实验结果:

第八周学习总结

I/O复用模型

I/O复用原理:
让应用程序可以同时对多个I/O端口进行监控以判断其上的操作是否可以进行,达到时间复用的目的。

I/O多路复用的优劣:
由于I/O多路复用是在单一进程的上下文中的,因此每个逻辑流程都能访问该进程的全部地址空间,所以开销比多进程低得多;缺点是编程复杂度高。

多进程模型

构造并发最简单的就是使用进程,像fork函数。例如,一个并发服务器,在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。

多进程优点:
每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;
通过增加CPU,就可以容易扩充性能;
可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系;
每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大

多进程缺点:
逻辑控制复杂,需要和主程序交互;
需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算

多线程模型

每个线程都有自己的线程上下文,包括一个线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的线程共享该进程的整个虚拟地址空间。由于线程运行在单一进程中,因此共享这个进程虚拟地址空间的整个内容,包括它的代码、数据、堆、共享库和打开的文件。

线程执行的模型:线程和进程的执行模型有些相似,每个进程的声明周期都是一个线程,我们称之为主线程。线程是对等的,主线程跟其他线程的区别就是它先执行。

多线程的优点:
无需跨进程边界;
程序逻辑和控制方式简单;
所有线程可以直接共享内存和变量等;
线程方式消耗的总资源比进程方式好;

多线程缺点:
每个线程与主程序共用地址空间,受限于2GB地址空间;
线程之间的同步和加锁控制比较麻烦;

一个线程的崩溃可能影响到整个程序的稳定性;
到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;
线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU

Linux下不管是多线程编程还是多进程编程,最终都是用do_fork实现的多进程编程,只是进程创建时的参数不同,从而导致有不同的共享环境。Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread 库使用一个管理线程(__pthread_manager() ,每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号,而主线程pthread_create()) 的调用者则通过管道将请求信息传给管理线程。

线程同步互斥及相关系统调用

  1. 临界区(Critical Section)适合一个进程内的多线程访问公共区域或代码段时使用。
API: 
  VOID  EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);    //进入临界区 
  VOID  LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);   //离开临界区

某一线程调用EnterCriticalSection函数进入临界区后,必须保证最后可以调用LeaveCriticalSection,否则公共区域无法释放,并被其它线程访问。
在MFC中封装了CCriticalSection类,该类提供进入临界区和离开临界区的函数Lock()和Unlock()

Ex: 
  CCriticalSection  cs;   //临界区对象 
  void  ThreadFunction() 
  { 
       cs.Lock(); 
      // 代码 
       cs.Unlock();         
   } //end ThreadFunction
  1. 互斥量 (Mutex):适合不同进程内多线程访问公共区域或代码段时使用,与临界区相似。
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName); 
   //创建一个互斥量,返回值为这个互斥量的句柄。参数bInitialOwner表示是否由调用此函数的进程拥有此互斥量 
  API: 
   HANDLE OpenMutex(DWORD dwDesiredAccess,BOOL hInheritHandle,LPCTSTR lpName);//打开一个已创建的互斥量 
   BOOL ReleaseMutex(HANDLE hMutex);   //释放 
   MFC中封装了CMutex类,同样的函数Lock()和Unlock()
  1. 事件(Event):通过线程间触发事件实现同步互斥
API:  
   HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,LPCTSTR lpName);  //创建一个事件,返回值为事件句柄 参数bManualReset表示是否通过手动重设事件,参数为TRUE,则需要调用ResetEvent重设事件,否则为自动重设 
HANDLE OpenEvent(DWORD dwDesizedAccess,BOOL bInheritHandle,LPCTSTR lpName);//打开事件

在MFC中封装了CEvent类,包括SetEvent() 触发事件、PulseEvent 暂停事件、ResetEvent()重设事件及Unlock()释放事件句柄
4. 信号量(Semaphore):与临界区和互斥量不同,可以实现多个线程同时访问公共区域数据,原理与操作系统中PV操作类似,先设置一个访问公共区域的线程最大连接数,每有一个线程访问共享区资源数就减一,直到资源数小于等于零。

API: 
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES,LONG lInitialCount,LONG lMaxmemCount,LPCTSTR lpName); 
//创建信号量,返回句柄,参数lInitialCount为信号量资源初始数基数,参数lMaxmemCount为该信号量的最大数 
HANDLE OpenSemaphore(DWORD dwDesiredAccess,BOOL hInheriHandle,LPCTSTR lpName);//打开信号量 
BOOL ReleaseSemaphore(HANDLE bSemaphore,LONG lReleaseCount,LPLONG lpPreviousCount); //释放信号量

在MFC中封装了CSemaphore类,声明该类的对象后使用API:WaitForSingleObject()函数实现等待访问资源,使用ReleaseSemaphore函数释放资源,函数参数中需串入信号量对象句柄。
总结:上述4个实现线程同步互斥的类均派生自虚基类CSyncObject,除临界区外其它3中方式均可用于多进程间线程同步互斥。
另:线程触发自定义事件
可使用API函数PostThreadMessage()函数,或创建CWinThread对象,调用该类的PostThreadMessage()

  1. 互斥锁是一种通过简单的加锁的方法来控制对共享资源的存取,用于解决线程间资源访问的唯一性问题。互斥锁有上锁和解锁两种状态,在同一时刻只能有一个线程掌握某个互斥的锁,拥有上锁状态的线程可以对共享资源进行操作。若其他线程希望对一个已经上了锁的互斥锁上锁,则该线程会被挂起,直到上锁的线程释放掉互斥锁为止。
    操作互斥锁的基本函数有:

1

.pthread_mutex_init

——互斥锁初始化;

pthread_mutex_lock

——互斥锁上锁(阻塞版);

pthread_mutex_trtylock

——互斥锁上锁(非阻塞版);

pthread_mutex_unlock

——互斥锁解锁;

pthread_mutex_destory

——消除互斥锁。
线程互斥锁的数据类型是pthread_mutex_t,在使用前,要对其进行初始化,有以下两种方法:

静态初始化:可以把常量PTHREAD_MUTEX_INITIALIZER赋给静态分配的互斥锁变量;
动态初始化:在申请内存之后,通过pthread_mutex_init进行初始化,在释放内存前需要调用pthread_mutex_destroy。

代码测试:
(1)

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 打印机
void printer(char *str)
{
	while(*str!='')
	{
		putchar(*str);	
		fflush(stdout);
		str++;
		sleep(1);
	}
	printf("
"); 
}

// 线程一
void *thread_fun_1(void *arg)
{
	char *str = "hello";
	printer(str); //打印
}

// 线程二
void *thread_fun_2(void *arg)
{
	char *str = "world";
	printer(str); //打印
}

int main(void)
{
	pthread_t tid1, tid2;
	
	// 创建 2 个线程
	pthread_create(&tid1, NULL, thread_fun_1, NULL);
	pthread_create(&tid2, NULL, thread_fun_2, NULL);

	// 等待线程结束,回收其资源
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL); 
	
	return 0;
}

测试结果:

输出混乱。

添加互斥锁:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mutex; //互斥锁

// 打印机
void printer(char *str)
{
	pthread_mutex_lock(&mutex); //上锁
	while(*str!='')
	{
		putchar(*str);	
		fflush(stdout);
		str++;
		sleep(1);
	}
	printf("
"); 
	pthread_mutex_unlock(&mutex); //解锁
}

// 线程一
void *thread_fun_1(void *arg)
{
	char *str = "hello";
	printer(str); //打印
}

// 线程二
void *thread_fun_2(void *arg)
{
	char *str = "world";
	printer(str); //打印
}

int main(void)
{
	pthread_t tid1, tid2;
	
	pthread_mutex_init(&mutex, NULL); //初始化互斥锁
	
	// 创建 2 个线程
	pthread_create(&tid1, NULL, thread_fun_1, NULL);
	pthread_create(&tid2, NULL, thread_fun_2, NULL);

	// 等待线程结束,回收其资源
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL); 
	
	pthread_mutex_destroy(&mutex); //销毁互斥锁
	
	return 0;
}

输出结果:

原文地址:https://www.cnblogs.com/xzh1996/p/7822129.html