我是靠谱客的博主 虚心路人,最近开发中收集的这篇文章主要介绍20155218 第八周学习总结+第八周测试+课下作业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
| 

1071493-20171112172155653-1047097501.png

测试三:

题目要求:

基于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.1n");
52
printf("服务器实现学号:20155218n");
53
printf("server received %d bytesn", n);
54
time_t t;
55
time(&t);
56
printf("当前时间:%sn",ctime(&t));
57
Rio_writen(connfd, buf, n);
58
}
59 }

遇到的问题:

在静态库连接的时候出现错误1071493-20171111193243591-1191552943.png
在寻求同学的帮助后,得知,
1071493-20171111193515513-1644045358.png

结果:1071493-20171111193641372-1432416296.png

课下作业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
|
|

1071493-20171112172155653-1047097501.png

课下作业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!) */
}

运行结果:
1071493-20171111200753216-2038905091.png

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>n", 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.1n");
printf("服务器实现学号:20155218n");
printf("server received %d bytesn", n);
time_t t;
time(&t);
printf("当前时间:%sn",ctime(&t));
Rio_writen(connfd, buf, n);
}
}

实验结果:
1071493-20171112095737388-1795343463.png

第八周学习总结

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()释放事件句柄

  1. 信号量(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("n");
}
// 线程一
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;
}

测试结果:
1071493-20171112092031497-855970991.png
输出混乱。

添加互斥锁:

#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("n");
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;
}

输出结果:
1071493-20171112095037122-1867383522.png

转载于:https://www.cnblogs.com/xzh1996/p/7822129.html

最后

以上就是虚心路人为你收集整理的20155218 第八周学习总结+第八周测试+课下作业20155218 第八周学习总结+第八周测试+课下作业的全部内容,希望文章能够帮你解决20155218 第八周学习总结+第八周测试+课下作业20155218 第八周学习总结+第八周测试+课下作业所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部