操作系统课程设计

 

操作系统课程设计 


任务一I/O系统调用开销比较

 

任务目的:

本任务主要目的在于了解I/O系统调用的特点并通过性能测试对此有直观的认识

 

任务要求:

LINUX平台用C编程逆序(指将内容反转过来,后面的编程前面的)一个文本文件,注意显示逆转结果的必须是原文件名。如文件原内容”abcd”,处理后内容应该为”dcba”.

 

请分别使用三种方法:

1)标准CI/O库函数:fopenfreadfwrite

2UnixI/O函数:openreadwrite

3open  mmap

要求尽量考虑效率,比较三种方法的性能。

 

设计思路:

(1)       stardard.c (fopenfreadfwrite)使用标准I/O方式读写文件。程序从参数中取得测试文件名,和每次读写数据的大小size,然后程序打开测试文件输入流,和建立一个唯一名字的空文件在当前目录并打开它的文件流。定位到文件末尾-size处,读取数据到buffer数组中。将buffer内容置反。并写入输出文件。依次从文件-2*size读取size个数据到buffer,执行相同操作,知道文件内容全部处理完毕。

最后删除输入文件,并将输出文件重命名为输入文件。

(2)       unix.c (openreadwrite) 使用UINX I/O 方式读取文件,同stardard.c原理相同,但需要注意unix调用接口。

(3)       mmap.c (open  mmap) 使用文件映射方式读写文件。程序从参数中取得测试文件名,和每次读写数据的大小size,然后程序打开测试文件并从文件尾部-size位置映射size到用户空间,建立一个唯一名字的空文件,调节文件大小为输入文件大小。并映射输出文件到用户空间,从输入文件内存开始复制数据到输出文件内存,知道size个数据处理完毕,再从输入文件-2*size处映射size文件到内存,重复知道整个文件都处理完毕。最后删除输入文件,并将输出文件重命名为输入文件。特别注意,mmap映射文件的偏移只能为linux 中一页大小的整数倍,也就是说4KB的整数倍。

详细设计:

    1. Stardard.c 标准I/O 读写方式文件分析:    size_t size =atoi(argv[2]); 从参数2中取得一次读写文件大小。if (mktemp(template)==NULL)…… 创建唯一文件名的文件在当前目录下。

fseek(src,-size,SEEK_END);

         do

         {

                   if (fread(buffer,1,size,src)<size)

                            ……..

                   size_t i;

                   char tmp;

                   for(i =0; i<size/2; i++)

                   {

                            tmp         = buffer[i];

                            buffer[i]     = buffer[size-i-1];

                            buffer[size-i-1] = tmp;

                   }

                  

                   if (fwrite(buffer,1,size,dest)<size)

                            …….

         }while(fseek(src,-size*2,SEEK_CUR) ==0);

 

使用fseek定位函数从文件尾部以此-size,-2*size,-3*size …位置读取数据到buffer中,在buffer中置反数据内容,并写入输出文件中。知道文件所有数据都处理完毕。也就是说fseek从当前位置向前偏移发送错误时,即文件数据都处理完毕。

if (rename(template,argv[1]) !=0)…… 重命名输出文件并自动删除重复的文件。

 

2. UNIX I/O 读写方式,注意函数接口的不同。其他算法和标准I/O 相同。

 

3.mmap 文件映射方式

do

         {

                   …_src=mmap(NULL,size,PROT_READ,MAP_SHARED,src,position-size)..

                   …s_dest=mmap(NULL,size,…,dest,statbuf.st_size-position))…

 

                   size_t count=size;

                   char *psrc=s_src+size-1;

                   char *pdest=s_dest;

                  

                   while(count--) *pdest++=*psrc--;

                  

                   munmap(s_src,size);

                   munmap(s_dest,size);

                  

         }while(position-=size);

映射输入输出文件分块映射用户内存空间,while(count--) *pdest++=*psrc--;从映射地址尾部将数据依次复制到输出文件内存,解除映射.再次读取另一块数据,执行重复操作。直到所有数据都处理完毕。

 

 

在给出测试结果之前,必须说明测试时使用的环境。

CPUIntel Core2 T2050 (1.66GHz,双核)

内存:2*512M DDR2 667MHz)

操作系统:Ubuntu 8.10, Kernel:2.6.27.13

编译器:gcc 4.3.2

 

为了方便测试,我写了个自动脚本,自动执行我们的多次读写情况。下面是一次读写的时间输出结果:

标准    I/O                 UINX    I/O                     mmap 


real    0m0.007s                 real          0m0.033s                                                 real    0m0.002s

user   0m0.004s                                        user   0m0.000s                                                 user   0m0.000s

sys    0m0.000s                                        sys    0m0.040s                                                 sys    0m0.000s

Stardard  4KB Per 1B                            Unix  4KB Per 1B                                           Mmap  4KB Per 4KB

 

real    0m0.002s                                        real    0m0.001s                                                 real    0m0.007s

user   0m0.000s                                        user   0m0.000s                                                 user   0m0.004s

sys    0m0.000s                                        sys    0m0.000s                                                 sys    0m0.000s

Stardard  4KB Per 256B               Unix  4KB Per 256B                              Mmap  256KB Per 4KB

 

real    0m0.002s                                        real    0m0.001s                                                 real    0m0.004s

user   0m0.000s                                        user   0m0.000s                                                 user   0m0.004s

sys    0m0.000s                                        sys    0m0.000s                                                 sys    0m0.000s

Stardard  4KB Per 4KB                Unix  4KB Per 4KB                               Mmap  256KB Per 16KB

 

real    0m0.208s                                        real    0m1.471s                                                 real    0m0.722s

user   0m0.124s                                        user   0m0.052s                                                 user   0m0.516s  

sys    0m0.084s                                        sys    0m1.400s                                                 sys    0m0.188s

Stardard  256KB Per 1B               Unix  256KB Per 1B                              Mmap  64MB Per 4KB

 

real    0m0.005s                                        real    0m0.010s                                                 real    0m4.642s

user   0m0.004s                                        user   0m0.008s                                                 user   0m0.612s

sys    0m0.000s                                        sys    0m0.004s                                                 sys    0m0.524s

Stardard  256KB Per 256B           Unix  256KB Per 256B                                   Mmap  64MB Per 16KB

 

real    0m0.004s                                        real    0m0.005s

user   0m0.004s                                        user   0m0.004s

sys    0m0.000s                                        sys    0m0.000s

Stardard  256KB Per 4KB            Unix  256KB Per 4KB

 

real    0m0.005s                                        real    0m0.004s

user   0m0.000s                                        user   0m0.000s

sys    0m0.004s                                        sys    0m0.004s

Stardard  256KB Per 16KB          Unix  256KB Per 16KB

 

real    0m41.188s                             real    6m12.575s

user   0m22.537s                             user   0m18.297s

sys    0m18.389s                             sys    5m53.698s

Stardard  64MB Per 1B                Unix  64MB Per 1B

 

real    0m1.542s                                        real    0m2.448s

user   0m0.716s                                        user   0m0.676s

sys    0m0.460s                                        sys    0m1.708s

Stardard  64MB Per 256B            Unix  64MB Per 256B

 

real    0m4.418s                                        real    0m2.098s

user   0m0.656s                                        user   0m0.664s

sys    0m0.536s                                        sys    0m0.432s

Stardard  64MB Per 4KB             Unix  64MB Per 4KB

 

real    0m2.198s                                        real    0m3.645s

user   0m0.628s                                        user   0m0.680s

sys    0m0.280s                                        sys    0m0.288s

Stardard  64MB Per 16KB           Unix  64MB Per 16KB

 


测试结果数据分析:

由于本程序测试结果很多,去多次数据,用数学统计法,通过整理后。分析如下

