2012-01-30 17:06:20 scteen 阅读数 11

整理自:http://www.cnmsdn.com/html/201109/1316004424ID10075.html

            http://my.oschina.net/kzhou/blog/29157

 

Launcher的屏幕切换效果是Android操作系统的特色之一,具有很好的用户体验。

以前,实现这种效果需要参考Launcher的源代码来实现,既费时也费力。

在2011年3月份的时候,Android发布了一个被称为“compatibility package”的兼容开发包。该兼容开发包主要包含了Android最新平台上的最酷的特性,这样的话,当你在为旧版本的Android设备开发程序的时候,就可以使用这个兼容开发包来使用最新的Android特性,而不用自己重新开发,从而减轻了开发者的开发任务。通过其中的ViewPager类实现Launcher的屏幕切换效果就显得得心应手了。

 

首先,需要下载该兼容开发包。

通过”Android SDK and AVD Manager”可以下载,具体操作过程:
  1. 启动 SDK and AVD Manager.如果你使用的是Eclipse,可以通过菜单 Window > Android SDK and AVD Manager;  如果你使用的是Windows操作系统,可以通过sdk目录中的 “SDK Manager.exe"来启动; 如果你使用的是linux系统 可以通过<sdk>/tools 目录中的android脚本来启动.
  2. 选择左边的”Available packages”,然后展开右边的” Android Repository”, 然后选择最下面的”Android Compatibility package ” 然后点击右下方的” Install selected”按钮.
  3. 等待安装完成后的提示信息.
使用下载工具(例如 迅雷)自己下载安装:
      由于一些众所周知的原因,Google的服务在国*内时不时的不能正常使用, 在下载Andorid开发工具的时候也是如此, 有时候下载了80%了 结果提示下载失败! 如果您经常遇到这种情况或者下载速度很慢, 可以通过其他方式下载.
     最新版本的兼容开发包的下载地址是: https://dl-ssl.google.com/android/repository/compatibility_r03.zip 大小是541696字节, 效验码(sha1)是”49e32c0aca8e52c2e9a398912a68c036583b917f” 下载完后把compatibility_r03.zip 这个文件解压到 <sdk>/extras/android/ 目录下即可. 解压后可以得到一个compatibility目录, 该目录下有各个版本的兼容开发包目录,上面下载的开发包包含2个版本 一个是v4;一个是v13. 每个目录下面分别包含示例代码和开发包的源代码.

如何使用兼容开发包

使用兼容开发包和使用第三方jar包一样,直接添加到项目引用的类库中就可以了:

  1. 在您的andorid项目跟目录中创建一个名称为”libs"的目录
  2. 复制您要使用版本的兼容开发包到 libs/ 目录.支持API Level 4 (Android 1.6)的jar包位于: <sdk>/extras/android/compatibility/v4/android-support-v4.jar.
  3. 把该jar包添加到类路径中.如果您使用Eclipse,只需要在Package Explorer中选中”libs”目录下的”android-support-v4.jar” 然后点击右键, 选中 Build Path > Add to Build Path. 添加成功后您会发现该jar包出现到了一个名称为”Referenced Libraries”的节点中.

现在您的app就可以使用兼容开发包了,所有支持的特性都在 android.support 包中 (例如, android.support.v4).

 

 

 

至此准备环境已经ok
下边还是通过代码进行说话吧
准备布局文件
viewpager_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:orientation="vertical">
<!-- 此处需要给出全路径 -->
<android.support.v4.view.ViewPager
    android:id="@+id/viewpagerLayout" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
</LinearLayout>
 
layout1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:orientation="vertical">
    <TextView android:textAppearance="?android:attr/textAppearanceLarge" android:layout_height="wrap_content" android:id="@+id/textView1" android:layout_width="fill_parent" android:text="第一页"></TextView>
    <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText1">
        <requestFocus></requestFocus>
    </EditText>
</LinearLayout>
 
layout2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:orientation="vertical">
    <TextView android:textAppearance="?android:attr/textAppearanceLarge" android:layout_height="wrap_content" android:id="@+id/textView1" android:layout_width="fill_parent" android:text="第二页"></TextView>
    <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText1">
        <requestFocus></requestFocus>
    </EditText>
