CSK.Blog--个人原创Weblog

关于linux kernel课程中semaphore计数作业

好久没写blog了啊,来篇技术点的

最近一直忙着做linux kernel的作业,难度到是不大,就是每次重新编译内核要30分钟,实在吃不消。

刚做好一个task.要求是这样的:

    • Add wait_times, wait_duration, 2 new data members into definition of data struct semaphore; Every time to wait(Sleep) for every lock being free, increase wait_times.After getting the lock, calc the wait duration and add itinto wait_duration;

    • Add use_times, use_duration, 2 new data members into definition of data struct semaphore; Every time after getting the sema, inc use_times. After releasing the sema, calc the utilization duration and add it into use_duration;
    • Use tsc to calc time;

    • Add function register_sema(struct *semaphore): Add sema_perf_list, a global var and a list_head to record all sema registered by register_sema;

    • choose a list of sema and register them before using them;

    • Export the 4 data and sema address of the registered sema list by proc;

最终要求作出的效果是在proc下导出文件semadata,显示如下内容:

#cat /proc/semadata
0xa0000000 1001 56890 2002 89765
0xa000ff00 401 6890 888 9765

-----------------------------------------

下面是我的解题思路和解决办法

关于这个问题网上也有不少的解法,其中我参考了

http://bbs.ss.pku.edu.cn/ss/index.php/6122/action_viewspace_itemid_9362.html

的办法,不过其中也有不少问题,所以我把我的修正点和不同的做法写出,一些基本的做法就看上文的链接

好了,下面说我的具体做法

1.在semaphore.h中增加4个规定的变量

由于要求用rdtsc指令记录时间,所以采用rdtscll宏,这个宏实质就是wrap了x86的rstsc指令,返回一个64bit的数据记录CPU的clock数

所以我用u64类型(unsigned long long)记录use_duration和wait_duration

1.在struct semaphore中增加:

 unsigned long wait_times;
 u64 wait_duration;
 unsigned long use_times;
 u64  use_duration;

2.然后在struct semaphore中增加增加:

 struct task_struct * structlst[10];
 u64 tsk_start_using[10];

为什么增加这2个成员呢?考虑到同一个semaphore在用一个时刻可以被不同process调用(执行down() ),所以考虑到需要记录不同process使用该semaphore的开始时间。

intel给我们的tips要求的是在task_struct中增加一个u64 start_time[10];

他的思路正好和上面的办法颠倒,是让每个task记住自己使用一个semaphore的开始时间。

同时因为考虑到一个process可以嵌套的调用很多semapore。

比如:

processA:

Down(sema1);

//some codes

Down(sema2);

//some codes

Up(sema2);

Up(sema1);

这样一来必须记录每个semaphore的开始时间。然后用一个int pos来跟踪嵌套调用down()的深度(好比是堆栈指针)

但是这也有个问题(多谢xris提醒)

考虑下面的调用顺序:

processA:

Down(sema1);

//some codes

Up(sema2);

Down(sema2);

//some codes

Up(sema1);

每个semapore是交错调用的,这样用前面stack数据结构来记录肯定失效了

所以我原先的做法是在task_struct中加入下面2个成员:

u64 start_time[10];

struct semaphore * used_sema_handle[10];

这样一来就能解决上面的问题,但是要求每次先从used_sema_handle里面找到对应semaphore的下标,然后在操作上面的start_time数组。

(用10个元素只是近似情况。同时这个办法也有问题,比如同一个semaphore可以连续down();2次,这样这个方法也实效,如果你有更好的办法欢迎告诉我)

那么为什么我最终没有修改task_struct而是在semaphore里面加了2个对应的变量呢?

那只是因为之前这个办法编译没有通过,提示task_struct在semaphore.h中是unreferenced (我还是不太习惯gcc,没办法,汗...),所以只好在struct semahore里面加2个对应的变量,可以起到同样效果

3.在semahore.h中增加下面函数:

