操作系统实验一: 操作系统初步

这篇博客介绍了操作系统实验,包括通过API和汇编调用系统调用的方式,理解并发实验中程序的执行特点,探讨内存分配中不同进程的内存空间,以及线程共享变量可能带来的问题。实验内容涵盖系统调用号、并发执行的封闭性和再现性、内存地址分配和线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、(系统调用实验)了解系统调用不同的封装形式。

要求:

  1. 参考下列网址中的程序。阅读分别运行用API接口函数getpid()直接调用和汇编中断调用两种方式调用Linux操作系统的同一个系统调用getpid的程序(请问getpid的系统调用号是多少?linux系统调用的中断向量号是多少?)。
  2. 上机完成习题1.13。
  3. 阅读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操作系统源代码,系统调用实现的流程图如下。

二、(并发实验)根据以下代码完成下面的实验。

要求:

  1. 编译运行该程序(cpu.c),观察输出结果,说明程序功能。(编译命令: gcc -o cpu cpu.c –Wall)(执行命令:./cpu)
  2. 再次按下面的运行并观察结果:执行命令:./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,并换行。输出不按照参数给定的顺序。这是由于程序并发执行失去了程序的封闭性和再现性,程序和机器执行程序的活动不再一一对应。

实验截图如下所示:

三、(内存分配实验)根据以下代码完成实验。

要求:

  1. 阅读并编译运行该程序(mem.c),观察输出结果,说明程序功能。(命令: gcc -o mem mem.c –Wall)
  2. 再次按下面的命令运行并观察结果。两个分别运行的程序分配的内存地址是否相同?是否共享同一块物理内存区域?为什么?命令:./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,运行结果如下:

四、(共享的问题)根据以下代码完成实验。 

要求:

  1. 阅读并编译运行该程序,观察输出结果,说明程序功能。(编译命令:gcc -o thread thread.c -Wall –pthread)(执行命令1:./thread 1000)
  2. 尝试其他输入参数并执行,并总结执行结果的有何规律?你能尝试解释它吗?(例如执行命令2:./thread 100000)(或者其他参数。)
  3. 提示:哪些变量是各个线程共享的,线程并发执行时访问共享变量会不会导致意想不到的问题。

答:

1、程序的功能是创建两个线程,每个线程内都会将counter累加相应的次数,最终输出counter的值。

 

2、执行结果,Initial value  为0,当输入参数较小时,Final value 为输入参数的两倍。当输入参数较大时,Final value小于输入参数的两倍。

分析:Loop和counter是两个线程共享的。不同线程对同一全局变量操作时,可能会导致错误的结果,是由于在执行一个线程的某一个功能时可能会跳跃到另一个线程,发生读脏数据的情况。 此题、一个线程在进行累加操作时,另一个线程读入了旧的counter的值,从而使得累加不够输入参数的两倍。

 

实验源程序及实验报告

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值