现在的位置: 首页 > 综合 > 正文

哈工大操作系统试验4 信号量的实现和应用

2014年02月07日 ⁄ 综合 ⁄ 共 22703字 ⁄ 字号 评论关闭

这次试验,涉及前几次实验的综合运用,比较难,明天问老师之后,会逐步完善。

//======================================实验报告=========================================================

(1) 会出现不按顺序输出,或称是程序崩溃的情况。

      有可能缓冲区满了,生产者还在写入数据,会造覆盖掉以前的数据,导致不按顺序输出。

      或者缓冲区空了,消费者还在读数据,也会导致不按顺序输出。

      因为没有 mutex 可能会出现多个线程并发访问临界区代码,造成崩溃。

(2) 

     不行,会出现死锁。当mutex信号为一,empty信号为0时,会出现死锁状态。

//=======================================================================================================

首先建立4个系统调用。sem_open     sem_wait      sem_post()      sem_unlink()

需要修改

include/unistd.h

linux-0.11/kernel/system_call.s 

linux-0.11/include/linux/sys.h

linux-0.11/kernel/makefile

这四个文件

自己编写

linux-0.11/kernel/sem.c          (还不太会写)

pc.c (测试用,最好先在本地测试好,再放到0.11上去测)

两个文件

include/unistd.h是POSIX标准定义的unix类系统定义符号常量的头文件,在写系统调用时需要自己新建的数据类型啊,结构体啊,最好在此处声明

使用时include即可。(不要忘记unistd.h是要加在虚拟机中的)

#ifndef _UNISTD_H
#define _UNISTD_H

/* ok, this may be a joke, but I'm working on it */
#define _POSIX_VERSION 198808L

#define _POSIX_CHOWN_RESTRICTED	/* only root can do a chown (I think..) */
#define _POSIX_NO_TRUNC		/* no pathname truncation (but see in kernel) */
#define _POSIX_VDISABLE '\0'	/* character to disable things like ^C */
/*#define _POSIX_SAVED_IDS */	/* we'll get to this yet */
/*#define _POSIX_JOB_CONTROL */	/* we aren't there quite yet. Soon hopefully */

#define STDIN_FILENO	0
#define STDOUT_FILENO	1
#define STDERR_FILENO	2

#ifndef NULL
#define NULL    ((void *)0)
#endif

/* access */
#define F_OK	0
#define X_OK	1
#define W_OK	2
#define R_OK	4

/* lseek */
#define SEEK_SET	0
#define SEEK_CUR	1
#define SEEK_END	2

/* _SC stands for System Configuration. We don't use them much */
#define _SC_ARG_MAX		1
#define _SC_CHILD_MAX		2
#define _SC_CLOCKS_PER_SEC	3
#define _SC_NGROUPS_MAX		4
#define _SC_OPEN_MAX		5
#define _SC_JOB_CONTROL		6
#define _SC_SAVED_IDS		7
#define _SC_VERSION		8

/* more (possibly) configurable things - now pathnames */
#define _PC_LINK_MAX		1
#define _PC_MAX_CANON		2
#define _PC_MAX_INPUT		3
#define _PC_NAME_MAX		4
#define _PC_PATH_MAX		5
#define _PC_PIPE_BUF		6
#define _PC_NO_TRUNC		7
#define _PC_VDISABLE		8
#define _PC_CHOWN_RESTRICTED	9

#include <sys/stat.h>
#include <sys/times.h>
#include <sys/utsname.h>
#include <utime.h>

#ifdef __LIBRARY__