static inline void reg_and_settime (struct semaphore *sem, u64 qw_start)
{
 struct task_struct *tsk = current;
 int npos = 0;
 for (;npos < 10;npos++)
 {
  if (sem->structlst[npos] == NULL)
  {
   sem->structlst[npos] =tsk;
   sem->tsk_start_using[npos] = qw_start;
   break;
  }

 }
}
static inline u64 rm_and_rdtime (struct semaphore *sem)
{
 int npos = 0;
 struct task_struct *tsk = current;
 for (;npos < 10; npos ++){

  if (sem->structlst[npos] == tsk)
  {
   sem->structlst[npos] = NULL;
   return sem->tsk_start_using[npos];
  }

 }
 return 0;/* oops~ not found */
}

这2个函数就是操作那对structlst和tsk_start_using成员的,reg_and_settime将当前的task指针记录到structlst,并找到一个位置存放此时的start_time。rm_and_rdtime则将当前task对应的start_time读出,然后把自身记录抹去。具体就不解释了

4.在down,down_interruptible,down_trylock加入下面代码:

 u64 qw_start_time;
 sem->use_times++;
 rdtscll(qw_start_time);
 reg_and_settime(sem,qw_start_time);

5.在up中第一行加入:

 u64 qw_current_time,qw_start_time;
 rdtscll(qw_current_time);
 qw_start_time = rm_and_rdtime(sem);
 if (qw_start_time != 0){  /* ensure the process invoked down before this up*/
  sem->use_duration += qw_current_time- qw_start_time;

 }

6.按照参考文章的办法修改lib/semaphore_sleeper.c。以及为了修改fs/proc/proc_misc.c所作的所有操作

7.在proc_misc.c中,修正参考文章的问题

unregister_sema修改为:

int unregister_sema(struct semaphore *sema)
{
        int ret;
        ret = 0;
        struct list_head *pos;
        struct list_head *next;
        struct list_head *prev;
 struct semaperf_list *psema;
        pos = container_of(sema, struct semaperf_list, sema_list);
       
        __list_for_each(pos, &g_semaperf_head.sema_list)
    {
     psema = container_of(pos, struct semaperf_list, sema_list);
    
     if (psema->semaperf == sema)
     {
  next = pos->next;
         prev = pos->prev;
         __list_del(prev, next);
  
  kfree(psema);
         --g_regist_count;
     }
    }

       return ret;
}

原文的办法,通过container_of宏查找semaphore *所在的struct semaperf_list指针是行不通的,这样只会造成调用该函数访问空地址,造成内核崩溃,当然我的修改也很愚蠢...

get_semaperf_list中的显示函数的打印语句改为:

     length += sprintf(buffer, "0x%x %ld %llu %ld %llu\n", (unsigned int)psema->semaperf,
     psema->semaperf->wait_times,
     psema->semaperf->wait_duration, 
     psema->semaperf->use_times,
     psema->semaperf->use_duration);
     strcat(page, buffer);

这个不用多做解释,use_duration这些是u64的

8.编写module

写module有2个功能

1).通过module创建n个semaphore,然后调用register_sema,以便显示

2).提供能够由ring 3的程序调用的down和up

其中第一个功能很容易实现,第二个很明显就是要用systemcall。但是这是一个module,不可能通过常规手段修改内核代码添加systemcall(除非你把这2个systemcall编译进内核)

我采取的办法是在module启动时候获取IDRT向量表,然后找到system call的int 80表向,最终找到system_call数组表,然后动态替换2个无用system_call实现增加systemcall的手段(对了,就是流氓软件hook system call的办法)

module的代码很长,我把它附在文章的末尾

9.编写测试程序调用module提供的systemcall

下面是我写的程序:

int main(void)
{

 while(1)
 {
  syscall_sema_dw(0); //enter critical region
  usleep(500); //sleep for 500 milliseconds
  syscall_sema_up(0); //leave critical region
  usleep(200);
 }

}

把程序编译为mutex_racer,然后同时执行2个该程序。可以看成使对一个critical_region的访问操作。

其中syscall_sema_dw和syscall_sema_up是module提供的systemcall,参数0表示使用module创建的第一个semaphore

最终编译好这些代码,进入新的内核,就可以看到效果了:

