
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
美