1.标准I/O,UNIX I/O,mmap 三种方式对不同文件大小,块大小的自身比较:

标准I/O stardard.c

文本框: 文文本框: 件文本框: 块

1B

      256B

     4KB

      16KB

4KB

     0.007s

     0.002s

     0.002s

       X

256KB

     0.202s

     0.006s

     0.004s

      0.005s

     64MB

    41.459s

     2.542s

     2.421s

      2.342s

由表分析可知,对于标准I/O,由于库中库函数中实现存在4KB数据缓冲区,所以对于小于4KB的输入文件,小于4KB的块请求。它都只一次全部请求4KB。对于其I/O请求的时间几乎是相同的。而对于大于4KB的文件。请求数据块越大,一次所处理的数据越大,相对总时间会减少。

 

UNIX I/O unix.c

文本框: 文文本框: 件文本框: 块

1B

      256B

     4KB

      16KB

4KB

     0.033s

     0.002s

     0.001s

       X

256KB

     1.471s

     0.010s

     0.004s

      0.004s

     64MB

   6m9.681s

     2.448s

     2.098s

      1.562s

由表分析可知,对于UINX I/O读写方式,由于不存在数据缓冲区,所以请求数据块越大,一次所处理的数据越大,相对总时间会减少。尤其是对于大文件特别明显。如64MB文件1B256B 竟然相差147倍。

 

mmap 映射 mmap.c

文本框: 文文本框: 件文本框: 块

1B

      256B

     4KB

      16KB

4KB

      X

       X

     0.002s

       X

256KB

      Xs

       X

     0.049s

      0.007s

     64MB

      X

       X

     10.722s

      5.042s

由表分析可知,对于mmap 内容映射方式。将文件内容映射到用户内存空间。操作内存内容就相当于操作文件内容。大大减少了文件操作的复杂度。对于大文件在时间上存在一定的优势。

 

2. I/O,UNIX I/O,mmap 三种方式相对情况的比较,由于比较数据较多。4KB,和16KB块大小,4KB,64MB文件大小的情况进行详细的分析和比较。

               (A)4KB 文件                         B64MB 文件

通过图表(A)可知对于小于4K标准I/O,UNIX I/O mmap几乎时间开销差不多。但标准I/O由于存在输入缓冲区,存在一点点优势。由图表(B)可知,对于处理大文件,UNIX I/O 比它两个存在相对的优势。对于mmap方便处理大文件,但在时间效率上远远没有其它两种方式块。

详细代码:

见附录一

 

 

 

 

 

 

 

 

 

 

任务二 文件内容的并行搜索

任务目的:

本任务旨在强化线程编程能力,对并行所带来的性能优势有直观的认识。

任务要求

 

任务要求:

在阅读大型项目代码时,经常要搜索某个标识符,找出该标识符的声明、定义或引用的地方(某文件的哪一行)。本任务要求实现一个程序idfind,其使用格式如下:

idfind [-j n] id dirname

选项-j指定并行线程数目。如果省略该选项,则只启动一个线程。

id表示要查找的标识符,dirname表示项目所在目录。

 

测试要求:

可以用Linux内核版本2.6.0的代码做为测试对象。

要进行性能对比的话,程序idfind最好在具有超线程或者多核能力的机器上进行测试,请根据实际情况调整并行线程数目并记录结果,最后给出结论。

 

设计思路:

本任务要求搜索目录下所有文件的内容,显然我们需要通过API来获取本目录中的文件名,从而通过得到的文件名来读取该文件,进而在文件中搜索标识符。但是,目录中有可能有子目录,按照任务要求,子目录下的文件同样需要进行搜索,而子目录又有可能有子目录。

显然这是一个典型的递归问题。

这里在主线程中使用了一个递归函数:

void travel_file(void);

该函数会遍历整个目录,如果当前搜索到的是一个文件,如果线程数量(信号量)没有达到上限值,则创建一个线程,并把该文件名传递给空闲的线程,如果线程数达到上限值,则主线程则必须等待直到某个线程结束。

这里便涉及到线程同步的问题,通常处理Posix线程同步可采用Posix信号量、互斥对象或条件对象。在本程序中采用的是一个Posix信号量来处理线程同步,因为使用Posix信号量简单、方便,非常容易编程实现。采用一个互斥信号量来处理文件名的复制,并且保证创建的线程先运行,并保证文件名的正确复制。

子线程只需从主线程中获取主线程travel_file()中所得到的路径名,所以这里使用了一个名为current_thread信号量,顾名思义,主线程遍历到一个新的文件,检测信号量的值,如果小于最大值,则主线程创建一个子线程,启动子线程开始当前文件的搜索任务的时候,搜索完成后,便释放这个信号量,,由于存储路径名的是一个全局变量,所以子线程必须将travel_file()所得到的正则文件路径拷贝到自身的私有空间中,为了防止线程对该变量的竞争,还必须设置一个copied互斥量,拷贝之前至拷贝结束期间都必须阻塞该信号量。

 

详细设计:

主线程代码如下:

void travel_file(void)

{

if (S_ISREG(statbuf.st_mode))

         {

                   if (sem_wait(&current_thread)<0)

                   …

                   pthread_mutex_lock(&mutex);

                   if (pthread_create(&tid,NULL,compare,(void*)pathname)!=0)

                   …

                   pthread_mutex_lock(&mutex);

                   pthread_mutex_unlock(&mutex);

         }

         else

         if(S_ISDIR(statbuf.st_mode))

         {

                   …

                   while((dirent=readdir(dir))!=NULL)

                   {

                            if ((strcmp(dirent->d_name,".")==0)||(strcmp(dirent->d_name,"..")==0))

                                     continue ;

 

                            strcat(pathname,"/");strcat(pathname,dirent->d_name);

                            travel_file();

                            pathname[strlen(pathname)-strlen(dirent->d_name)-1] ='/0';

                   }

                   …

         }

}

递归函数中if (S_ISREG(statbuf.st_mode))…用于判断当前文件的类型。如果是文件的,if (sem_wait(&current_thread)<0)   … 用于信号量的可用资料,如果有资源可用使用,则if (pthread_create(&tid,NULL,compare,(void*)pathname)!=0)  …创建一个新的线程,pthread_mutex_lock(&mutex);用于保证文件名目录的正确复制。如果是文件夹,while((dirent=readdir(dir))!=NULL)… travel_file() readdir(dir)得到当前目录下的文件,并进行递归。知道当前目录下的所有文件都被遍历。

 

子线程:

void *compare(void *args)

{

         …

         strcpy(fp,(char*)args);

         pthread_mutex_unlock(&mutex);

                   …

         while(fgets(buf,512-1,src)!=NULL)

         {

                   …

                   if (strstr(buf,id)!=NULL)

                   {

                            total++;

                            printf("%s:line %d/t%s",fp,line,buf);

                   }

         }

         …

         if (sem_post(&current_thread)<0)…

         pthread_detach(pthread_self());

         return NULL;

}

子线程得到文件路径名,复制到自己私有空间后。就释放信号量。子线程同过文件路径打开文件while(fgets(buf,512-1,src)!=NULL)…依次读取文件中的一行,并判断文件中是否有指定的内容。直到文件尾部。if (sem_post(&current_thread)<0)之后线程释放信号量,pthread_detach(pthread_self());并回收线程资源。

子线程如何判断主线程travel_file()文件遍历结束?这里在while(value !=thread_num)

sem_getvalue(&current_thread,&value);主线程在结束前,重复得到信号量的值,如果资源都可用则可保证,所有子线程都已结束,那么主线程可正常退出,否则等待。

 

调试分析及测试结果:

在给出测试结果之前,必须说明测试时使用的环境。