#define __NR_setup	0	/* used only by init, to get system going */
#define __NR_exit	1
#define __NR_fork	2
#define __NR_read	3
#define __NR_write	4
#define __NR_open	5
#define __NR_close	6
#define __NR_waitpid	7
#define __NR_creat	8
#define __NR_link	9
#define __NR_unlink	10
#define __NR_execve	11
#define __NR_chdir	12
#define __NR_time	13
#define __NR_mknod	14
#define __NR_chmod	15
#define __NR_chown	16
#define __NR_break	17
#define __NR_stat	18
#define __NR_lseek	19
#define __NR_getpid	20
#define __NR_mount	21
#define __NR_umount	22
#define __NR_setuid	23
#define __NR_getuid	24
#define __NR_stime	25
#define __NR_ptrace	26
#define __NR_alarm	27
#define __NR_fstat	28
#define __NR_pause	29
#define __NR_utime	30
#define __NR_stty	31
#define __NR_gtty	32
#define __NR_access	33
#define __NR_nice	34
#define __NR_ftime	35
#define __NR_sync	36
#define __NR_kill	37
#define __NR_rename	38
#define __NR_mkdir	39
#define __NR_rmdir	40
#define __NR_dup	41
#define __NR_pipe	42
#define __NR_times	43
#define __NR_prof	44
#define __NR_brk	45
#define __NR_setgid	46
#define __NR_getgid	47
#define __NR_signal	48
#define __NR_geteuid	49
#define __NR_getegid	50
#define __NR_acct	51
#define __NR_phys	52
#define __NR_lock	53
#define __NR_ioctl	54
#define __NR_fcntl	55
#define __NR_mpx	56
#define __NR_setpgid	57
#define __NR_ulimit	58
#define __NR_uname	59
#define __NR_umask	60
#define __NR_chroot	61
#define __NR_ustat	62
#define __NR_dup2	63
#define __NR_getppid	64
#define __NR_getpgrp	65
#define __NR_setsid	66
#define __NR_sigaction	67
#define __NR_sgetmask	68
#define __NR_ssetmask	69
#define __NR_setreuid	70
#define __NR_setregid	71
/*新增的系统调用号*/
#define __NR_sem_open	72
#define __NR_sem_wait	73
#define __NR_sem_post	74
#define __NR_sem_unlink	75


#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name)); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

#define _syscall1(type,name,atype,a) \
type name(atype a) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a))); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

#define _syscall2(type,name,atype,a,btype,b) \
type name(atype a,btype b) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b))); \
if (__res >= 0) \
	return (type) __res; \
errno = -__res; \
return -1; \
}

#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
	return (type) __res; \
errno=-__res; \
return -1; \
}

#endif /* __LIBRARY__ */

extern int errno;

int access(const char * filename, mode_t mode);
int acct(const char * filename);
int alarm(int sec);
int brk(void * end_data_segment);
void * sbrk(ptrdiff_t increment);
int chdir(const char * filename);
int chmod(const char * filename, mode_t mode);
int chown(const char * filename, uid_t owner, gid_t group);
int chroot(const char * filename);
int close(int fildes);
int creat(const char * filename, mode_t mode);
int dup(int fildes);
int execve(const char * filename, char ** argv, char ** envp);
int execv(const char * pathname, char ** argv);
int execvp(const char * file, char ** argv);
int execl(const char * pathname, char * arg0, ...);
int execlp(const char * file, char * arg0, ...);
int execle(const char * pathname, char * arg0, ...);
volatile void exit(int status);
volatile void _exit(int status);
int fcntl(int fildes, int cmd, ...);
int fork(void);
int getpid(void);
int getuid(void);
int geteuid(void);
int getgid(void);
int getegid(void);
int ioctl(int fildes, int cmd, ...);
int kill(pid_t pid, int signal);
int link(const char * filename1, const char * filename2);
int lseek(int fildes, off_t offset, int origin);
int mknod(const char * filename, mode_t mode, dev_t dev);
int mount(const char * specialfile, const char * dir, int rwflag);
int nice(int val);
int open(const char * filename, int flag, ...);
int pause(void);
int pipe(int * fildes);
int read(int fildes, char * buf, off_t count);
int setpgrp(void);
int setpgid(pid_t pid,pid_t pgid);
int setuid(uid_t uid);
int setgid(gid_t gid);
void (*signal(int sig, void (*fn)(int)))(int);
int stat(const char * filename, struct stat * stat_buf);
int fstat(int fildes, struct stat * stat_buf);
int stime(time_t * tptr);
int sync(void);
time_t time(time_t * tloc);
time_t times(struct tms * tbuf);
int ulimit(int cmd, long limit);
mode_t umask(mode_t mask);
int umount(const char * specialfile);
int uname(struct utsname * name);
int unlink(const char * filename);
int ustat(dev_t dev, struct ustat * ubuf);
int utime(const char * filename, struct utimbuf * times);
pid_t waitpid(pid_t pid,int * wait_stat,int options);
pid_t wait(int * wait_stat);
int write(int fildes, const char * buf, off_t count);
int dup2(int oldfd, int newfd);
int getppid(void);
pid_t getpgrp(void);
pid_t setsid(void);

