Chinaunix首页 | 论坛 | 博客
  • 博客访问: 493554
  • 博文数量: 72
  • 博客积分: 1851
  • 博客等级: 上尉
  • 技术积分: 1464
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-16 17:50
文章分类

全部博文(72)

文章存档

2013年(1)

2012年(17)

2011年(51)

2010年(3)

分类: LINUX

2011-06-14 09:10:35

高级开发(high-level)

 

多线程模式

       如果在多线程运行的情况下,如果处理请求的一个线程出现阻塞但整个系统中所有可用的线程数量超过10时,则系统就会自动关闭线程,如果可用线程为0,系统就会自动创建线程来处理请求,在系统中请求的线程不会由于请求的阻塞而导致线程阻塞后,无线程处理来自文件系统的请求。

 

在运行高级模式开发,通过命令行进行设置(默认开启多线程):

 

  1. -s disable multi-threaded operation

实现的部分代码如下:

 

  1. if (multithreaded)
  2.         res = fuse_loop_mt(fuse);
  3.     else
  4.         res = fuse_loop(fuse);

fuse_loop_mt又实际调用程序fuse_session_loop_mt:

在该程序中,使用的数据结构:

 

  1. //主要用于标识一个线程
  2. struct fuse_worker{
  3. struct fuse_worker *prev;
  4. struct fuse_worker *next;
  5. pthread_t thread_id;
  6. size_t bufsize;
  7. char* buf;
  8. struct fuse_mt *mt;
  9. }
  10. //用于将所有的线程进行串起来
  11. struct fuse_mt{
  12. pthread_mutex_t lock;
  13. int numworker;
  14. int numavail;
  15. struct fuse_session *se;
  16. struct fuse_chan *prevch;
  17. struct fuse_worker main;
  18. sem_t finish;
  19. int exit;
  20. int error;
  21. }

下面就是程序的主体:

 

  1. int fuse_session_loop_mt(struct fuse_session *se)
  2. {
  3.        ….....
  4.     //main函数中调用时,进行对主线程的初始化
  5.           mt.se = se;
  6.      mt.prevch = fuse_session_next_chan(se,NULL);
  7.     mt.error = 0;
  8.     mt.numworker=0;
  9.     mt.numavail     =0;
  10.     mt.main.thread_id = pthread_self();
  11.     mt.main.prev = mt.main.next = &mt.main;
  12.     //创建线程函数,并启动该线程
  13.     pthread_mutex_lock(&mt.lock);
  14.     //线程中调用fuse_do_work来进行读取设备/dev/fuse
  15.     err = fuse_start_thread(&mt);
  16.     pthread_mutex_unlock(&mt.lock);
  17.     //接下来就是对启动的各个worker线程进行设置
  18.     if(!err){
  19.       //阻塞自己,直到用户的session退出,这个操作为初始化中最后一个
  20.      while(!fuse_session_exited(se))
  21.         sem_wait(&mt.finish);
  22.     
  23.      //由于session都退出了,直接要求每个用户的worker线程都退出
  24.       for(w=mt.main.next;w!=&mt.main;w=w->next)
  25.         pthread_cancel(w->thread_id);
  26.      mt.exit=1;
  27.      pthread_mutex_unlock(&mt->lock);
  28.     
  29.       //只留下一个主线程,其他线程都退出
  30.      while(mt.main. &mt.main)
  31.         fuse_join_worker(&mt,mt.main.next);
  32.     }    
  33. }

fuse_do_worker部分实际的工作就是从设备/dev/fuse中读取请求,然后进行分发,具体代码如下:

 

  1. static void * fuse_do_work(void* data)
  2. {
  3.     ….......
  4.     while(!fuse_session_exited(mt->se))
  5.     {
  6.         …..
  7.         pthread_setcancelstate(PTHREAD__CANCEL_ENABLE,NULL);
  8.         //非阻塞读取请求
  9.         res = fuse_chan_recv(&ch,w->buf,w->bufsize);
  10.         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
  11.         //错误处理
  12.         if(res == -EINTR) continue;
  13.         if(res<=0) {
  14.             if(res<0) {
  15.                 fuse_session_exit(mt->se);
  16.                 mt->error = -1;
  17.             }
  18.             break;
  19.         }
  20.         ….......
  21.         //forget请求码的含义是内核告诉文件系统不用将该inode节点进行缓存,文件系统不用回复
  22.         if(((struct fuse_in_header *)w->buf)->opcode == FUSE_FORGET)
  23.              isforget = 1;
  24.         //线程马上就会去分发请求,可能会阻塞,自减
  25.         if(!isforget) mt->numavail--;
  26.         //读取请求的线程为0.需要进行重建
  27.         if(mt->numavail==0)
  28.          fuse_start_thread(mt);
  29.         //请求分发
  30.         pthread_mutex_unlock(&mt->lock);
  31.         fuse_session_process(mt->se,w->buf,res,ch);
  32.         pthread_mutex_lock(&mt->lock);
  33.         //处理完成,重新变为可用
  34.         if(!isforget) mt->numavail++;
  35.         //读取请求线程最多为10,如果大于10,就请求退出,注意这里使用的是可用线程,而非所有线程
  36.         if(mt->numavail>10){
  37.             if(mt->exit){
  38.              pthread_mutex_unlock(&mt->lock);
  39.              return NULL;
  40.             }
  41.             list_del_workr(w);
  42.             mt->numavail--;
  43.             mt->numworker--;
  44.             pthread_mutex_unlock(&mt->lock);
  45.             …......
  46.          }
  47.         }
  48.          pthread_mutex_unlock(&mt->lock);
  49.     }
  50.         sem_post(&mt->finish);
  51.         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
  52.         pause();
  53.         
  54.         return NULL;
  55. }