CPUIntel Core2 T2050 (1.66GHz,双核)

内存:2*512M DDR2 667MHz)

操作系统:Ubuntu 8.10, Kernel:2.6.27.13

编译器:gcc 4.3.2

 

由于本程序设计多次测试,下面是程序的一次执行结果:

kerson@kerson-laptop:~/ext/test4$ ./idfind get_base linux-2.6.27.2/

linux-2.6.27.2/arch/x86/pci/mmconfig_32.c:line 28   static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)

linux-2.6.27.2/arch/x86/pci/mmconfig_32.c:line 71             base = get_base_addr(seg, bus, devfn);

linux-2.6.27.2/arch/x86/pci/mmconfig_32.c:line 104           base = get_base_addr(seg, bus, devfn);

linux-2.6.27.2/arch/x86/kvm/lapic.h:line 34      u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);

linux-2.6.27.2/arch/x86/kvm/lapic.c:line 868     u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu)

linux-2.6.27.2/arch/x86/kvm/lapic.c:line 872     EXPORT_SYMBOL_GPL(kvm_lapic_get_base);

linux-2.6.27.2/arch/powerpc/platforms/cell/beat_syscall.h:line 110        #define HV_get_base_clock __BEAT_ADD_VENDOR_ID(0x111, 1)

linux-2.6.27.2/net/sched/em_cmp.c:line 28                 unsigned char *ptr = tcf_get_base_ptr(skb, cmp->layer) + cmp->off;

linux-2.6.27.2/net/sched/em_nbyte.c:line 47               unsigned char *ptr = tcf_get_base_ptr(skb, nbyte->hdr.layer);

linux-2.6.27.2/net/sched/em_text.c:line 39                  from = tcf_get_base_ptr(skb, tm->from_layer) - skb->data;

linux-2.6.27.2/net/sched/em_text.c:line 42                  to = tcf_get_base_ptr(skb, tm->to_layer) - skb->data;

linux-2.6.27.2/net/key/af_key.c:line 2705 static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp)

linux-2.6.27.2/net/key/af_key.c:line 3455          hdr = pfkey_get_base_msg(skb, &err);

linux-2.6.27.2/drivers/ide/pci/sis5513.c:line 188         static u8 sis_ata133_get_base(ide_drive_t *drive)

linux-2.6.27.2/drivers/ide/pci/sis5513.c:line 248                  u8 drive_pci = sis_ata133_get_base(drive), clk, idx;

linux-2.6.27.2/drivers/ide/pci/sis5513.c:line 302                  u8 drive_pci = sis_ata133_get_base(drive), clk, idx;

linux-2.6.27.2/drivers/ide/pci/sis5513.c:line 354                  u8 drive_pci = sis_ata133_get_base(drive);

linux-2.6.27.2/drivers/watchdog/ibmasr.c:line 142     static int __init asr_get_base_address(void)

linux-2.6.27.2/drivers/watchdog/ibmasr.c:line 392              rc = asr_get_base_address();

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_kernel.h:line 315                  int (*ipath_f_get_base_info)(struct ipath_portdata *, void *);

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_file_ops.c:line 86        static int ipath_get_base_info(struct file *fp,

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_file_ops.c:line 125                ret = dd->ipath_f_get_base_info(pd, kinfo);

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_file_ops.c:line 2354                       ret = ipath_get_base_info(

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba7220.c:line 1897    * ipath_init_7220_get_base_info - set chip-specific flags for user code

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba7220.c:line 1904    static int ipath_7220_get_base_info(struct ipath_portdata *pd, void *kbase)

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba7220.c:line 2544              dd->ipath_f_get_base_info = ipath_7220_get_base_info;

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba6110.c:line 1670    * ipath_init_ht_get_base_info - set chip-specific flags for user code

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba6110.c:line 1677    static int ipath_ht_get_base_info(struct ipath_portdata *pd, void *kbase)

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba6110.c:line 1924              dd->ipath_f_get_base_info = ipath_ht_get_base_info;

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba6120.c:line 1524    * ipath_init_pe_get_base_info - set chip-specific flags for user code

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba6120.c:line 1531    static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase)

linux-2.6.27.2/drivers/infiniband/hw/ipath/ipath_iba6120.c:line 1784              dd->ipath_f_get_base_info = ipath_pe_get_base_info;

linux-2.6.27.2/drivers/char/tpm/tpm_atmel.c:line 191                   if ((iobase = atmel_get_base_addr(&base, &region_size)) == NULL) {

linux-2.6.27.2/drivers/char/tpm/tpm_atmel.h:line 39  static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)

linux-2.6.27.2/drivers/char/tpm/tpm_atmel.h:line 116         static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)

linux-2.6.27.2/drivers/misc/sgi-gru/grufile.c:line 347 static int get_base_irq(void)

linux-2.6.27.2/drivers/misc/sgi-gru/grufile.c:line 365 static int get_base_irq(void)

linux-2.6.27.2/drivers/misc/sgi-gru/grufile.c:line 398          irq = get_base_irq();

linux-2.6.27.2/include/net/pkt_cls.h:line 317     static inline unsigned char * tcf_get_base_ptr(struct sk_buff *skb, int layer)

linux-2.6.27.2/include/asm-cris/arch-v10/system.h:line 34  static inline unsigned long _get_base(char * addr)

linux-2.6.27.2/kernel/timer.c:line 97         static inline struct tvec_base *tbase_get_base(struct tvec_base *base)

linux-2.6.27.2/kernel/timer.c:line 511                         base = tbase_get_base(prelock_base);

linux-2.6.27.2/kernel/timer.c:line 747                         BUG_ON(tbase_get_base(timer->base) != base);

total :43

输出内容中,每行表示一个找到标示符的结果,分三部分。第一部分为标示符所在的文件路径。第二部分为标示符所在文件中的行号,第三部分为标示符在文件中的那一行内容。

 

测试结果数据分析:

为了更好的测试出并行性的优势,本测试采用了O3级优化来编译源程序。

首先我们使用单线程来搜索内核源代码2.6.28.7中的标识符get_base并测试时间:

kerson@kerson-laptop:~/ext/test4$ ./idfind get_base linux-2.6.27.2/

real  1m45.382s

user  0m6.580s

sys   0m7.148s

接着使用双线程测试:

kerson@kerson-laptop:~/ext/test4$ ./idfind –j 2 get_base linux-2.6.27.2/

real  0m3.852s

user  0m3.780s

sys   0m1.296s

双线程比单线程快了27.3倍?是否有误?

再次运行单线程:

kerson@kerson-laptop:~/ext/test4$ ./idfind get_base linux-2.6.27.2/

real  0m4.861s

user  0m3.696s

sys   0m1.156s

这次的测试结果和第一次测试的结果大相径庭,为什么呢?

经过笔者的仔细思考,经过第一次程序运行以后,系统将要进行搜索的文件写入了缓存当中,因此大大减少了之后程序运行的时间。

由于本测试专注于多线程的性能,而不是在于I/O设备的性能,因此我们可以等到运行时间基本稳定的时候开始记录数据。

 

 

 

 

 

 

经过多次测试,得到的结果如下:

线程数

第一次运行时间

第二次运行时间

第三次运行时间

平均时间

1

9.565

9.856

30.867

16.762

2

8.424

7.551

8.419

8.131

4

8.433

8.385

8.545

8.454

8

8.364

8.436

8.444

8.414

16

8.333

7.870

8.484

8.229

32

8.140

8.405

8.362

8.302

64

8.309

8.592

8.098

8.333

128

8.159

8.468

8.370

8.332

256

8.351

8.440

8.385

8.392

结果分析及结论:

