b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

哈工大操作系统---lab5:信号量的实现与应用

电脑杂谈  发布时间:2019-06-18 03:12:42  来源:网络整理

操作无法完成 系统不允许操作目标文件_金刚系统圆通操作_操作系统 哈工大

waitforsingleobject函数用来检测hhandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwmilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回。如进程只允许调用异步信号安全函数,那么child fork handler都不能用于清除同步对象操作系统 哈工大,因为这些函数全部都不是异步信号安全的。两个子线程在运行过程中,通过调用waitforsingleobject等待该互斥量,如果此时互斥量处于无信号态,则在等待线程被系统挂起,一直等到互斥量变为信号态才继续运行,waitforsingleobject返回的同时,将使互斥量再次变为无信号态。

注:

1.信号量位于内核中 + 信号量的基本操作属于系统调用

2.将信号量的实现放在kerner目录下新建的sem.c文件中

sem_t是信号量类型,根据实现的需要自定义
(也就是一个数据结构,包含:value+阻塞队列|对信号量的P.V操作)
//打开一个名称为name的信号量,如果没有就新建一个名称为name的信号量
//返回操作结果(成功->指向信号量的指针,也就是信号量在内核中的地址   失败->返回null)
sem_t *sem_open(const char * name,unsigned int value);
//p操作(value--看是不是有进程阻塞)
//返回操作结果(成功->0   失败->-1)
int sem_wait(sem_t *sem);
//v操作(value++看是不是有进程被唤醒)
//返回操作结果(成功->0   失败->-1)
int sem_post(sem_t *sem);
//删除信号量name
//返回操作结果(成功->0   失败->-1)
int sem_unlink(const char * name);

P.V操作模板

P操作:信号量–,判断是不是要阻塞

V操作:信号量++,判断是不是要唤醒

由于在执行这两个操作的时候,涉及到信号量的改变,所以防止放置进程的切换,使得信号量的值被搞乱了,就需要设置临界区来保护信号量:

这里不采用软件保护法(比如:轮换法\标记法\ peterson算法\ Lamport面包店算法),

采用硬件保护法,

由于是linux0.11运行在单cpu上(Bochs虚拟机提供单cpu环境),所以可以采用简单的开关中断的方法,

如果是多cpu环境,就使用硬件原子指令保护法(用硬件原子指令操控一个mutex信号量来保护临界区)

模板1:

如果要将阻塞的进程唤醒,直接取出阻塞队列的首个进程,用if语句实现:

操作系统 哈工大_操作无法完成 系统不允许操作目标文件_金刚系统圆通操作

//信号量--操作,判断是不是要阻塞
P()
{
    cli();//关中断
    value--;
    if(value < 0)
    {
        //阻塞进程
        //1.将进程放入对应(producer|consumer)的阻塞队列中
        //2.将进程的状态设置为阻塞态
        //调度另外一个进程
        schedual();
    }
    sti();//开中断
}
//信号量++操作,判断是不是要唤醒
V()
{
    cli();//关中断
    value++;
    if(value <= 0)
    {
        //唤醒进程
        //1.将进程从对应的阻塞队列中取出来
        //2.将进程的状态设置为就绪态,并将进程加入就绪队列
    }
    sti();//开中断
}

模板2:

如果要将阻塞的进程唤醒,因为要保证优先级高的先唤醒,所以需要先将所有阻塞的进程全部唤醒(V操作),然后用schedule()来进行调度分配信号量给唤醒的某个进程,用while()语句(P操作)来找到已经分配了信号量的进程:

//信号量--操作,判断是不是要阻塞
P()
{
    cli();//关中断
    //遍历阻塞队列所有被唤醒的进程中分配了信号量的,也就是!=0的
    while(sem->value == 0)
    {
        //则将当前进程加入到阻塞队列
        schedule();
    }
    sem->value--;
    sti();//开中断
}
//信号量++操作,判断是不是要唤醒
V()
{
    cli();//关中断
    sem->value++;
    //让阻塞队列中所有进程全部被唤醒,也就是变成就绪态,进入就绪队列
    //if 阻塞队列中没有进程 就不需要做啥
    sti();//开中断
}

总体来说:ringbuffer在生产sequencer中记录一个cursor,追踪生产者生产到的最新位置,通过worksequence和sequence记录整个workpool消费的位置和每个workprocessor消费到位置,来协调生产和消费程序。特点:不容易理解和编写,执行速度快,常用来编写系统软件和实时性要求较高的程序,例如:驱动程序、过程控制程序等。asp是微软公司开发的代替cgi脚本程序的一种应用,它可以与和其它程序进行交互,asp是一种服务器端脚本编写环境,可以用来创建和运行动态网页或web应用程序。