#endif

linux-0.11/kernel/system_call.s 的修改与实验2大同小异

/*
 *  linux/kernel/system_call.s
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 *  system_call.s  contains the system-call low-level handling routines.
 * This also contains the timer-interrupt handler, as some of the code is
 * the same. The hd- and flopppy-interrupts are also here.
 *
 * NOTE: This code handles signal-recognition, which happens every time
 * after a timer-interrupt and after each system call. Ordinary interrupts
 * don't handle signal-recognition, as that would clutter them up totally
 * unnecessarily.
 *
 * Stack layout in 'ret_from_system_call':
 *
 *	 0(%esp) - %eax
 *	 4(%esp) - %ebx
 *	 8(%esp) - %ecx
 *	 C(%esp) - %edx
 *	10(%esp) - %fs
 *	14(%esp) - %es
 *	18(%esp) - %ds
 *	1C(%esp) - %eip
 *	20(%esp) - %cs
 *	24(%esp) - %eflags
 *	28(%esp) - %oldesp
 *	2C(%esp) - %oldss
 */

SIG_CHLD	= 17

EAX		= 0x00
EBX		= 0x04
ECX		= 0x08
EDX		= 0x0C
FS		= 0x10
ES		= 0x14
DS		= 0x18
EIP		= 0x1C
CS		= 0x20
EFLAGS		= 0x24
OLDESP		= 0x28
OLDSS		= 0x2C

state	= 0		# these are offsets into the task-struct.
counter	= 4
priority = 8
signal	= 12
sigaction = 16		# MUST be 16 (=len of sigaction)
blocked = (33*16)

# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12

nr_system_calls = 76

/*
 * Ok, I get parallel printer interrupts while using the floppy for some
 * strange reason. Urgel. Now I just ignore them.
 */
.globl system_call,sys_fork,timer_interrupt,sys_execve
.globl hd_interrupt,floppy_interrupt,parallel_interrupt
.globl device_not_available, coprocessor_error

.align 2
bad_sys_call:
	movl $-1,%eax
	iret
.align 2
reschedule:
	pushl $ret_from_sys_call
	jmp schedule
.align 2
system_call:
	cmpl $nr_system_calls-1,%eax
	ja bad_sys_call
	push %ds
	push %es
	push %fs
	pushl %edx
	pushl %ecx		# push %ebx,%ecx,%edx as parameters
	pushl %ebx		# to the system call
	movl $0x10,%edx		# set up ds,es to kernel space
	mov %dx,%ds
	mov %dx,%es
	movl $0x17,%edx		# fs points to local data space
	mov %dx,%fs
	call sys_call_table(,%eax,4)
	pushl %eax
	movl current,%eax
	cmpl $0,state(%eax)		# state
	jne reschedule
	cmpl $0,counter(%eax)		# counter
	je reschedule
ret_from_sys_call:
	movl current,%eax		# task[0] cannot have signals
	cmpl task,%eax
	je 3f
	cmpw $0x0f,CS(%esp)		# was old code segment supervisor ?
	jne 3f
	cmpw $0x17,OLDSS(%esp)		# was stack segment = 0x17 ?
	jne 3f
	movl signal(%eax),%ebx
	movl blocked(%eax),%ecx
	notl %ecx
	andl %ebx,%ecx
	bsfl %ecx,%ecx
	je 3f
	btrl %ecx,%ebx
	movl %ebx,signal(%eax)
	incl %ecx
	pushl %ecx
	call do_signal
	popl %eax
3:	popl %eax
	popl %ebx
	popl %ecx
	popl %edx
	pop %fs
	pop %es
	pop %ds
	iret