</LinearLayout>
 
layout3.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:orientation="vertical">
    <TextView android:textAppearance="?android:attr/textAppearanceLarge" android:layout_height="wrap_content" android:id="@+id/textView1" android:layout_width="fill_parent" android:text="第三页"></TextView>
    <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editText1">
        <requestFocus></requestFocus>
    </EditText>
</LinearLayout>
 
主程序
package a.b;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

public class TestViewPager extends Activity {
	private ViewPager myViewPager;

	private MyPagerAdapter myAdapter;
	
	private LayoutInflater mInflater;
	private List<View> mListViews;
	private View layout1 = null;
	private View layout2 = null;
	private View layout3 = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.viewpager_layout);
		myAdapter = new MyPagerAdapter();
		myViewPager = (ViewPager) findViewById(R.id.viewpagerLayout);
		myViewPager.setAdapter(myAdapter);
        
        mListViews = new ArrayList<View>();
        mInflater = getLayoutInflater();
        layout1 = mInflater.inflate(R.layout.layout1, null);
        layout2 = mInflater.inflate(R.layout.layout2, null);
        layout3 = mInflater.inflate(R.layout.layout3, null);
       
        mListViews.add(layout1);
        mListViews.add(layout2);
        mListViews.add(layout3);
        
        //初始化当前显示的view
        myViewPager.setCurrentItem(1);
        
        //初始化第二个view的信息
        EditText v2EditText = (EditText)layout2.findViewById(R.id.editText1);
        v2EditText.setText("动态设置第二个view的值");
        
        myViewPager.setOnPageChangeListener(new OnPageChangeListener() {
			
			@Override
			public void onPageSelected(int arg0) {
				Log.d("k", "onPageSelected - " + arg0);
				//activity从1到2滑动,2被加载后掉用此方法
				View v = mListViews.get(arg0);
				EditText editText = (EditText)v.findViewById(R.id.editText1);
				editText.setText("动态设置#"+arg0+"edittext控件的值");
			}
			
			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
				Log.d("k", "onPageScrolled - " + arg0);
				//从1到2滑动,在1滑动前调用
			}
			
			@Override
			public void onPageScrollStateChanged(int arg0) {
				Log.d("k", "onPageScrollStateChanged - " + arg0);
				//状态有三个0空闲,1是增在滑行中,2目标加载完毕
				/**
			     * Indicates that the pager is in an idle, settled state. The current page
			     * is fully in view and no animation is in progress.
			     */
			    //public static final int SCROLL_STATE_IDLE = 0;
			    /**
			     * Indicates that the pager is currently being dragged by the user.
			     */
			    //public static final int SCROLL_STATE_DRAGGING = 1;
			    /**
			     * Indicates that the pager is in the process of settling to a final position.
			     */
			    //public static final int SCROLL_STATE_SETTLING = 2;

			}
		});
        
	}
	
    private class MyPagerAdapter extends PagerAdapter{

		@Override
		public void destroyItem(View arg0, int arg1, Object arg2) {
			Log.d("k", "destroyItem");
			((ViewPager) arg0).removeView(mListViews.get(arg1));
		}

		@Override
		public void finishUpdate(View arg0) {
			Log.d("k", "finishUpdate");
		}

		@Override
		public int getCount() {
			Log.d("k", "getCount");
			return mListViews.size();
		}

		@Override
		public Object instantiateItem(View arg0, int arg1) {
			Log.d("k", "instantiateItem");
			((ViewPager) arg0).addView(mListViews.get(arg1),0);
			return mListViews.get(arg1);
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			Log.d("k", "isViewFromObject");
			return arg0==(arg1);
		}

		@Override
		public void restoreState(Parcelable arg0, ClassLoader arg1) {
			Log.d("k", "restoreState");
		}

		@Override
		public Parcelable saveState() {
			Log.d("k", "saveState");
			return null;
		}

		@Override
		public void startUpdate(View arg0) {
			Log.d("k", "startUpdate");
		}
    	
    }
	
}
 