可能的输出效果:
1:  0
1:  1
1:  2
2:  3
2:  4
2:  5
1:  6
3:  7
3:  8
4:  9
5:  10
...
1:  498
5:  499
其中PID的顺序变化是不确定的(消费者进程的并发执行是不确定的,哪个时刻谁在执行,执行多久都是不确定的,但是后面的数一定是有序的)

多进程共享文件的操作方式:

在Linux下使用c语言,可以通过三种方式进程文件的读写:

二进制文件一共要用到三个函数,fopen,fread,fwrite。先介绍函数,我们一共要用到三个函数,fopen,fread,fwrite。许多程序使用fopen(), fread(), 或 fwrite()函数创建和管理一些自定义的文件用来保存数据. 使用sqlite替代这些自定义的文件格。

2.使用系统调用函数open()\read()\write()\lseek()\close()\ ...

3.通过内存镜像文件,使用系统调用mmap() (Linux0.11不支持)

生产者-消费者程序模板:

//生产者进程:
producer()
{
    p(empty);//empty--,看是不是要阻塞(生产者进程)
    p(mutex);//mutex--,看是不是可以进入临界区(此处对应共享缓冲区-也就是文件)
    //向缓冲区(共享文件)中写入数据
    v(mutex);//mutex++,出了临界区,需要mutex++,以便下一次可以进入
    v(full);//full++,看是不是要唤醒(消费者进程)
}
//消费者进程:
consumer()
{
    p(full);//full--,看是不是要阻塞(消费者进程)
    p(mutex);//mutex--,看是不是可以进入临界区(此处对应共享缓冲区-也就是文件)
    //向缓冲区(共享文件)中写入数据
    v(mutex);//mutex++,出了临界区,需要mutex++,以便下一次可以进入
    v(empty);//empty++,看是不是要唤醒(生产者进程)
}
在unistd.h中添加系统调用号:
...
#define __NR_setregid 71
//添加的系统调用号
#define __NR_sem_open 72
#define __NR_sem_wait 73
#define __NR_sem_post 74
#define __NR_sem_unlink 75
在system_call.s中改写系统调用数:
nr_system_calls = 72
=>
nr_system_calls = 76

金刚系统圆通操作_操作无法完成 系统不允许操作目标文件_操作系统 哈工大

在sys.h中添加系统调用的定义:
...
extern int sys_setregid();
//添加的系统调用定义
extern sem_t * sem_open();
extern int sem_wait();
extern int sem_post();
extern int sem_unlink();
//在sys_call_table数组中添加系统调用的引用:
fn_ptr sys_call_table[] = 
{ sys_setup, sys_exit, sys_fork, sys_read,……, sem_open, sem_wait, sem_post, sem_unlink},

定义在unisted.h文件中:

...
#endif /* __LIBRARY__ */
extern int errno;
//定义的信号量数据结构:
typedef struct sem_t 
{
	char name[20];//信号量的名称
	int value;    //信号量的值
	struct * tast_struct queue;//指向阻塞队列的指针
}sem_t;
/**
    如果自己实现队列(用if语句实现的时候),在这里需要定义数据结构
*/
...(原本的系统调用)
sem_t *sem_open(const char * name,unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_unlink(const char * name);

实现在kernel目录新建的sem.c文件中:

0.头文件及其初始化部分:

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <asm/segment.h>
#include <asm/system.h>
//信号量最大数量
#define SEM_LIST_LENGTH 5
//信号量数组(都初始化为没有的状态)
sem_t sem_list[SEM_LIST_LENGTH] = {
	{'\0',0,NULL}, {'\0',0,NULL},{'\0',0,NULL},{'\0',0,NULL},{'\0',0,NULL}
};

1.sem_open()信号量的打开or创建

sem_t * sem_open(const char * name,unsigned int value)
{
    //首先将信号量的名称赋值到新建的缓冲区中
    char nbuf[20];
    int i = 0;
    for(i = 0; nbuf[i] = get_fs_byte(name+i); i++);
    //然后开始遍历已有的信号量数组,如果有该名字的信号量,直接返回信号量的地址
    sem_t * result = NULL;
    for(i = 0; i < SEM_LIST_LENGTH; i++)
    {
        if(!strcmp(sem_list[i].name,nbuf))
        {
            result = &sem_list[i];
            printk("sem %s is found\n",result->name);
            return result;
        }
    }
    //如果找不到信号量,就开始新建一个名字为name的信号量,值=value,队列指针=NULL,然后返回信号量的地址
    for(int i = 0; i < SEM_LIST_LENGTH; i++)
    {
        if(sem_list[i].name[0] = '\0')
        {
            strcpy(sem_list[i].name,nbuf);
            sem_list[i].value = value;
            sem_list[i].queue = NULL;
            result = & sem_list[i];
            printk("sem %s is created , value = %d\n",result->name,result->value);
            return result;
        }
    }
}


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-106763-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...