从上图可以看出,线程数为2的时候,运行速度最快,性能最高。这是因为本机为双核机器,程序充分利用了CPU的两个处理核心。随着线程数的增多,速度没有提升,反而略有所下降,这是因为处理线程系统是需要付出代价的,但是因为大多线程都是处于阻塞态,而POSIX线程库对此类情况有所优化,所以性能并没有大幅度下降。

运行速度最慢的是单线程,因为它只利用了CPU一个处理核心。

按理说,该程序运行在双核处理器上,双线程应该要比单线程快一倍,由图可知,该结果验证了上面的结论。

详细代码:

见附录二

 

 

 

 

 

 

 

 

 

 

 

任务三 利用元数据操作ext2文件系统

任务要求:

1.了解并熟悉ext2文件系统的结构

2.编制一个文件系统浏览器,该浏览器类似shell,但只能对一个ext2文件系统执行以下操作:

a)      cd: 改变用户当前工作目录。并假定初始目录为ext2文件系统的根目录

b)      ls:列举用户当前目录的所有文件,每项输出格式包括:文件的权限(file permission),文件大小,修改时间和文件名

c)      cat:输出一个文件的内容

d)      exit:退出该程序

 

设计思路:

    对于 ext2 文件系统来说,硬盘分区首先被划分为一个个的 block,这些 blocks 被聚在一起分成几个大的 block group。每个 block group 中有多少个 block 是固定的。

每个 block group 都相对应一个 group descriptor,这些 group descriptor 被聚在一起放在硬盘分区的开头部分,跟在 super block 的后面。所谓 super block,所谓 ext2 文件系统的 super block,就是硬盘分区开头(开头的第一个 byte  byte 0)从 byte 1024 开始往后的一部分数据。由于 block size 最小是 1024 bytes,所以 super block 可能是在 block 1 中(此时 block 的大小正好是 1024 bytes),也可能是在 block 0 中。。在这个 descriptor 当中有几个重要的 block 指针。我们这里所说的 block 指针,就是指硬盘分区上的 block 号数。我们注意到,一个硬盘分区上的 block 计数是从 0 开始的,并且这个计数对于这个硬盘分区来说是全局性质的。在 block group  descriptor 中另一个重要的 block 指针,是指向所谓的 inode table。这个 inode table 就不止一个 block 那么大了。这个 inode table 就是这个 block group 中所聚集到的全部 inode 放在一起形成的。

一个 inode 当中记载的最关键的信息,是这个 inode 中的用户数据存放在什么地方。我们在前面提到,一个 inode 大体上相对应于文件系统中的一个文件,那么用户文件的内容存放在什么地方,这就是一个 inode 要回答的问题。一个 inode 通过提供一系列的 block 指针,来回答这个问题。这些 block 指针指向的 block,里面就存放了用户文件的内容。

那么我们怎么读取文件数据呢。首先要读的,当然是文件系统的根目录。注意,这里所谓的根目录,是相对于这一个文件系统或者说硬盘分区而言的,它并不一定是整个 Linux 操作系统上的根目录。这里的这个 root 目录存放在一个固定的 inode 中,这就是文件系统上的 inode 2。需要提到 inode 计数同 block 计数一样,也是全局性质的。这里需要特别注意的是,inode 计数是从 1 开始的,而前面我们提到过 block 计数是从 0 开始,这个不同在开发程序的时候要特别留心。

那么,我们先来看一下得到一个 inode 号数以后,怎样读取这个 inode 中的用户数据。在 super block 中有一个字段 s_inodes_per_group 记载了每个 block group 中有多少个 inode。用我们得到的 inode 号数除以 s_inodes_per_group,我们就知道了我们要的这个 inode 是在哪一个 block group 里面,这个除法的余数也告诉我们,我们要的这个 inode 是这个 block group 里面的第几个 inode;然后,我们可以先找到这个 block group  group descriptor,从这个 descriptor,我们找到这个 group  inode table,再从 inode table 找到我们要的第几个 inode,再以后,我们就可以开始读取 inode 中的用户数据了。

这个公式是这样的:block_group = (ino - 1) / s_inodes_per_group。这里 ino 就是我们的 inode 号数。而 offset = (ino - 1) % s_inodes_per_group,这个 offset 就指出了我们要的 inode 是这个 block group 里面的第几个 inode

找到这个 inode 之后,我们来具体的看看 inode 是什么样的。

我们看到在 inode 里面可以存放 EXT3_N_BLOCKS= 15)这么多个 block 指针。用户数据就从这些 block 里面获得。15  blocks 不一定放得下全部的用户数据,在这里 ext3 文件系统采取了一种分层的结构。这组 15  block 指针的前 12 个是所谓的 direct blocks,里面直接存放的就是用户数据。第 13  block,也就是所谓的 indirect block,里面存放的全部是 block 指针,这些 block 指针指向的 block 才被用来存放用户数据。第 14  block 是所谓的 double indirect block,里面存放的全是 block 指针,这些 block 指针指向的 block 也被全部用来存放 block 指针,而这些 block 指针指向的 block,才被用来存放用户数据。第 15  block 是所谓的 triple indirect block,比上面说的 double indirect block 有多了一层 block 指针。作为练习,读者可以计算一下,这样的分层结构可以使一个 inode 中最多存放多少字节的用户数据。(计算所需的信息是否已经足够?还缺少哪一个关键数据?)

一个 inode 里面实际有多少个 block,这是由 inode 字段 i_size 再通过计算得到的。i_size 记录的是文件或者目录的实际大小,用它的值除以 block 的大小,就可以得出这个 inode 一共占有几个 block

 

详细设计:

1.本任务中,最重要的一点是如何通过文件夹inode号判断当前目录下是否存在对应的文件或目录。由于我们都知道,要想找到文件或目录,必须找到文件或目录的inode号,由于根目录的inode号一般都为2,从根目录出发读取inode结构中的相关成员可以读取目录下的文件或文件的内容。

2.本程序根据传入的参数,将ext2文件系统名传递到程序中,得到ext2文件系统所在的文件,并打开该文件,将该文件全部映射到内容。已方便文件内容的读取。

3.主程序,首先执行check_ext2fs 函数,首先读取文件系统的超级块信息,并将将数据存储在全局变量ext2_super_block中,并判断该文件是否为ext2文件系统。如果不是则给出提示信息并退出。

3.之后接收用户输入该函数实现在shell中,通过标准输入流,每次读取一行数据,去除多余的空格和换行符,将参数进行分析,以便为后面的程序。然后通过字符串的比较判断是那条命令,并调用相应的处理函数,这里是通过函数数组指针实现的。

4.cd命令:该命令带一个参数,也就是你也进入的目录名。对于直接回车或者输入“/”的情况,则默认返回当前工作目录为根目录,当输入'“..”时,则改变目录为当前目录的上一级目录,之后对于用户输入的目录路径,通过sys_cd函数。该函数调用ext2_dir_entry函数获取cur_inode 下的所有文件信息。通过比较该文件信息,则可知道该文件是否在该目录下,如果存在则改变当前工作路径,并改变cur_inode标志。否则,则返回出错信息。

5.ls命令:该命令实现不带任何参数。实现函数为sys_ls ,该函数也是调用ext2_dir_enty函数取cur_inode 下的所用文件信息。通过get_mode(),get_time处理返回的每个文件信息,后排版整理输出该目录下所有文件信息。

6.cat命令:该函数带一个参数,你需要cat的文件名。该函数实现为sys_cat, 同样该函数调用ext2_dir_entry函数获取cur_inode 下的所有文件信息。通过比较该文件信息,则可知道该文件是否在该目录下,如果存在则将该文件inode号传入inode_get_ext2_inode()函数中,获得该文件更多的信息。通过该ext2_inode 结构传入ext2_inode_get_data()获取该文件的内容到_buf缓冲区中,输出该缓冲区内容,即为该文件的内容。