在实机上测试后,非常流畅,这也就是说官方版的左右滑屏控件已经实现
目前,关于viewpager的文章非常少,本文是通过阅读viewpager源代码分析出的写法
当然此文章仅是抛砖引玉,而且属于框架式程序,目的就是让读者了解API的基本用法
希望这篇原创文章对大家有帮助
欢迎感兴趣的朋友一起讨论
共同学习,共同进步
2012-01-13 11:47:39 Alex_zhuang 阅读数 648
在Windows操作系统上按下Ctrl和F12键或在Mac OS X 操作系统上同时按下fn和7键,屏幕就会从预设的直式改成横式显示,再按一次则切换回来。
2009-11-10 00:27:00 guoquanyou 阅读数 1410

 

Android1.6平台屏幕横向纵向切换快捷键

在 Windows 操作系统上按下 「Ctrl」和「F12」键 ,或是在 Mac OS X 操作系统上同时按下「fn」 和「7」键,屏幕就会从预设的直式显示改成横式显示,再按一次则切换回原来的直式显示。

纵向效果图:
android平台--青开IT实训基地提供
横向效果图:
android平台--青开IT实训基地提供
2019-11-17 20:44:23 MessiGodLike 阅读数 21

一种通用嵌入式LCD页面切换方法

在资源较匮乏的嵌入式系统上,无法运行操作系统,此种情形下,通过按键控制LCD界面的切换时,若不采取一定的逻辑框架,在需求变动时,程序逻辑改动较大。页面逐渐增多时,前期若未设计好框架通过性,后期发动成本和难度呈指数级别上升,这时介绍一种较通用的界面切换框架,在需求改动时,只要更改数组中的部分变量值便可满达到要求。

以我之前做完的一个产品为例该产品有一块小屏幕,显示系统信息,以数字和图片的方式显示,外部按键有上、下、左、右、确定、返回六个。界面部分包括主界面和内部菜单,内部菜单最深为三级。开始只能确定部分内部菜单,剩下需要后期根据实际使用情况再确定。设计一种类似于查表的方式,确定相应按键按下后需要切换至的页面。

 

 

流程非常简单,代码逻辑也很简单,把主要工作都放在页面切换的数据结构体中了。核心代码如下:

typedef void (*pfn_page)();

Int g_curPage = PAGE_MAIN;

struct sPAGE_SWITCH

{

int iCurPageIndex;//当前显示页面

int iKeyIndex;//接收到的按键

pfn_page fnNewPage;//将要切换至的页面

};

 

const sPAGE_SWITCH s_page_info[] = 

{//以主页面为例

{PAGE_MAIN, KEY_UP, page_1},

{PAGE_MAIN, KEY_DOWN, page_2},

{PAGE_MAIN, KEY_LEFT, page_3},

{PAGE_MAIN, KEY_RIGHT, page_4},

{PAGE_MAIN, KEY_ESC, page_5},

{PAGE_MAIN, KEY_ENTER, page_6},

...

};

 

//页面切换函数,两个参数分别为当前页面与按键编号

void switch_page(int cur_page, int key_index)

{

for(int i = 0; i < array_num(s_page_info); i++)

{

if(cur_page == s_page_info[i].iCurPageIndex 

&& key_index == s_page_info[i].iKeyIndex)

{

s_page_info[i].fnNewPage();

break;

}

}

}

 

//页面响应函数

void page_1()

{

...一些显示页面信息的操作

g_curPage = PAGE_1;

}

 

由当前页面,按键的编号,可以惟一确定将要切换至的页面。结构体数组中存储了所有页面在按下按键消息后,页面切换信息。如果系统有20个页面和6个按键,则结构体数组最多情况下会有20*6=120个成员。

页面切换函数在按键消息发生后被调用,函数从结构体数组中查找该切换到哪个页面,并执行相应的切换函数,自动完成页面切换。

本种方式实现的切换优势在增加、减少页面、修改页面切换顺序或机制后,不需要修改任何代码,只修改结构体数组的信息即可。可以很好应对需求变更,具有较好的通用性。

2019-11-24 16:49:50 rjszz1314 阅读数 35

一、内容

