一、(系统调用实验)了解系统调用不同的封装形式。
要求:
- 参考下列网址中的程序。阅读分别运行用API接口函数getpid()直接调用和汇编中断调用两种方式调用Linux操作系统的同一个系统调用getpid的程序(请问getpid的系统调用号是多少?linux系统调用的中断向量号是多少?)。
- 上机完成习题1.13。
- 阅读pintos操作系统源代码,画出系统调用实现的流程图。
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0x14,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
:"=m" (pid)
);
printf("%d\n",pid);
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = getpid();
printf("%d\n",pid);
return 0;
}
答:1、系统调用号和操作系统的位数和发行版本有关,其中Ubuntu 64 位机器上getpid的系统调用号是172。一般来说,Linux内核中getpid的系统调用号是39(64位)或者20(32位),这里系统调用号不同的原因是64位系统中对32位系统进行了兼容。在此,linux系统调用的中断向量号是0x80 ,系统调用号位0x14(十进制20)。
2、(1)linux系统调用的C函数形,代码如下
#include <stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
(2)汇编代码形式 ,代码如下:
section data
msg db "Hello,world!",0xA
len equ $ msg;
section .text;
global start;
start:
mov eax,4;
mov abx,1;
mov ecx,msg;
mov edx,len;
int 0x80;
mov eax,1;
xor ebx,ebx;
int 0x80;
编译运行,屏幕上打印出Hello World!
3、阅读pintos操作系统源代码,系统调用实现的流程图如下。
二、(并发实验)根据以下代码完成下面的实验。
要求:
- 编译运行该程序(cpu.c),观察输出结果,说明程序功能。(编译命令: gcc -o cpu cpu.c –Wall)(执行命令:./cpu)
- 再次按下面的运行并观察结果:执行命令:./cpu A & ; ./cpu B & ; ./cpu C & ; ./cpu D &程序cpu运行了几次?他们运行的顺序有何特点和规律?请结合操作系统的特征进行解释。
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <assert.h> #include<unistd.h> int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: cpu <string>\n"); exit(1); } char *str = argv[1]; while (1) { sleep(1); printf("%s\n", str); } return 0; }
答:1、程序功能是输出给定argv[1]参数字母。2、程序cpu运行4次,程序功能是输出给定argv[1]参数字母,两次输出之间停顿1s,并换行。输出不按照参数给定的顺序。这是由于程序并发执行失去了程序的封闭性和再现性,程序和机器执行程序的活动不再一一对应。
实验截图如下所示:
三、(内存分配实验)根据以下代码完成实验。
要求:
- 阅读并编译运行该程序(mem.c),观察输出结果,说明程序功能。(命令: gcc -o mem mem.c –Wall)
- 再次按下面的命令运行并观察结果。两个分别运行的程序分配的内存地址是否相同?是否共享同一块物理内存区域?为什么?命令:./mem &; ./mem &
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> int main(int argc, char *argv[]) { int *p = malloc(sizeof(int)); // a1 assert(p != NULL); printf("(%d) address pointed to by p: %p\n",getpid(), p); // a2 *p = 0; // a3 while (1) { sleep(1); *p = *p + 1; printf("(%d) p: %d\n", getpid(), *p); // a4 } return 0; }
答:
1、该程序功能是创建一个进程,令指针p指向该内存空间,并输出该内存的地址,然后令p的值累加,依次输出进程识别码和p的当前值。运行结果如下图:
2、执行命令,发现为两个进程分别分配了两个物理地址,分别为0x11cb01,0x112f010。两个分别运行的程序分配的内存地址不相同,不共享同一块物理内存区域。运行结果如下图:
当关闭了ALSR 地址空间随机化后再次运行程序,会发现两个分别运行的程序分配的内存地址不相同,但是共享同一块物理内存区域,均为0x602010,运行结果如下:
四、(共享的问题)根据以下代码完成实验。
要求:
- 阅读并编译运行该程序,观察输出结果,说明程序功能。(编译命令:gcc -o thread thread.c -Wall –pthread)(执行命令1:./thread 1000)
- 尝试其他输入参数并执行,并总结执行结果的有何规律?你能尝试解释它吗?(例如执行命令2:./thread 100000)(或者其他参数。)
- 提示:哪些变量是各个线程共享的,线程并发执行时访问共享变量会不会导致意想不到的问题。
答:
1、程序的功能是创建两个线程,每个线程内都会将counter累加相应的次数,最终输出counter的值。
2、执行结果,Initial value 为0,当输入参数较小时,Final value 为输入参数的两倍。当输入参数较大时,Final value小于输入参数的两倍。
分析:Loop和counter是两个线程共享的。不同线程对同一全局变量操作时,可能会导致错误的结果,是由于在执行一个线程的某一个功能时可能会跳跃到另一个线程,发生读脏数据的情况。 此题、一个线程在进行累加操作时,另一个线程读入了旧的counter的值,从而使得累加不够输入参数的两倍。