.align 2
coprocessor_error:
	push %ds
	push %es
	push %fs
	pushl %edx
	pushl %ecx
	pushl %ebx
	pushl %eax
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	movl $0x17,%eax
	mov %ax,%fs
	pushl $ret_from_sys_call
	jmp math_error

.align 2
device_not_available:
	push %ds
	push %es
	push %fs
	pushl %edx
	pushl %ecx
	pushl %ebx
	pushl %eax
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	movl $0x17,%eax
	mov %ax,%fs
	pushl $ret_from_sys_call
	clts				# clear TS so that we can use math
	movl %cr0,%eax
	testl $0x4,%eax			# EM (math emulation bit)
	je math_state_restore
	pushl %ebp
	pushl %esi
	pushl %edi
	call math_emulate
	popl %edi
	popl %esi
	popl %ebp
	ret

.align 2
timer_interrupt:
	push %ds		# save ds,es and put kernel data space
	push %es		# into them. %fs is used by _system_call
	push %fs
	pushl %edx		# we save %eax,%ecx,%edx as gcc doesn't
	pushl %ecx		# save those across function calls. %ebx
	pushl %ebx		# is saved as we use that in ret_sys_call
	pushl %eax
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	movl $0x17,%eax
	mov %ax,%fs
	incl jiffies
	movb $0x20,%al		# EOI to interrupt controller #1
	outb %al,$0x20
	movl CS(%esp),%eax
	andl $3,%eax		# %eax is CPL (0 or 3, 0=supervisor)
	pushl %eax
	call do_timer		# 'do_timer(long CPL)' does everything from
	addl $4,%esp		# task switching to accounting ...
	jmp ret_from_sys_call

.align 2
sys_execve:
	lea EIP(%esp),%eax
	pushl %eax
	call do_execve
	addl $4,%esp
	ret

.align 2
sys_fork:
	call find_empty_process
	testl %eax,%eax
	js 1f
	push %gs
	pushl %esi
	pushl %edi
	pushl %ebp
	pushl %eax
	call copy_process
	addl $20,%esp
1:	ret

hd_interrupt:
	pushl %eax
	pushl %ecx
	pushl %edx
	push %ds
	push %es
	push %fs
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	movl $0x17,%eax
	mov %ax,%fs
	movb $0x20,%al
	outb %al,$0xA0		# EOI to interrupt controller #1
	jmp 1f			# give port chance to breathe
1:	jmp 1f
1:	xorl %edx,%edx
	xchgl do_hd,%edx
	testl %edx,%edx
	jne 1f
	movl $unexpected_hd_interrupt,%edx
1:	outb %al,$0x20
	call *%edx		# "interesting" way of handling intr.
	pop %fs
	pop %es
	pop %ds
	popl %edx
	popl %ecx
	popl %eax
	iret

floppy_interrupt:
	pushl %eax
	pushl %ecx
	pushl %edx
	push %ds
	push %es
	push %fs
	movl $0x10,%eax
	mov %ax,%ds
	mov %ax,%es
	movl $0x17,%eax
	mov %ax,%fs
	movb $0x20,%al
	outb %al,$0x20		# EOI to interrupt controller #1
	xorl %eax,%eax
	xchgl do_floppy,%eax
	testl %eax,%eax
	jne 1f
	movl $unexpected_floppy_interrupt,%eax
1:	call *%eax		# "interesting" way of handling intr.
	pop %fs
	pop %es
	pop %ds
	popl %edx
	popl %ecx
	popl %eax
	iret

parallel_interrupt:
	pushl %eax
	movb $0x20,%al
	outb %al,$0x20
	popl %eax
	iret

 linux-0.11/include/linux/sys.h 的修改也差不多