通过 Lab1 中的 bootloader 可以从实模式切换的保护模式,然后再读取磁盘并加载 ELF 文件以加载 OS
操作系统,操作系统能够读入字符并显示到屏幕上,具体内容如下: 练习 1:理解通过 make 生成可执行文件的过程
练习 2:使用 qemu 执行并调试 Lab1 中的软件练习 3:分析 bootloader 进入保护模式的过程
练习 4:分析 bootloader 加载 ELF 格式的 OS 的过程练习 5:实现函数调用堆栈跟踪函数
练习 6:完善中断初始化和处理
Challenge1:增加一用户态函数,能够实现从内核态到用户态,从用户态到内核态的转化
Challenge2:需要实现用键盘实现用户模式内核模式切换

二、目的

操作系统也是一个软件,只是机器在启动时先会运行操作系统,再在操作系统下面再运行其它软件。所以我们需要了解操作系统的启动过程。机器在启动时,先运行 BIOS 用以检测启动盘,然后跳转到
bootloader 的位置运行 bootloader,随后 bootloader 会加载磁盘上的 OS 信息,开始启动 OS 操作系统。通过这次实验我们可以了解到 bootloader 的运行过程并且学会使用 gdb 调试这个过程,以及 CPU
的中断机制和函数调用过程中的堆栈关系。并且还能了解小型操作系统 ucore OS 的编译运行和启动过程, 其中里面包含了一些工具的使用。
计算机原理:
CPU 的编址与寻址: 基于分段机制的内存管理

CPU 的中断机制
外设:串口/并口/CGA,时钟,硬盘
Bootloader 软件:
编译运行 bootloader 的过程调试 bootloader 的方法
PC 启动 bootloader 的过程
ELF 执行文件的格式和加载
外设访问:读硬盘,在 CGA 上显示字符串
ucore OS 软件:
编译运行 ucore OS 的过程
ucore OS 的启动过程调试 ucore OS 的方法
函数调用关系:在汇编级了解函数调用栈的结构和处理过程中断管理:与软件相关的中断处理
外设管理:时钟

三、实验环境配置以及工具说明

实验环境:Linux 64 位用到的工具有:
1、Make,执行 makefile 中的命令,编译 OS 形成.img 文件
2、qemu,用来加载 OS 的硬件模拟器,可以模拟很多架构的硬件环境。
3、GDB,用来调试操作系统的加载过程,可以设置断点并且查看汇编代码。

四、实验步骤以及结果分析

  • 练习 1:理解通过 make 生成可执行文件的过程

1.1 ucore.img 是 如 何 一 步 一 步 生 成 的 ?
make 之后会执行 makefile 里面的指令,先对 ucore 操作系统进行编译,初始化,debug 工具,
以及一系列驱动的编译,之后生成 ucore.img 的命令如下:
在这里插入图片描述
即:
dd if=/dev/zero of=bin/ucore.img count=10000 dd if=bin/bootlock of=bin/ucore.img conv=notrunc
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
可以看到,先生成了 kernel 和 bootblock ,最后再生成.img 文件。使用 make “V=” 的指令可以将运行结果进行输出,可以看到
在这里插入图片描述
先是对 kern 中的文件进行编译生成一系列.o 文件,而 kern 文件夹下存储着就是 ucore OS 的源码。
生成 bootblock 的过程,查看 makefile 中相关内容
在这里插入图片描述
在执行过程中执行了如下指令
在这里插入图片描述
对 bootmain.c sign.c 进行了编译,最后通过 ld 进行链接生成了 bootblock
最后再生成.img 文件,我们可以了解在生成.img 文件中相关参数的含义
-if 表示输入文件

-of 表示输出文件
count 表示被赋值的块数conv=notrunc 不截短输出文件执行之后 ucore.img 生成完毕

1.2 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
我们可以进入 sign.c 中,查看关于硬盘主引导扇区的描述
在这里插入图片描述
可以看到
磁盘主引导扇区只有 512 字节
最后两个字节为 0x55AA

  • 练习 2:使用 qemu 执行并调试 lab1 中的软件