实验分析

使用系统自带的例子,在测试的文件系统hello.c中,阻塞文件系统中的getattr请求(通过睡眠实现),其它请求正常使用,测试代码如下:

 

  1. static int hello_getattr(const char *path, struct stat *stbuf)
  2. {
  3.         int res = 0;

  4.         memset(stbuf, 0, sizeof(struct stat));
  5.         if (strcmp(path, "/") == 0) {
  6.                 stbuf->st_mode = S_IFDIR | 0755;
  7.                 stbuf->st_nlink = 2;
  8.         } else if (strcmp(path, hello_path) == 0) {
  9.                 stbuf->st_mode = S_IFREG | 0444;
  10.                 stbuf->st_nlink = 1;
  11.                 stbuf->st_size = strlen(hello_str);
  12.         } else
  13.                 res = -ENOENT;

  14. // sem_wait(&finish);
  15.        //这个测试的代码
  16.         sleep(120);
  17.         return res;
  18. }

而其它请求正常进行,进行测试的代码如下:

 

  1. #include <unistd.h>
  2. #include <fcntl.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <sys/stat.h>
  6. #include <sys/types.h>

  7. void main()
  8. {
  9.  int fd,i;
  10.  i=0;
  11.  while(i<10)
  12.  {
  13.   if(0==fork())
  14.   {
  15.      execve("./test.sh",NULL,NULL);
  16.   }
  17.   i++;
  18.  }
  19.  sleep(30);
  20.  printf("send open request");
  21.  while(i<100)
  22.  {
  23.  if((fd=open("/tmp/sshfs",O_RDONLY))<0)
  24.  {
  25.     fprintf(stderr,"open file error!\n");
  26.     exit(-1);
  27.  }
  28.  else printf("open file fd:%d\n",fd);
  29.   i++;
  30.  }
  31. }

./test.sh的代码就是:ls -l /tmp/sshfs

运行结果如下:

 

  1. send open requestopen file fd:3
  2. open file fd:4
  3. open file fd:5
  4. open file fd:6
  5. open file fd:7
  6. open file fd:8
  7. open file fd:9
  8. ....................

查看系统进程:

 

  1. 18506 pts/13 00:00:00 a.out
  2. 18507 pts/13 00:00:00 test.sh
  3. 18508 pts/13 00:00:00 test.sh
  4. 18509 pts/13 00:00:00 test.sh
  5. 18510 pts/13 00:00:00 test.sh
  6. 18511 pts/13 00:00:00 test.sh
  7. 18512 pts/13 00:00:00 test.sh
  8. 18513 pts/13 00:00:00 test.sh
  9. 18514 pts/13 00:00:00 test.sh
  10. 18515 pts/13 00:00:00 test.sh
  11. 18516 pts/13 00:00:00 test.sh

在实验,如果加大请求的次数,挂载的文件系统hello1的线程数量就会增加:

 

  1. UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
  2. ndsl 1968 1 1968 0 13 19:54 ? Ssl 0:00 ./hello1 /tmp/sshf
  3. ndsl 1968 1 1969 0 13 19:54 ? Ssl 0:00 ./hello1 /tmp/sshf
  4. ndsl 1968 1 1970 0 13 19:54 ? Ssl 0:00 ./hello1 /tmp/sshf
  5. ndsl 1968 1 2000 0 13 19:55 ? Ssl 0:00 ./hello1 /tmp/sshf
  6. ndsl 1968 1 2008 0 13 19:55 ? Ssl 0:00 ./hello1 /tmp/sshf
  7. ndsl 1968 1 2010 0 13 19:55 ? Ssl 0:00 ./hello1 /tmp/sshf
  8. ndsl 1968 1 2012 0 13 19:55 ? Ssl 0:00 ./hello1 /tmp/sshf
  9. ndsl 1968 1 2013 0 13 19:55 ? Ssl 0:00 ./hello1 /tmp/sshf
  10. ndsl 1968 1 2014 0 13 19:55 ? Ssl 0:00 ./hello1 /tmp/sshf
  11. ndsl 1968 1 2070 0 13 20:00 ? Ssl 0:00 ./hello1 /tmp/sshf
  12. ndsl 1968 1 2096 0 13 20:01 ? Ssl 0:00 ./hello1 /tmp/sshf
  13. ndsl 1968 1 2177 0 13 20:04 ? Ssl 0:00 ./hello1 /tmp/sshf
  14. ndsl 1968 1 2181 0 13 20:04 ? Ssl 0:00 ./hello1 /tmp/sshf

总的请求只有12个,加上一个主线程,则总的线程就13个,将请求线程加至100个时,hello1的线程数据急剧上升,112(100个阻塞式的ls请求,100个非阻塞open请求),只要线程阻塞就会创建一个新线程。实际计算应该为100个,可能是之前创建的10个线程还没有被主线程进行终止掉。这样就出现了一个很大的缺点:对来自上层的请求,没有进行缓存,而是直接创建一个线程进行处理,会耗费大量的时间,性能就会出现下降。

阅读(5596) | 评论(0) | 转发(0) |
0

上一篇:TCP状态转换图

下一篇:fuse多线程(二)

给主人留下些什么吧!~~