程序代码及相关注释请见附录!

 

调试分析及测试结果:

在给出测试结果之前,必须说明测试时使用的环境。

CPUIntel Core2 T2050 (1.66GHz,双核)

内存:2*512M DDR2 667MHz)

操作系统:Ubuntu 8.10, Kernel:2.6.27.13

编译器:gcc 4.3.2

首先使用使用dd if=/dev/zero of=fs bs=1M count=64 建立一个文件大小为64M的空白文件,做文件系统文件。之后使用$/sbin/mke2fs fs在该空白文件中建立ext2fs文件系统。使用sudo mount –o loo fs mnt 挂接该文件系统,之后再mnt 目录中放入若干文件.sudo umount mnt卸载该文件系统后,即可开始改程序的调试和运行。

下面是程序的结果:

kerson@kerson-laptop:~/ext/test6$ ./ext2fs fs

/$ cd work

/work$ ls

22 drwxrwxr-x  500   500   1024       Fri Mar 12 16:51:46 2010   .

2  drwxr-xr-x  1000  1000  1024       Sun Mar 14 21:10:12 2010   ..

23 -rw-rw-r--  500   500   146         Fri Mar 12 16:51:46 2010   Makefile.am

24 -rw-r--r--  500   500   18009        Fri Mar 12 16:51:46 2010   COPYING

25 -rw-rw-r--  500   500   6970        Fri Mar 12 16:51:46 2010   INSTALL

26 -rw-rw-r--  500   500   0           Fri Mar 12 16:51:46 2010   TODO

27 -rwxrw-r--  500   500   6315        Fri Mar 12 16:51:46 2010   install-sh

28 -rw-rw-r--  500   500   0           Fri Mar 12 16:51:46 2010   NEWS

29 -rw-rw-r--  500   500   1824        Fri Mar 12 16:51:46 2010   work.kdevelop.pcs

30 -rw-rw-r--  500   500   61          Fri Mar 12 16:51:46 2010   Makefile.cvs

31 -rw-rw-r--  500   500   182321      Fri Mar 12 16:51:46 2010   ltmain.sh

32 -rw-rw-r--  500   500   150         Fri Mar 12 16:51:46 2010   configure.in

33 drwxrwxr-x  500   500   1024       Fri Mar 12 16:51:46 2010   src

37 -rw-rw-r--  500   500   0           Fri Mar 12 16:51:46 2010   ChangeLog

38 -rwxrwxr-x  500   500   690195     Fri Mar 12 16:51:46 2010   configure

39 -rwxrw-r--  500   500   12924       Fri Mar 12 16:51:46 2010   depcomp

40 drwxr-xr-x  500   500   1024        Fri Mar 12 16:51:46 2010   autom4te.cache

46 -rwxrw-r--  500   500   29628       Fri Mar 12 16:51:46 2010   config.sub

47 -rw-rw-r--  500   500   11383       Fri Mar 12 16:51:46 2010   Doxyfile

48 -rwxrw-r--  500   500   1988        Fri Mar 12 16:51:46 2010   mkinstalldirs

49 drwxrwxr-x  500   500   1024       Fri Mar 12 16:51:46 2010   templates

52 -rw-rw-r--  500   500   25          Fri Mar 12 16:51:46 2010   AUTHORS

53 -rw-rw-r--  500   500   1516        Fri Mar 12 16:51:46 2010   work.kdevses

54 -rw-rw-r--  500   500   270285      Fri Mar 12 16:51:46 2010   aclocal.m4

55 drwxrwxr-x  500   500   1024       Fri Mar 12 16:51:46 2010   debug

69 -rw-rw-r--  500   500   1447        Fri Mar 12 16:51:46 2010   config.h.in

70 -rw-rw-r--  500   500   56          Fri Mar 12 16:51:46 2010   hello.c

71 -rwxrw-r--  500   500   39923       Fri Mar 12 16:51:46 2010   config.guess

72 -rw-rw-r--  500   500   5608        Fri Mar 12 16:51:46 2010   work.kdevelop

73 -rw-rw-r--  500   500   19821       Fri Mar 12 16:51:46 2010   Makefile.in

74 -rwxrw-r--  500   500   10270       Fri Mar 12 16:51:46 2010   missing

75 -rw-rw-r--  500   500   0           Fri Mar 12 16:51:46 2010   README

/work$ cat hello.c

#include<stdio.h>

void main()

{                

   printf("hello");

}

/work$ exit

 

通过上面数据可知 cd work 成果的实现了进入work目录。而ls列出当前目录下所有的文件。实现有权限都有相应的实现。cat hello.c 打印出hello.c文件的内容。基本上都实现该任务的要求。

 

详细代码:

见附录三

 

本次课程设计我未能将全部的任务都实现。但我每个都有真人的思考,有设计想法。

学校做了不足两个星期,感觉时间也很紧迫,虽然那几天我们基本上每天都泡在里面,但还是不够,三个程序都有很多缺陷来不及完善。

当我们困惑时、无从下手时,能从网上找到很多相关的帮助和代码,但看代码是需要一定的时间和耐心的。我几乎每天都忙到深夜,有时候睡觉也在思考今天调试程序的问题。

此外,在学校有一个很好的交流环境,有很多与我做相同事情却用不同方法的得到不同结果人,大家在迷惘时候,交流一下,不仅能够带来灵感和解决当前问题,而且对知识的理解也会更加深刻。

其实很多的东西不是我们不会,只是我们不够熟练,不晓得怎么用,在我们有思路后,编程就是一种体力劳动,当我们真正动手的时候,才发现所学甚少,已有的知识是还不够用的。

开始做课程设计只是为了完成任务,到后来想把自己的思想用到程序里面去的时候,就好像有了追求一样陷在其中,每每有一点很小的成就,也许在别人眼里只是一个见怪不怪的稀松平常的实现方法,但对自己来说却是从未遇见过,或者说是曾经被自己所忽略掉的内容,又有了说不出的惊喜。

本次课程设计使我对操作系统有了更深入的了解。但使人遗憾的是本人语言表达能力欠佳。仓促完成报告,草草作罢,尚有许多遗漏、缺陷和不成熟之处请老师见谅。

附录一:

/*

 *  stardard.c

 *

 *  (C) 2010 kerson

 */

 

/*

 * This program use stardard I/O  .

 */

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

 

int main(int argc,char *argv[])

{

         FILE *src,*dest;

         char template[]="template-XXXXXX";

 

         if (argc !=3)                        //判断参数是否合理

         {

                   printf("Usage:%s <soucrefile> <copy size>/n",argv[0]);

                   exit(EXIT_FAILURE);

         }

        

         size_t size =atoi(argv[2]);                                   //得到块的大小

         char buffer[size+1];                                            //申请缓冲区

        

         if (mktemp(template)==NULL)                          //创建唯一文件名在当前目录下

         {

                   printf("create temp file failure!/n");

                   exit(EXIT_FAILURE);

         }

 

         if (((src =fopen(argv[1],"r"))==NULL)||((dest =fopen(template,"w"))==NULL))

         {        //打开输入输出文件

                   printf("open file failure!/n");

                   remove(template);

                   exit(EXIT_FAILURE);

         }

         fseek(src,-size,SEEK_END);                                                 //偏移文件指针到末尾

         do

         {

                   if (fread(buffer,1,size,src)<size)                                   //读取一个文件块

                   {

                            printf("read file failure!/n");

                            remove(template);

                            exit(EXIT_FAILURE);

                   }       

                  

                   size_t i;

                   char tmp;

                   for(i =0; i<size/2; i++)                                                 //在缓冲区中置反数据内容

                   {

                            tmp         = buffer[i];

                            buffer[i]     = buffer[size-i-1];

                            buffer[size-i-1] = tmp;

                   }

                  

                   if (fwrite(buffer,1,size,dest)<size)                                 //写入输出文件

                   {

                            printf("write file failure!/n");

                            remove(template);

                            exit(EXIT_FAILURE);

                   }

         }while(fseek(src,-size*2,SEEK_CUR) ==0);                         //移动块,直到文件结束

                 

         fclose(src);fclose(dest);                                                         //关闭输入输出文件

         if (rename(template,argv[1]) !=0)                                         //重命名该文件

         {

                   printf("remove old file failure/n");

                   remove(template);

                   exit(EXIT_FAILURE);

         }

        

         return EXIT_SUCCESS;

}

 

 

                  

 

 

 

 