starting sema dump... total: 4
0xf8c3618c 0 0 0 0
0xf8c360e8 0 0 0 0
0xf8c36044 0 0 0 0
0xf8c35fa0 27198 119425837 29922 71715898387
----------------------
 dump completed

因为只使用了1个semaphore,所以前3项都是0

好了,到此任务大功告成,当然你也可以写其他的测试程序,比如模拟PV操作看效果。

最后附上我写的module代码:

/*
 Semaphore debug helper module
 BY CSK
 csk@live.com
*/

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/unistd.h>


#include<asm/uaccess.h> // for copy_to_user
#include<linux/sched.h> // for current macro

#include <linux/sema_helper.h>

/* index 223 and 285 is not used in kernel 2.6.20.3 */
#define __NR_HELPER_SEMA_UP 223

#define __NR_HELPER_SEMA_DW 285

long * systable; /* location to store system_call_table */

static int (*saved_223)(void);
static int (*saved_285)(void);

#define HELPER_SEMA_NUM 4
static struct semaphore pSema_lst[HELPER_SEMA_NUM];

/* init semaphore */

static void init_env()
{
 int pos = 0;
 /* init each sema */
 for (;pos<HELPER_SEMA_NUM ; pos++)
 {
  sema_init(pSema_lst + pos , 1); //mutex-style sema
  memset(pSema_lst[pos].structlst,NULL,10*sizeof(struct task_struct *));
  register_sema(pSema_lst + pos);
 }


}

static void release_env()
{
 int pos = 0;
 for (; pos<HELPER_SEMA_NUM; pos++)
 {
  unregister_sema(pSema_lst + pos );
 }

}

/* new syscall */

asmlinkage int helper_down_sema(int sema_id)
{
//struct timeval ktv;
//do_gettimeofday(&ktv);
//copy_to_user(tv,&ktv,sizeof(ktv));
 down(pSema_lst + sema_id );
// printk(KERN_ALERT"<helper_down_sema>PID %ld called ,with value %d.\n",(long)current->pid,sema_id);
 return 0;
}


asmlinkage int helper_up_sema(int sema_id)
{
//struct timeval ktv;
//do_gettimeofday(&ktv);
//copy_to_user(tv,&ktv,sizeof(ktv));
//printk(KERN_ALERT"<helper_up_sema>PID %ld called ,with value %d.\n",(long)current->pid,sema_id);
 up(pSema_lst + sema_id);
return 0;
}


/* Begin hacking code ;-) */
//IDTR
struct {
    unsigned short limit;
    unsigned int base;
} __attribute__((packed)) idtr;


// IDT
struct {
    unsigned short off1;
    unsigned short sel;
    unsigned char none, flags;
    unsigned short off2;
} __attribute__((packed)) idt;

 