extern int sys_setup();
extern int sys_exit();
extern int sys_fork();
extern int sys_read();
extern int sys_write();
extern int sys_open();
extern int sys_close();
extern int sys_waitpid();
extern int sys_creat();
extern int sys_link();
extern int sys_unlink();
extern int sys_execve();
extern int sys_chdir();
extern int sys_time();
extern int sys_mknod();
extern int sys_chmod();
extern int sys_chown();
extern int sys_break();
extern int sys_stat();
extern int sys_lseek();
extern int sys_getpid();
extern int sys_mount();
extern int sys_umount();
extern int sys_setuid();
extern int sys_getuid();
extern int sys_stime();
extern int sys_ptrace();
extern int sys_alarm();
extern int sys_fstat();
extern int sys_pause();
extern int sys_utime();
extern int sys_stty();
extern int sys_gtty();
extern int sys_access();
extern int sys_nice();
extern int sys_ftime();
extern int sys_sync();
extern int sys_kill();
extern int sys_rename();
extern int sys_mkdir();
extern int sys_rmdir();
extern int sys_dup();
extern int sys_pipe();
extern int sys_times();
extern int sys_prof();
extern int sys_brk();
extern int sys_setgid();
extern int sys_getgid();
extern int sys_signal();
extern int sys_geteuid();
extern int sys_getegid();
extern int sys_acct();
extern int sys_phys();
extern int sys_lock();
extern int sys_ioctl();
extern int sys_fcntl();
extern int sys_mpx();
extern int sys_setpgid();
extern int sys_ulimit();
extern int sys_uname();
extern int sys_umask();
extern int sys_chroot();
extern int sys_ustat();
extern int sys_dup2();
extern int sys_getppid();
extern int sys_getpgrp();
extern int sys_setsid();
extern int sys_sigaction();
extern int sys_sgetmask();
extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();
extern int sys_sem_open();
extern int sys_sem_wait();
extern int sys_sem_post();
extern int sys_sem_unlink();

fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid , sys_sem_open, sys_sem_wait, sys_sem_post, sys_sem_unlink};

linux-0.11/kernel/makefile 的修改    其实就是把上次的who都换成sem就行了。

#
# Makefile for the FREAX-kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#

AR	=ar
AS	=as --32
LD	=ld
LDFLAGS	=-m elf_i386 -x
CC	=gcc-3.4 -march=i386
CFLAGS	=-m32 -g -Wall -O -fstrength-reduce -fomit-frame-pointer \
	-finline-functions -nostdinc -I../include
CPP	=gcc-3.4 -E -nostdinc -I../include

.c.s:
	$(CC) $(CFLAGS) \
	-S -o $*.s {1}lt;
.s.o:
	$(AS) -o $*.o {1}lt;
.c.o:
	$(CC) $(CFLAGS) \
	-c -o $*.o {1}lt;

OBJS  = sched.o system_call.o traps.o asm.o fork.o \
	panic.o printk.o vsprintf.o sys.o exit.o \
	signal.o mktime.o sem.o

kernel.o: $(OBJS)
	$(LD) -m elf_i386 -r -o kernel.o $(OBJS)
	sync

clean:
	rm -f core *.o *.a tmp_make keyboard.s
	for i in *.c;do rm -f `basename $i .c`.s;done
	(cd chr_drv; make clean)
	(cd blk_drv; make clean)
	(cd math; make clean)

dep:
	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
	(for i in *.c;do echo -n `echo $i | sed 's,\.c,\.s,'`" "; \
		$(CPP) -M $i;done) >> tmp_make
	cp tmp_make Makefile
	(cd chr_drv; make dep)
	(cd blk_drv; make dep)

### Dependencies:
sem.s sem.o: sem.c ../include/errno.h ../include/signal.h \
  ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
  ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
  ../include/asm/segment.h
fork.s fork.o: fork.c ../include/errno.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
  ../include/asm/segment.h ../include/asm/system.h
mktime.s mktime.o: mktime.c ../include/time.h
panic.s panic.o: panic.c ../include/linux/kernel.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h
printk.s printk.o: printk.c ../include/stdarg.h ../include/stddef.h \
  ../include/linux/kernel.h
sched.s sched.o: sched.c ../include/linux/sched.h ../include/linux/head.h \
  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
  ../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \
  ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \
  ../include/asm/segment.h
signal.s signal.o: signal.c ../include/linux/sched.h ../include/linux/head.h \
  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
  ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h
sys.s sys.o: sys.c ../include/errno.h ../include/linux/sched.h \
  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \
  ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \
  ../include/sys/times.h ../include/sys/utsname.h