/*

 *  unix.c

 *

 *  (C) 2010 kerson

 */

 

/*

 * This program use UNIX I/O  .

 */

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

 

int main(int argc,char *argv[])

{

         int src,dest;

         char template[]="template-XXXXXX";

 

         if (argc !=3)                                                                //判断参数是否合理

         {

                   printf("Usage:%s <soucrefile> <copy size>/n",argv[0]);

                   exit(EXIT_FAILURE);

         }

        

         size_t size =atoi(argv[2]);                                  //得到块的大小

         char buffer[size+1];                                           //申请缓冲区

        

         if (((src =open(argv[1],O_RDONLY))<0)||((dest =mkstemp(template))<0))

         {        //打开输入文件,创建唯一文件名并打开作为输出

                   printf("open file failure!/n");

                   exit(EXIT_FAILURE);

         }

        

         lseek(src,-size,SEEK_END);                              //偏移文件指针到末尾

         do

         {

                   if (read(src,buffer,size)<size)                             //读取一个文件块

                   {

                            printf("read file failure!/n");

                            remove(template);

                            exit(EXIT_FAILURE);

                   }       

                  

                   size_t i;

                   char tmp;

                   for(i =0; i<size/2; i++)                              //在缓冲区中置反数据内容

                   {

                            tmp         = buffer[i];

                            buffer[i]     = buffer[size-i-1];

                            buffer[size-i-1] = tmp;

                   }

                  

                   if (write(dest,buffer,size)<size)                 //写入输出文件

                   {

                            printf("write file failure!/n");

                            remove(template);

                            exit(EXIT_FAILURE);

                   }

         }while(lseek(src,-size*2,SEEK_CUR) !=-1);     //移动块,直到文件结束

        

         close(src);close(dest);                                         //关闭输入输出文件

         if (rename(template,argv[1]) !=0)                     //重命名该文件

         {

                   printf("remove old file failure/n");

                   remove(template);

                   exit(EXIT_FAILURE);

         }

        

         return EXIT_SUCCESS;

}

 

 

                  

 

 

 

 

 

 

 

 

 

 

 

 

/*

 *  mmap.c

 *

 *  (C) 2010 kerson

 */

/*

 * This program use file mmap  .

 */

 

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <errno.h>

 

int main(int argc,char *argv[])

{

         int src,dest;

         char template[]="template-XXXXXX";

         struct stat statbuf;

         char *s_src,*s_dest;

         if (argc !=3)                                                                          //判断参数是否合理

         {

                   printf("Usage:%s <soucrefile> <copy size>/n",argv[0]);

                   exit(EXIT_FAILURE);

         }

                   size_t size =atoi(argv[2]);                 //得到快的大小,注意只能处理4KB的倍数大小

        

         if (((src =open(argv[1],O_RDONLY))<0)||((dest =mkstemp(template))<0))

         {        //打开输入文件流,在当前目录下创建一个唯一文件名的空文件,并打开

                   printf("open file failure!/n");

                   exit(EXIT_FAILURE);

         }

        

         if (fstat(src,&statbuf)<0)                                                       //的到输入文件的属性

         {

                   printf("fstat source!");

                   remove(template);

                   return 0;

         }

         if (ftruncate(dest,statbuf.st_size)<0)                              //将输出文件截断为输入文件的大小

         {

                   printf("ftruncate failure!/n");

                   remove(template);

                   exit(EXIT_FAILURE);

         }

 

         size_t position=statbuf.st_size;                                                         //得到输入文件的大小

 

         do

         {

                   if ((s_src=mmap(NULL,size,PROT_READ,MAP_SHARED,src,position-size))==MAP_FAILED)

                   {        //映射输入文件每个块到用户空间

                            printf("src mmap failure!/n");

                            remove(template);

                            exit(EXIT_FAILURE);

                   }       

                   if ((s_dest=mmap(NULL,size,PROT_WRITE,MAP_SHARED,dest,statbuf.st_size-position))==MAP_FAILED)

                   {        //映射输出文件到用户空间

                            printf("dest mmap failure!/n");

                            remove(template);

                            exit(EXIT_FAILURE);

                   }

                   size_t count=size;

                   char *psrc=s_src+size-1;                                                        //输入文件指向内存块地址尾部

                   char *pdest=s_dest;                                            

                  

                   while(count--) *pdest++=*psrc--;    

                                      /将输入文件地址内存数据复制到输出文件输出文件内存

                   munmap(s_src,size);                                                              //为输入输出文件解除内存映射

                   munmap(s_dest,size);

                  

         }while(position-=size);                                                           //改变块的的地址

         close(src);close(dest);

         if (rename(template,argv[1]) !=0)                                          //重命名输出文件,并删除输入文件

         {

                   printf("remove old file failure/n");

                   remove(template);

                   exit(EXIT_FAILURE);

         }

         return EXIT_SUCCESS;

}

 

 

附录二:

*

 *  idfind.c

 *

 *  (C) 2010 kerson

 */

 

/*

 * This program .

 */

 

#include <stdio.h>

#include <dirent.h>

#include <sys/stat.h>

#include <string.h>

#include <stdlib.h>

#include <pthread.h>

#include <semaphore.h>

 

char *id;

char pathname[512];

int total =0;

static pthread_mutex_t copied = PTHREAD_MUTEX_INITIALIZER;             //copied 互斥信号量

static sem_t current_thread;

 

void *compare(void *args)                                                                      //子线程文件内部标志比较函数

{

         char fp[512];

 

         strcpy(fp,(char*)args);                                                          //copy 文件路径到子线程私有空间

         pthread_mutex_unlock(&copied);//释放copied信号量,主线程可用改变和子线程并行执行

        

         FILE *src;

 

         if ((src=fopen(fp,"r"))==NULL)

         {

                   printf("fopen error!/n");

                   exit(EXIT_FAILURE);

         }

 

         int line =0;

         char buf[512];

         while(fgets(buf,512-1,src)!=NULL)                                      //每次读取一行数据到buf

         {

                   line++;                                                                         //记录行数

        

                   if (strstr(buf,id)!=NULL)                                             //判断该行中是否存在字串为标示串

                   {

                            total++;

                            printf("%s:line %d/t%s",fp,line,buf);                            //输出找到的标示串详细信息

                   }

         }

         fclose(src);

 

         if (sem_post(&current_thread)<0)                                         //释放信号量

         {

                   printf("sem_post error!/n");

                   exit(EXIT_FAILURE);

         }

 

         pthread_detach(pthread_self());                                            //当前子线程所占用的资源

         return NULL;

}

 

void travel_file(void)                                                                     //主线程递归搜索目录所有文件