// find the address of sys_call_tb
void disp_sys_call_table(void)
{
    unsigned int sys_call_off;
    unsigned int sys_call_table;
    char* p;
    int i;


    asm("sidt %0":"=m"(idtr));
    printk("addr of idtr: %x\n", &idtr);

  
    memcpy(&idt, idtr.base+8*0x80, sizeof(idt));
    sys_call_off=((idt.off2<<16)|idt.off1);
    printk("addr of idt 0x80: %x\n", sys_call_off);


    p=sys_call_off;
    for (i=0; i<100; i++)
    {
        if (p[i]=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85')
        {
            sys_call_table=*(unsigned int*)(p+i+3);
            systable = (long *)sys_call_table;
      printk("addr of sys_call_table: %x\n", sys_call_table);
            return ;
        }
    }
}


/* end of hacking code ;-) */

 

 

int syscall(void)
{
/* init semaphore data */

init_env();


/* start kernel hacking! */
disp_sys_call_table();
//save old entiry before replace
saved_223=(int(*)(void))(systable[__NR_HELPER_SEMA_UP]);
saved_285=(int(*)(void))(systable[__NR_HELPER_SEMA_DW]);
//ok, replace them
systable[__NR_HELPER_SEMA_UP]=(unsigned long)helper_up_sema;
systable[__NR_HELPER_SEMA_DW]=(unsigned long)helper_down_sema;

return 0;
}

void exit_syscall(void)
{
release_env();
//save back
systable[__NR_HELPER_SEMA_UP]=(unsigned long)saved_223;
systable[__NR_HELPER_SEMA_DW]=(unsigned long)saved_285;
}

module_init(syscall);
module_exit(exit_syscall);

-----------------------

写的虎头蛇尾,如果觉得哪里不正确欢迎指出

CSK 2007-4-22

AW完结,blog暂时解冻

GRE战役的第一部分,analytical writting部分,在4.6日完成了,想想那一月的准备时间,虽然并没有gter上有些人说的“非人的生活”那么夸张,但的确可以使得让我不想再经历第二回。 考试好了原本打算就可以轻松下的,实则不然,回到寝室马上就准备4点的linux kernel的上机作业... 但目前还是对这学期的课云里雾里... 之前花了些时间重做了我网站封页,让他看上去正规些,大家可以去看看,今后就不打算改了。基本上做到2点,1.起到访客导航作用 2.提供一些额外的背景信息。 虽然哪些信息在我flash网站的关于部分写的很详细,但总有不少朋友会说花了不少力气才找到我的email地址这些信息... 这学期看起来不太好过,而且刚在在设计页面时候分明感觉自己已经生疏了不少...

开学了

网站发布了,是时候认真学习奋斗了....

本学期已与3.1开始,想想这个学期任务也不少

先是GRE,还有电子设计大赛(我一个CS学生还搞这个...总觉得自己不是很专)。还有这个学期的各类project:数据库和OS... project还是要好好做,否则要落得上学期编译大作业的下场。

所以,本blog有可能成为我的日记本... 各位想在这看到什么有用信息的朋友可以考虑删除我的链接了(呵呵,应该不会吧;-)

在作flash网站的2年时间里,我一直在考虑的问题就是今后到底要往什么地方发展。觉得自己感兴趣的地方很多,比如破解、图形编程、游戏开发、音频编程、底层的一些开发、网络安全、应用软件的开发、web的技术、Flash还有小时候培养的电子制造爱好。

但想想也真的没有什么可以说是非常精通深入地。每样东西都搞过些,也都做出些不大不小的东西来。想想今后要挑选其中一项舍弃别的真的有点舍不得。

后来考虑的结果是先暂时舍弃毕竟偏重应用的层次,研究底层和偏重理论的东西。因为这些是不容易更新改变的。

所以今后研究的方向是破解、系统内核的研究,图形学理论、ANN的一些方面。

web和flash这类就暂时不考虑了,2年时间,我也有点疲劳了。

前不久花了1晚把一些数据压缩理论、图形学和PE文件相关的文章打印出来整天的看,或许我还是适合搞工程,看理论的东西如果不实践,我就不踏实。以前看到学校CS版上有人说“真正的牛人是不coding的”,看来我还远没达到这层境界。还是踏踏实实做些比较低级的事情吧

闲话就说到这里,这个blog最近可能要冷清下了,是时候闭门修炼了。

当然有什么好玩的或者值得注意的事情我还是可能会出来说些话的,呵呵

闲着无事,添加了些ReformScript的例子

最近情绪低落,一直做不进事情。

所以就修改了下flash网站,主要是增加ReformScript对于事件处理的可能性。效果可以看flash网站的站点日志。

然后用ReformScript写了一个目前还十分十分简单的函数绘图器,同时也作为一个ReformScript例子,具体代码见flash网站试验区。

这个东西很早就酝酿着要做,网站发布时时间很紧,就没写。现在也太晚了,先放出来,大家有兴趣也可以改改完善下。

这个东西强调概念性,效率问题就不要提了,速度肯定是慢的。看下图吧~当然相信你有兴趣会直接进网站看

 

点下面进入Flash站并转到该内容:

http://www.csksoft.net/index_mainsite.asp#SubView%3D3%26SubSection%3Dlab.script%26ViewPos%3D1%26ViewType%3D2

以后的事

Site V2构架的文章恐怕上一篇就是最终话了,其实觉得写不写也没必要。必尽这些东西除了我目前还没实用价值。

接下来是4月的GRE机考。还有Prototype A。

所以基本上要往书堆里钻了。经历2年的知识萧条,要给头脑补充点“营养”

说来有趣,现在都被认为我是美工和作网站设计的了,嗯,这样的主次颠倒可不行...

 

 

Site V2自述、构架和发展,第一部分

=======================================请点击标题察看全文=============================== 文章过长,请点击标题进入浏览 =======================================头部===============================
说在前面:发布好网站,我一直这样对自己说,这只不过是Flash的一个应用,她不是我的目标和追求。也没有任何值得炫耀的资本

网站发布好趁着周末休息了下,现在说说这个网站,作为总结。

作了1年半的东西发布了,自然是很激动,也发布到了一些bbs做了宣传,水源上的反应让我很开心,说明这些劳动还是得到了肯定。后来又发到了相关的flash开发有关的论坛。这里可以得到一些意见。

这里我想说的是大家都太注重网站本身的界面了,固然界面华丽也是我追求的。但要知道我这近2年时间可不是花在设计界面上,我不是学媒设的,没这个天赋。我想这个网站的特点在他的高度灵活性和可扩展性上,尤其是ReformScript机制的引入

所以这里我从最初的构思开始,结合网站自身以及背后被我称为ReformCore的构架来谈我对这个网站的定位和期望。

既然你有耐心看到这里,说明你对我说的还是比较有兴趣的,可以先看看blog上我之前说过的话:

=======================================请点击标题察看全文===============================

Yes,It Released!

经过了将近2年的漫长开发,前前后后复工数次,经过一次大规模修改,期间遇到各类事物阻拦。但是,她终究诞生了!

Flash Site V2,与今天正式发布,开始她的职能。

虽然其中还有不少细节要完善,但目前已经满足发布的所有要求。这里先放上截图和一些说明,当然直接去体验下是最好的了。ReformScript的帮助也有了!

很晚了,先通个风,明天具体介绍网站的构架方面信息。

点击www.csksoft.net 访问

加载画面

首页面,实际画面,1440*900,点击看全图

分版面,实际画面,1440*900,点击看全图

各类工具窗口,实际画面,1440*900,点击看全图

幻灯片察看,实际画面,1440*900,点击看全图

管理后台,实际画面,1440*900,点击看全图

状态汇报

好就没有更新blog了。下面先说下最近在做的事情

2006.12.? - 2007.1.23 考试阶段,何时开始已经不知道了

最近的事情:Flash 网站,PrototypeA 计划

已经拖了很久了,让一切在这个寒假做个了结吧,下一篇文章发布之日,也就是flash site v2发布之时。

plez waiting...

==================

P.S. PrototypeA includes:

PAQ7,8 研究

PE shell研究

汇编优化研究

高等图形算法

音频合成器编写,打算仿造FL4的3x osc

应该能猜到这个plan的内容了,不说下去了

MMX指令优化的32bit AlphaBlend

前一段时间再进行一个目前保密的项目:Prototype A (哈哈,知道的人不要说阿)

其中需要较高的运行效率,所以就写了一个将32bit位图渲染到目标32bit位图的AlphaBlend。支持Alpha通道。

代码指令参照了http://dev.gameres.com/Program/Visual/2D/mmxaddalpha.htm提供的代码,在此表示感谢。由于第一次写汇编优化。所以不知道这样写是不是最高效的。

如果对位深没有要求,可以采用intel提供的16bit的alphablend,网上很多了,这个号称是目前最快的.

参数:

pDest:目标渲染buffer,32bit的,通道情况:ARGB

wlined:pDest的扫描线宽(即横向的像素个数,即实际宽度/4)

hlined:pDest的扫描线行数,可以理解成实际高度

startX:pDest的开始坐标X

startY:pDest的开始坐标Y,采用倒置坐标系

pSrc:要渲染得图片,通道情况:ARGB

wlines,hlines和前面类似

Alpha:对pSrc做得整体alphaBlend,0-255级

返回:

如果图片得到渲染,返回TRUE

用在Prototype A中效果还好,采用GDI渲染一个100*100的图片可以有1000fps*

代码:

BOOL AlphaBlt(BYTE *pDest,DWORD wlined,DWORD hlined,int startX,int startY,BYTE *pSrc,DWORD wlines,DWORD hlines,DWORD Alpha)
{
 int Xd,Yd,Xs=0,Ys=0;
 int loopH,loopW;
 if (startX>=(int)wlined) return FALSE;
 if (startY>=(int)hlined) return FALSE;
 
 if (startX + (int)wlines <=0) return FALSE;
 if (startY + (int)hlines <=0) return FALSE;
 
 Xd = startX;
 Yd = startY;
 loopW = (startX + wlines);
 loopH = (startY + hlines);
 if (loopH>hlined) loopH = hlined;
 if (loopW>wlined) loopW = wlined;
 if (startX<0)
 {
  Xd = 0;
  Xs = -startX;
 }
 if (startY<0)
 {
  Yd = 0;
  Ys = -startY; 
 }
 
 DWORD factorA = hlines-Ys+Yd-1;
 for (DWORD j= Yd ; j< loopH ; j++)
 {
  DWORD dwOffSrc,dwOffDest;
  dwOffSrc = (factorA-j)*wlines;
  
  dwOffDest = (hlined-j-1)*wlined;
  DWORD srcPosX = Xs;
  for (DWORD i = Xd ; i< loopW ; i++)
  {
  
   BYTE *BufSrc = pSrc + (dwOffSrc + srcPosX)*4;
   DWORD dwSrc =((DWORD)BufSrc[3] << 24) |((DWORD)BufSrc[2] << 16) | ((WORD)BufSrc[1] << 8) | (BufSrc[0]);
   DWORD *dwpDest = (DWORD *)pDest + dwOffDest + i;
    __asm{
     pxor mm2,mm2

     mov edx,dwpDest
     movd mm0,[edx]
     movd mm1,dwSrc
     punpcklbw mm0,mm2
     punpcklbw mm1,mm2

     movq mm3,mm1
     punpckhwd mm3,mm3
     punpckhdq mm3,mm3

     movd mm4,Alpha

     punpcklwd mm4,mm4

     punpckldq mm4,mm4

     pmullw mm3,mm4 //Alpha * SrcAlpha
     psrlw mm3,8

     movq mm4,mm0

     movq mm5,mm1

     psubusw mm4,mm1

     psubusw mm5,mm0

     pmullw mm4,mm3

     pmullw mm5,mm3

     psrlw mm4,8

     psrlw mm5,8

     paddusw mm0,mm5

     psubusw mm0,mm4

     packuswb mm0,mm0


     movd [edx],mm0 //保存结果

     emms

   }


   srcPosX++;
  }
 }
 return TRUE;
}

写了个Tiger语言的IDE

编译大作业下周就要检查了,所以最近一直在完善它。不过我没时间去做真正的代码生成了,不过就目前做的东西总觉得太傻了些,所以今天集中精力写了下面这个东西:

功能和特点:
>源代码语法上色
>直接编译
>随意切换编译器
>产生图形化语法树(就是PrettyPrint)

IDE里面包括我写的tiger编译器,所以IDE可以直接运行,点击exec.bat即可
除了tiger编译器,我把IDE的所有原代码都放在一起了。 也可以把你写的编译器用于本IDE,而且不用修改任何代码也不用重新编译,只要把你编译
器的类路径放在本IDE目录下的配置文件里面,详见readme IDE会把Sytem.out.printf这类的输出,即stdout和stderr显示在ide环境里面,好比是cl
和vc ide关系

写的很匆忙,有bug多多包涵~

校内
ftp://Great_csk:public@public.sjtu.edu.cn/public-files/TigerBoxv1_csk.rar
校外:
http://www.csksoft.net/data/legacyftp/Products/APP/TigerBoxv1_csk.rar

分页:[«]11[12][13][14][15][16][17][18][19][20][21][22][23][24][25][»]

日历

<< 2015-6 >>

Sun

Mon

Tue

Wed

Thu

Fri

Sat

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

Copyright Shikai Chen 2000-2012. Powered By Z-Blog(CSK Modified)