这次试验,涉及前几次实验的综合运用,比较难,明天问老师之后,会逐步完善。
//======================================实验报告=========================================================
(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
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;
#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/