{

         struct stat statbuf;

         struct dirent *dirent;

         DIR *dir;

         pthread_t tid;

 

         if (lstat(pathname,&statbuf)<0)                                             //得到文件属性

         {

                   printf("%s lstat error!/n",pathname);

                   exit(EXIT_FAILURE);

         }

 

         if (S_ISREG(statbuf.st_mode))                                             //如果是正则文件

         {

                   if (sem_wait(&current_thread)<0)                               //等待可用的线程

                   {

                            printf("sem_wait error!/n");

                            exit(EXIT_FAILURE);

                   }

 

                   pthread_mutex_lock(&copied);                                   //给路径加锁

                   if (pthread_create(&tid,NULL,compare,(void*)pathname)!=0)      //创建子线程

                   {

                            printf("pthread_create errro!");

                            exit(EXIT_FAILURE);

                   }

                   pthread_mutex_lock(&copied);                                   //该加锁保证子线程在pathname修改

                   pthread_mutex_unlock(&copied);                               //前,完成pathname的复制

         }

         else

         if(S_ISDIR(statbuf.st_mode))                                               //如果该项是目录

         {

                   if ((dir =opendir(pathname)) ==NULL)                       //打开该目录

                   {

                            printf("opendir error!/n");

                            exit(EXIT_FAILURE);

                   }

 

                   while((dirent=readdir(dir))!=NULL)                                     //递归该目录

                   {

                            if ((strcmp(dirent->d_name,".")==0)||(strcmp(dirent->d_name,"..")==0))

                                     continue ;                                                  //如果该目录是. .. 则忽略

 

                            strcat(pathname,"/");strcat(pathname,dirent->d_name);         //修改pathname

                            travel_file();                                                       //递归

                            pathname[strlen(pathname)-strlen(dirent->d_name)-1] ='/0';//改回pathname

                   }

 

                   if (closedir(dir)<0)                                                       //关闭该目录

                   {

                            printf("closedir error!/n");

                            exit(EXIT_FAILURE);

                   }

         }

 

}

 

int main(int argc,char *argv[])

{

         int thread_num;

         char *dirname;

 

         if ((argc!=3 && argc!=5)||((argc ==5)&&(strcmp(argv[1],"-j")!=0)))     

         {        //判断程序参数是否合理

                   printf("Usage :%s [-j n] id dirname/n",argv[0]);

                   return EXIT_FAILURE;

         }

 

         if (argc ==3 )                                                                        //处理参数

         {

                   thread_num =1; id =argv[1];dirname =argv[2];

         }

         else

         {

                            thread_num =atoi(argv[2]);id =argv[3];dirname =argv[4];

         }

        

         if (sem_init(&current_thread,0,thread_num)<0)                   //信号量初始化最大值为线程数

         {

                   printf("sem_init error!/n");

                   return EXIT_FAILURE;

         }

 

         if (dirname[strlen(dirname)-1]=='/') dirname[strlen(dirname)-1]='/0';       //处理输入文件末尾的/

         strcpy(pathname,dirname);

         travel_file();

 

         int value=0;

         while(value !=thread_num)                                                                      //等待所有子线程都结束

                   sem_getvalue(&current_thread,&value);

        

         printf("total :%d/n",total);

        

         return EXIT_SUCCESS;

}

        

 

 

 

 

 

 

 

 

 

 

 

 

 

 

附录三:

*

 *  ext2fs.c

 *

 *  (C) 2010 kerson

 */

 

/*

 * This program .

 */

 

#include <stdio.h>

#include <linux/ext2_fs.h>

#include <stdlib.h>

#include <sys/mman.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <string.h>

#include <time.h>

 

#define LOADER_SIZE 1024                                                                 //bootloader 所占字节数

#define SUPPER_BLOCK_SIZE 1                                                //supper_block 所在块号

#define command_num 4                                                               //命令的个数

#define _buf_size 1024*10                                                            //缓冲区的字节大小

 

struct ext2_super_block *ext2_super_block;                                  //supper_block

char *file_addr,_buf[_buf_size];                                                    //文件映射用户空间地址,申请缓冲区

char _tmp[EXT2_NAME_LEN];                                         

 

__le32 block_size;

__le32 cur_inode =EXT2_ROOT_INO;                                         //根节点

 

char command_line[128]="/";                                                                 //当前命令提示符

 

void sys_cd  (char *dir);                                                               //cd命令处理函数

void sys_ls  (char *arg);                                                                        

void sys_cat (char *file);                                                                         

void sys_exit(char *arg);

 

char call_name[command_num][EXT2_NAME_LEN]={"cd",  "ls",  "cat",  "exit"};

void (*call[command_num])(char *)         ={sys_cd,sys_ls,sys_cat,sys_exit}; //指向函数数组的指针

 

char *copyname(char name[],__u8 name_len)                               //从内存地址中拷贝文件名或数据

{

         strncpy(_tmp,name,name_len);

         _tmp[name_len]='/0';

 

         return _tmp;

}

 

char *memory_map(char *file)                                                      //将文件映射到用户空间

{

         int src;

 

         if ((src=open(file,O_RDONLY))<0)

         {

                   printf("open file failure!/n");

                   exit(EXIT_FAILURE);

         }

 

         struct stat statbuf;

         if (fstat(src,&statbuf)<0)

         {

                   printf("fstat source failure!/n");

                   exit(EXIT_FAILURE);

         }

 

         return mmap(0,statbuf.st_size,PROT_READ,MAP_PRIVATE,src,0);     

}

 

int check_ext2fs(char *file_addr)    //检查该文件系统是否为ext2文件系统,并填充supper_block

{

         ext2_super_block=(struct ext2_super_block*)(file_addr+LOADER_SIZE);//填充supper_block

 

         block_size = EXT2_BLOCK_SIZE(ext2_super_block);       //块的大小

        

         return ext2_super_block->s_magic!=0xEF53 ?1:0;               //返回该文件系统是否为ext2系统

}

 

struct ext2_inode* inode_get_ext2_inode(__le32 inode)      //inode 号到该文件的ext2_inode结果

{

         __le32 block_group = (inode-1)/ext2_super_block->s_inodes_per_group;      //inode 所在组号

         __le32 offset           = (inode-1)%ext2_super_block->s_inodes_per_group;   //inode 所在组内偏移

        

         struct ext2_group_desc *ext2_group_desc=(struct ext2_group_desc *)(file_addr + /

                              (ext2_super_block->s_first_data_block+SUPPER_BLOCK_SIZE)*block_size+sizeof(struct ext2_group_desc)*block_group);                        //填充ext2_group_desc 结构

        

         return (struct ext2_inode *)(file_addr+ext2_group_desc->bg_inode_table*block_size+sizeof(struct ext2_inode)*offset);                                                //返回ext2_inode的结构指针

}

 

char *copy_block(char *offset,__le32 block_num)     //拷贝制定数据块

{

         memcpy(offset,file_addr+block_num*block_size,block_size);

 

         return offset+block_size;

}

 

char *ext2_inode_get_data(struct ext2_inode *ext2_inode)

{        //根据指定ext2_inode 得到文件数据到_buf缓冲区中,由于缩减了程序复杂度,体现出本任务的中心,只处理了前面12个直接数据块。

         __le32 i =-1;

 

         __le32 file_block_count =ext2_inode->i_size ==0? 0: (ext2_inode->i_size-1)/block_size+1;

         //判断该文件大小

         _buf[0]='/0';

 

         char *offset =_buf;

         while(++i<file_block_count)

                   offset=copy_block(offset,ext2_inode->i_block[i]);               //依次读取文件块

        

         return _buf;

}

 

struct ext2_dir_entry_2 * get_dir(struct ext2_dir_entry_2 *ext2_dir_entry_current,struct ext2_inode *ext2_inode)//从文件夹内容中,的到所有的文件。

{

         static struct ext2_dir_entry_2 *ext2_dir_entry_start_addr;

        