要想对整个过程进行调试,我们可以在 /tools/gdbinit 文件中更改相关信息,以便再开始调试时加载其中的指令,开始调试。
在这里插入图片描述
2.1 设定 i8086 为系统默认结构;设定与 qemu 模拟器链接的 1234 端口号;
2.2 利 用 b *addr 设置断点
在这里插入图片描述
此时断点已经设置在了 0x7c00
2.3 输入 c 程序继续进行,此时来到第二个断点 0x7c00,
在这里插入图片描述
2.4 输入 x /4i $pc 可以查看当前执行到的位置以及汇编代码
在这里插入图片描述

  • 练习 3:分析 bootloader 进入保护模式的过程

我们可以进入/boot/bootasm.S 中查看完整的过程
1.从 0x7c00 地址开始,先进入的实模式(16 位地址访问),此时会禁止中断
在这里插入图片描述
2. 然后建立重要的数据段寄存器,DS ES SS ,全部置为 0
在这里插入图片描述
3.开启的时候需要等待 8042 Input buffer 为空
在这里插入图片描述
4.然后发送 Write 8042 Output Port (P2)命令到 8042 Input buffer
在这里插入图片描述

5.然后再等待 8042 Input buffer 为空,8042 Output Port(P2)得到字节的第 2 位置 1,然后写入8042 Input buffer;
在这里插入图片描述

6.初始化 GDT 表:一个简单的 GDT 表和其描述符已经静态储存在引导区中,直接载入
在这里插入图片描述
7.进入保护模式:此时 cr0 寄存器的 PE 位置置为了 1,因此保护模式开启了
在这里插入图片描述
8.跳转之后,基址更新,真正跳转到保护状态
在这里插入图片描述

9.最后设置相关寄存器以及堆栈指针
在这里插入图片描述
call bootmain 进入保护模式完成

因此我们可以回答以下问题:
3.1 为何开启 A20 和如何开启 A20?
A20 未开启时是实模式,此时访问超过 1MB 的地址时,就会从 0 循环计数,只有开始 A20 进入保护模式时才能完整的访问 4G 内存。
打开过程如下:

  1. 等待 8042 Input buffer 为空;
  2. 发送 Write 8042 Output Port (P2)命令到 8042 Input buffer;
  3. 等待 8042 Input buffer 为空;
  4. 将 8042 Output Port(P2)得到字节的第 2 位置 1,然后写入 8042 Input buffer;

3.2 如何初始化 GDT 表?
一个简单的 GDT 表和其描述符已经静态储存在引导区中,通过 lgdt gdtdesc 载入即可

3.3 如何使能和进入保护模式?
进入保护模式:通过将 cr0 寄存器 PE 位置 1 便开启了保护模式通过长跳转更新 cs 的基地址
设置段寄存器,并建立堆栈
转到保护模式完成,进入 boot 主方法

  • 练习 4:分析 bootloader 加载 ELF 格式的 OS 的过程

4.1 bootloader 是如何读取硬盘扇区的? 流程如下:
1、等待磁盘准备好;
2、发出读取扇区的命令;
3、等待磁盘准备好;
4、把磁盘扇区数据读到指定内存

可以查看相应的代码
Bootloader 读取硬盘扇区:
在这里插入图片描述
利用 readseg 函数读取磁盘信息
在这里插入图片描述
先四舍五入到磁盘扇区的边界将字节转换到扇区
内核从扇区 1 开始
按递增的顺序开始加载磁盘内容

4.2 bootloader 是如何加载 ELF 格式的 OS? 流程如下:

  1. 读取 ELF 的头部;
  2. 判断 ELF 文件是否是合法;
  3. 将描述表的头地址存在 ph;
  4. 按照描述表将 ELF 文件中数据载入内存;
  5. 根据 ELF 头部储存的入口信息,找到内核的入口(不再返回); 具体代码如下:

读取磁盘后,先判断 ELF 是否有效
在这里插入图片描述
先加载每个程序段
在这里插入图片描述
进入 ELF 的头指针入口