traps.s traps.o: traps.c ../include/string.h ../include/linux/head.h \
  ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \
  ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
  ../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h
vsprintf.s vsprintf.o: vsprintf.c ../include/stdarg.h ../include/string.h

下面开始科普一下中诶实验用到的知识

(1)对信号量操作的PV原语:

       P原语操作的动作是:
  (1)S减1;
  (2)若S减1后仍大于或等于零,则进程继续执行;
  (3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
  V原语操作的动作是:
  (1)S加1;
  (2)若相加结果大于零,则进程继续执行;
  (3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
  PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。

        PV 源语执行期间不允许有中断这句话很重要,在写cem.c的时候要使用开中断关中断来实现。

(2)cli 与 sti函数

        cli 禁止中断发生
        sti 允许中断发生
       在对 ss 和sp操作的时候,  如果有中断发生,中断的保存现场的操作是将相关寄存器值保存到ss:sp指向的地址.
       如果ss 或者sp没有完成赋值操作, 这时候ss:sp指向的地址则是不期望的地方. 如果将系统或者其他应用的数据覆盖,会导致系统/应用崩溃.

(3)系统调用 schedule()

       它会唤醒当前等待进程中优先级最高的进程,sem_wait()需要用他实现。

        吧state设为TASK_UNINTERRUPTIBLE再调用schedule()让系统处理其他的事

        sem_post()中只要吧state设为  running 可运行态,然后让系统自己去分配去就行了。

(4)文本文件与二进开文件

        fopen提供wt与wb两种打开参数,fwrite是以二进制写数据,以文本方式打开是不可读的。并且换行符'\n'会被解析成两个字符 0x0D 0x0A,

        因为是作为缓冲区pc.c用到了wb这种打开方式。

 (5)进程之间共享数据

        pcc中使用了把需要共享的数据写到,文件缓冲区中来达到进程间共享数据的目的。

  (6)函数的查询

      有一些函数可以在linux手册中查询   比如 man fopen

  (7)0.11 中的终端输出问题

     0.11 输出有问题,看结果的话可以重定向到文件来看 ./pc > a.txt

     或者让它慢慢显示    ./pc | more

     (8) 实验指导书中失效的链接

       喜欢E文的同学可以看看 http://linux.die.net/man/7/sem_overview

    (9)  PCB结构

       在linux中每一个进程都通过一个叫做task_struck的数据结构来定义,也就是PCB,fork时,系统会自动产生一个task_struct结构,从父进程里

       继承一些数据,然后被插入到进程树中。

       /include/linux/sched.h可以找到task_struct 的定义。

       在sem.c中我们主要通过修改这个结沟中的state来改变进程的状态。

   (10) 临界区

      系统中需要被多个进程访问,但同时中能被一个进程访问的共用数据。

   (11)死锁

      多个进程因互相争夺资源而互相等待的现象,无外力干预就会一直持续。

   可以在本地运行的代码,有关POSIX thread的程序在编辑时要链接,thread库

  用gcc -o pc pc.c -lpthread -Wall  就可以了 

   0.11下编译是不许要加 -lpthread 因为他根本没这个库   - -! 

   这个很简单就不做解释了,只需要说明一点,所有的消费者需要共享get_pos这个变量,来确定需要取文件缓冲区的那个位置的数据,这个变量写在文件缓冲区的第11个位置。

#define   __LIBRARY__
#include <sys/stat.h>
#include <sys/types.h>
#include  <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
int input_num(FILE* fp, int put_pos,int num){
    fseek( fp, put_pos*sizeof(int) , SEEK_SET );
    int run_code=fwrite( &num, 1, sizeof(num), fp);		
    fflush(fp);
    return run_code;	
}
int output_num(FILE* fp, int get_pos,int* num){
    fseek( fp, get_pos*sizeof(int) , SEEK_SET );
    return fread( num, sizeof(int),1, fp);	
}
int main()
{
    int const NUM=100;
    int const PRONUM=5;
    int const MAXSIZE=10;
    int  i,j,k;
    int  cost_num;
    int  get_pos = 0;
    int  put_pos = 0;
    sem_t   *empty, *full, *mutex;
    FILE *fp = NULL;
    empty =(sem_t *)sem_open("empty",O_CREAT,0064,10);
    full  = (sem_t *)sem_open("full",O_CREAT,0064,0);
    mutex = (sem_t *)sem_open("mutex",O_CREAT,0064,1);
    fp=fopen("filebuffer.txt", "wb+");
    input_num(fp,10,get_pos);

    if( !fork() )
    {
	
        for( i = 0 ; i < NUM; i++)
        {
            sem_wait(empty);
            sem_wait(mutex);
	    input_num(fp,put_pos,i);
            put_pos = ( put_pos + 1)% MAXSIZE;
            sem_post(mutex);
            sem_post(full);
        }
        exit(0);
    }

    for( k = 0; k < PRONUM ; k++ )
    {
        if( !fork() )
        {
            for( j = 0; j < NUM/PRONUM; j++ )
            {
                sem_wait(full);
                sem_wait(mutex);
		fflush(stdout);
                output_num(fp,10,&get_pos);
                output_num(fp,get_pos,&cost_num);
                printf("%d:   %d\n",getpid(),cost_num);
                fflush(stdout);
                get_pos = (get_pos + 1) % MAXSIZE;
		input_num(fp,10,get_pos);
                sem_post(mutex);
                sem_post(empty);
            }
           exit(0);
        }
    }
    wait(NULL);

    sem_unlink("empty");
    sem_unlink("full");
    sem_unlink("mutex");

    fclose(fp);
    return 0;
}

在0.11上可以执行的代码

把系统调用的宏加上,再吧sem_open的参数改一下就可以了。

#define   __LIBRARY__
#include <sys/stat.h>
#include <sys/types.h>
#include  <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
_syscall2(int,sem_open, const char*, name, unsigned int , value)
_syscall1(int, sem_wait, sem_t  *, sem)
_syscall1(int, sem_post, sem_t  *, sem)
_syscall1(int, sem_unlink, const char  *, name)


static int st = 0;
int input_num(FILE* fp, int put_pos,int num){
    int run_code;
    fseek( fp, put_pos*sizeof(int) , SEEK_SET );
    run_code=fwrite( &num, 1, sizeof(num), fp);		
    fflush(fp);
    return run_code;	
}
int output_num(FILE* fp, int get_pos,int* num){
    fseek( fp, get_pos*sizeof(int) , SEEK_SET );
    return fread( num, sizeof(int),1, fp);	
}
int main()
{
    int const NUM=500;
    int const PRONUM=5;
    int const MAXSIZE=10;
    int  i,j,k;
    int  cost_num;
    int  get_pos = 0;
    int  put_pos = 0;
    sem_t   *empty, *full, *mutex;
    FILE *fp = NULL;
    empty =(sem_t *)sem_open("empty",10);
    full  = (sem_t *)sem_open("full",0);
    mutex = (sem_t *)sem_open("mutex",1);
    fp=fopen("/var/buffer.txt", "wb+");
    input_num(fp,10,get_pos);

    if( !fork() )
    {
	
        for( i = 0 ; i < NUM; i++)
        {
            sem_wait(empty);
            sem_wait(mutex);
	    input_num(fp,put_pos,i);
            put_pos = ( put_pos + 1)% MAXSIZE;
            sem_post(mutex);
            sem_post(full);
        }
        exit(0);
    }

    for( k = 0; k < PRONUM ; k++ )
    {
        if( !fork() )
        {
            for( j = 0; j < NUM/PRONUM; j++ )
            {
                sem_wait(full);
                sem_wait(mutex);
		fflush(stdout);
                output_num(fp,10,&get_pos);
                output_num(fp,get_pos,&cost_num);
                printf("%d:   %d\n",getpid(),cost_num);
                fflush(stdout);
                get_pos = (get_pos + 1) % MAXSIZE;
		input_num(fp,10,get_pos);
                sem_post(mutex);
                sem_post(empty);
            }
           exit(0);
        }
    }
    wait(NULL);

    sem_unlink("empty");
    sem_unlink("full");
    sem_unlink("mutex");

    fclose(fp);
    return 0;
}

sem.c的代码如下,因为确实比较难写,我就抄了抄网上的代码。

需要吧数据结构的定义放到 虚拟机的user/include/unistd.h 下再包含unistd.h,这是比较牛逼的写法(上文提供的unistd.h没有包含结构体的定义)

思路是每个sem_t中包含一个循环队列,

队列中存着在当前信号量下等待的进程,

每当Sem_wait被调用且满足要求时,当前进程中断并进入队尾。

sem_post被调用且条件成立时,队头进程被唤醒。

#define Maxlength 10
struct queue
{
    int front;
    int rear;
    struct task_struct *wait[Maxlength];
};
typedef struct queue queue;
   
struct sem_t
{
    int value;
    int used;
    struct queue waitsem;
};
typedef struct sem_t sem_t;

sem.c的代码

#define __LIBRARY__
#include <unistd.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <signal.h>

const int  SEM_NUM=20; 
char sem_name[20][20] = {};
sem_t sem_array[20];

int find_sem(char* name){
    int i;
    for(i = 0;i < SEM_NUM; i++)
    {
        if(!strcmp(sem_name[i],name))
        {   
            return i;
        }
    }
    return -1;
}
char* get_name(const char* name){
	char tempname[20]={};
	char temp;
        int i=0;
	while((temp = get_fs_byte(name+i))!='\0')
	{
		tempname[i] = temp;
		i++;
	}
	tempname[i]='\0';
	return tempname;	
}
int next(int i)
{   
    return (i+1==Maxlength)?0:i+1; 
}

void initQue(queue * q)
{
    q->front = 0;
    q->rear = Maxlength - 1;
}

int empty(queue* q)
{
    if(next(q->rear) == q->front ){
	printk("isempty\n");
        return 1;
    }
    else
        return 0;
}
int isFull(queue* q)
{
    if(q->front==(q->rear+2)%Maxlength){
	printk("ifFull,%d  %d\n",q->front,q->rear);	
	return 1;
    }
    else 
	return 0;	
}
struct task_struct * getFront( queue * q )
{
    int temp;
    if( !empty(q) )
    {
        temp = q->front;
        q->front = next( q->front );
        return q->wait[temp];
    }
    else
        return NULL;
}

int insertRear( struct task_struct * child, queue * q)
{
    if(isFull(q))
        return -1;
    q->rear = next( q->rear );
    q->wait[q->rear] = child;
}

void sleep_on_sem(void)
{
        current->state=TASK_UNINTERRUPTIBLE;
        schedule();
}

void wake_on_sem(struct task_struct *p)
{
      if(p != NULL)
            (*p).state = TASK_RUNNING;
}

int sys_sem_open(const char *name, unsigned int value)
{
    int i = 0;
    char* tempname = malloc(sizeof(char)*20);
    
    tempname=get_name(name);
    if (!(i=find_sem(tempname))){
	return &sem_array[i];
    }
    for (i = 0; i < SEM_NUM; i++)
    {
        if (!sem_array[i].used)
        {
            strcpy(sem_name[i],tempname);
            sem_array[i].value = value;
            sem_array[i].used = 1;
            initQue( &(sem_array[i].waitsem));
            return &sem_array[i];
        }
    }
    return -1;
}

int sys_sem_wait(sem_t* sem)
{
    cli();
    sem->value--;
    if(sem->value < 0)
    {
        insertRear( current, &(sem->waitsem));
        sleep_on_sem();
    }
    sti();
    return 0;
}

int sys_sem_post(sem_t * sem)
{
    cli();
    sem->value++;
    if(sem->value<=0)
    {
        wake_on_sem(getFront(&(sem->waitsem)));
    }
    sti();
    return 0;
}


int sys_sem_unlink(const char *name)
{
    int locate = 0;
    char* tempname = malloc(sizeof(char)*20);
    tempname=get_name(name);
    if (!(locate=find_sem(tempname))){
        sem_array[locate].used = 0;
        sem_array[locate].value = 0;
        sem_name[locate][0] = '\0';
        return 0;
    }    
    return -1;
}

喵喵~

最后给大家推荐一个漫画家的博客

http://wc31415.blogcn.com/

抱歉!评论已关闭.