         struct  ext2_dir_entry_2 *ext2_dir_entry_next  = ext2_dir_entry_current ==NULL  /

         ?ext2_dir_entry_start_addr=(struct ext2_dir_entry_2 *)ext2_inode_get_data(ext2_inode)

         :(struct ext2_dir_entry_2 *)((char *)ext2_dir_entry_current+ext2_dir_entry_current->rec_len);

         //从上次文件结构中,得到下个文件的相关信息。

         return (((char*)ext2_dir_entry_next-(char*)ext2_dir_entry_start_addr)>(ext2_inode->i_size-1))||((ext2_dir_entry_next->inode==0)||(ext2_dir_entry_next->rec_len==0))      

                                     ?NULL:ext2_dir_entry_next; //返回下个文件的ext2_dir_entry指针

}

char *get_time(time_t time)                                //处理文件属性中的日期

{

         ctime_r(&time,_tmp);

        

         _tmp[strlen(_tmp)-1] ='/0';

        

         return _tmp;

}

 

char *get_mode(__le16 mode)                                    //处理文件属性中的权限

{

         strcpy(_tmp,"----------");

 

         if (S_ISDIR(mode)) _tmp[0]='d';

 

         if (S_IRUSR &mode) _tmp[1]='r';

         if (S_IWUSR &mode) _tmp[2]='w';

         if (S_IXUSR &mode) _tmp[3]='x';

 

         if (S_IRGRP &mode) _tmp[4]='r';

         if (S_IWGRP &mode) _tmp[5]='w';

         if (S_IXGRP &mode) _tmp[6]='x';

 

         if (S_IROTH &mode) _tmp[7]='r';

         if (S_IWOTH &mode) _tmp[8]='w';

         if (S_IXOTH &mode) _tmp[9]='x';

 

         return _tmp;

}

 

void sys_ls(char *command)                     //ls命令

{

         struct ext2_inode *ext2_inode,*ext2_inode_cur = inode_get_ext2_inode(cur_inode);

                   //得到当前目录的inode

         char arg[block_size];

         if (sscanf(command,"%s",arg)!=EOF)                //处理参数

         {

                   printf("ls: unrecognized operand %s/nUsage : ls/n",arg);

                   return ;

         }

//依次得到该目录下的

         struct ext2_dir_entry_2* ext2_dir_entry=get_dir(NULL,ext2_inode_cur);       文件

         do

         {

                   ext2_inode = inode_get_ext2_inode(ext2_dir_entry->inode);//得到目录下文件的inode 

                 

                   printf("%-2u %s  %-4u  %-4u  %-8u   ",ext2_dir_entry->inode,get_mode(ext2_inode->i_mode),ext2_inode->i_uid,ext2_inode->i_gid,ext2_inode->i_size);

                   printf("%s   ",get_time(ext2_inode->i_ctime));

                   printf("%s/n",copyname(ext2_dir_entry->name,ext2_dir_entry->name_len));

                  //输出处理后的结果

         }while((ext2_dir_entry=get_dir(ext2_dir_entry,ext2_inode_cur))!=NULL);

}                 //处理直到整个目录遍历完毕

 

void change_command_line(char *dir,__le32 old_inode,__le32 new_inode)

{        //根据inode号改变提示符下的目录名

         if (old_inode == new_inode ) return ;

 

         if (strcmp(dir,"..")==0)                   //如果命令为cd ..则返回上层目录

         {

                   size_t len=strlen(command_line);

                   while(command_line[--len]!='/')

                            ;

                   command_line[len]='/0';

         }else if (old_inode ==EXT2_ROOT_INO) strcat (command_line,dir);

                   else strcat(strcat(command_line,"/"),dir);            //否则,添加上进入的目录名

        

         if (new_inode ==EXT2_ROOT_INO) strcpy(command_line,"/"); 

}

 

void sys_cd(char *command)                                      //cd命令

{

         char dir[block_size];

        

         struct ext2_inode *ext2_inode_cur =inode_get_ext2_inode(cur_inode);

                  //得到当前名录的ext2_inode        结构

__le32 old_inode =cur_inode;

        

         if (sscanf(command,"%s",dir)==EOF)   //分离参数

         {

                   cur_inode =EXT2_ROOT_INO;

                   change_command_line(dir,old_inode,cur_inode);

                   return ;

         }

 

         struct ext2_dir_entry_2* ext2_dir_entry=get_dir(NULL,ext2_inode_cur);  //遍历目录下文件

         do

         {

                   if (strcmp(dir,copyname(ext2_dir_entry->name,ext2_dir_entry->name_len))==0 &&(ext2_dir_entry->file_type==EXT2_FT_DIR))  break;      //存在该文件

        

         }while((ext2_dir_entry=get_dir(ext2_dir_entry,ext2_inode_cur))!=NULL);

 

         if (ext2_dir_entry!=NULL)                                        //存在该文件

         {

                   cur_inode =ext2_dir_entry->inode;                    //cur_inode为当前目录号

                   change_command_line(dir,old_inode,cur_inode);       //改变提示符

         }       

         else printf("%s: No such file or directory/n",dir);                  //输出出错信息

}

 

void sys_cat(char *command)                            //cat命令

{

         char file[block_size];                       //文件名缓存器

        

         struct ext2_inode *ext2_inode_cur =inode_get_ext2_inode(cur_inode);

        

         if (sscanf(command,"%s",file)==EOF)

         {

                   printf("Usage : cat <file>/n");

                   return ;

         }

        

         struct ext2_dir_entry_2* ext2_dir_entry=get_dir(NULL,ext2_inode_cur);

         do

         {

                   if(strcmp(file,copyname(ext2_dir_entry->name,ext2_dir_entry->name_len))==0&& ext2_dir_entry->file_type == EXT2_FT_REG_FILE) break;

        

         }while((ext2_dir_entry=get_dir(ext2_dir_entry,ext2_inode_cur))!=NULL);

        

         if (ext2_dir_entry!=NULL)

         {       

                   printf("%s",ext2_inode_get_data(inode_get_ext2_inode(ext2_dir_entry->inode)));

         }        //如果存在该文件,则得到该文件内容,并打印出来

         else printf("%s: No such file or directory/n",file);

}

 

void sys_exit(char *arg)                                              exit命令

{

         exit(EXIT_SUCCESS);

}

 

void shell(void)                                         //shell用户交互函数

{

         int i;

         char line[block_size],                       //一行命令

command[block_size],                     //分离命令

arg[block_size];                               //分离参数

 

         while(1)

         {

                   printf("%s$ ",command_line);

                  

                   fgets(line,block_size-1,stdin);

                  

                   if (sscanf(line,"%s",command)!=EOF)                //得到命令

                   {

                            for (i=0;i<command_num;i++)

                                     if (strcmp(command,copyname(call_name[i],strlen(call_name[i])))==0)

                                     {        //匹配命令,找到相应处理函数

                                               (*call[i])(line+strlen(command));

                                               break;

                                     }

        

                            if (i==command_num)  //不存在,输出出错信息

                            printf("%s: command not found/n",command);

                   }

         }

}

 

int main(int argc,char *argv[])

{

         if (argc !=2)                                              //判断参数是否合法

         {

                   printf("Usage:%s <filesystem>/n",argv[0]);

                   exit(EXIT_FAILURE);

         }

 

         if ((file_addr=memory_map(argv[1]))==MAP_FAILED)     //内存映射

         {

                   printf("mmap source failure!/n");

                   exit(EXIT_FAILURE);

         }

 

         if (check_ext2fs(file_addr))                      //检查是否为ext2fs

         {

                   printf("%s is not ext2 filesystem./n",argv[1]);

                   exit(EXIT_FAILURE);

         }

 

         shell();                                                                //用户shell交互

        

         return 0;

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值