之后便开始加载 ELF 里面的 OS 系统

  • 练习 5:实现函数调用堆栈跟踪函数
    1、先获取 ebp 和 eip 的值,其中. ebp 指向堆栈位置调用者的 ebp
    2、ebp+4 指向调用者调用时的 eip,ebp+8 是函数的参数
    3、bootloader 设 置 的 堆 栈 从 0x7c00 开 始 , 使 用 “call bootmain” 转入
    bootmain 函数。 call 指令压栈,所以 bootmain 中 ebp 为 0x7bf8
    4、然后通过 ebp+12,ebp+16,ebp+20,ebp+24 来输出 4 个参数的值
    5、最后更新 ebp:ebp=ebp[0],更新 eip:eip=ebp[1],直到 ebp 对应地址的值为 0
    编写的代码如下所示:
    在这里插入图片描述
  • 练习 6:完善中断初始化和处理

6.1 中断向量表中一个表项占多少字节?其中哪几位代表中断处理代码的入口?
中断描述符表(也可简称为保护模式下的中断向量表)中一个表项占 8 个字节,2-3 字节是段选择子,0-1 字节和 6-7 字节拼成位移(offset),两者联合便是中断处理程序的入口地址

在这里插入图片描述

6.2 请编程完善 kern/trap/trap.c 中对中断向量表进行初始化的函数 idt_init
先填写 IDT 表,再将表头地址告诉 LIDT,以便加载 IDT 表
在这里插入图片描述
其中 SETGATE 的宏定义如下:
在这里插入图片描述

其中有如下定义:
gate:为相应的 idt[]数组内容,处理函数的入口地址
istrap:系统段设置为 1,中断门设置为 0
sel:段选择子
off:为 vectors[]数组内容
dpl:设置特权级。这里中断都设置为内核级,即第 0 级

6.3 请编程完善 trap.c 中的中断处理函数 trap,在对时钟中断进行处理的部分填写 trap
在时钟中断中,每中断一次便将 ticks 累加 1,当加到 100 时,输出并且归零,这样便可以得到相应的功能函数,其中 print_ticks()实现的是打印功能
在这里插入图片描述
执行结果:
在这里插入图片描述

  • Challenge1:增加一用户态函数,能够实现从内核态到用户态,从用户态到内核态的转化

出于安全考虑,操作系统会将进行用户态和内核态的区分,内核态权限高,可以调用一
些系统级的操作,而用户态权限低,访问的空间和执行的操作受限。因此一些用户级的应用需要调用系统操作时,就必须要陷入操作系统提供的陷阱,只有这样才能从用户态进入内核态进行操作。
/kern/init/init.c 中
lab1_switch_to_user()是用来从内核态切换到用户态的。先需要预留一段 8 字节空间用来给之后 ss 和 esp 的入栈。
lab1_switch_to_kernel()是用来从用户态切换到内核态的。
/kern/init/init.c lab1_switch_to_user()
在这里插入图片描述
/kern/init/init.clab1_switch_to_kernel()
在这里插入图片描述

从内核态到用户态
在这里插入图片描述
从内核态到用户态,需要建立切换到用户态所需的 trapfram 结构的数据 switchk2u
将 tf_cs,tf_ds,tf_es,tf_ss 设置为用户态的代码段和数据段
设置 EFLAG 的 I/O 特权位,使得用户态可以使用 in/out 指令
设置临时栈,指向 switchk2u,这样 iret 返回时,CPU 会从 switchk2u 恢复数据,而不是现有的栈,从而完成内核态到用户态的转变 d
代码如下:
在这里插入图片描述

从用户态到内核态
在这里插入图片描述
把 tf_cs,tf_ds 等设置成内核代码和内核数据段设置 EFLAGS,让用户态不能执行 in/out 指令
设置临时栈,指向 switchu2k,当 iret 返回时,cpu 会从 switchu2k 恢复数据,而不是从现有的栈恢复数据
代码如下:
在这里插入图片描述
利用 make grade 进行验证
在这里插入图片描述
得分 40/40

  • Challenge2:需要实现用键盘实现用户模式内核模式切换

我在这采取的是在操作系统上增加一个功能,即是用户态的动作,捕捉键盘中断信号,判断接受的字符,再进行相应的转化操作。即在 while 循环中添加用户功能。
然后在进行验证判断
在这里插入图片描述

操作系统启动后输入 0,3 进行验证
在这里插入图片描述
结果正确

没有更多推荐了,返回首页