2011年三月

Pthreads并行编程之spin lock与mutex性能对比分析[zz]

原文地址:http://www.parallellabs.com/2010/01/31/pthreads-programming-spin-lock-vs-mutex-performance-analysis/

Pthreads提供的Mutex锁操作相关的API主要有:
pthread_mutex_lock (pthread_mutex_t *mutex);
pthread_mutex_trylock (pthread_mutex_t *mutex);
pthread_mutex_unlock (pthread_mutex_t *mutex);

Pthreads提供的与Spin Lock锁操作相关的API主要有:
pthread_spin_lock (pthread_spinlock_t *lock);
pthread_spin_trylock (pthread_spinlock_t *lock);
pthread_spin_unlock (pthread_spinlock_t *lock);

从实现原理上来讲,Mutex属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在 Core0和Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程 A就会被阻塞(blocking),Core0 会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而Spin lock则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在 Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。

详细信息请点击:这里



linux下跨线程的越界访问hook方法

我们知道在linux下多线程之间,是共用同一片存储区域的,可以访问共享的全局变量等,但是能否访问线程内部的局部变量呢,会不会出现越界异常呢?今天笔者尝试了一下,发现可以在线程间越界访问!

代码如下:

/*
 * Author:yaronli <jidalyg_8711@163.com>
 * http://www.yaronspace.cn/blog
 *
 * File: hookpthread.c
 * Create Date: 2011-03-27 21:22:31
 *
 */
#include <stdio.h>
#include <pthread.h>
void * fun(void * i)
{
    int a=20;
    printf("fun: &a:[%x]-----a:[%d]\n", &a,a);
    sleep(3);
    printf("fun: &a:[%x]-----a:[%d]\n", &a,a);
    pthread_exit(NULL);
 
}
 
void * hook(void * a)
{
    int b=10;
    sleep(1);
    printf("hook: &b:[%x]\n", &b);
    printf("hook: &a:[%d]\n", *(&b-10489856/4));
    *(&b-10489856/4)=30;
    pthread_exit(NULL);
}
int main()
{
    pthread_t pidfun,pidhook;
    pthread_create(&pidfun, NULL, fun, NULL);
    pthread_create(&pidhook, NULL, hook, NULL);
    sleep(5);
    return 0;
}
/* vim: set ts=4 sw=4: */

该程序首先是在启动两个线程,在fun线程中设置局部变量a=20并显示出来,在hook线程中尝试读取a的变量值并试图更改它,然后再次显示变量a的值
在我的机器上的输出如下:

fun: &a:[419c512c]-----a:[20]
hook: &b:[423c612c]
hook: &a:[20]
fun: &a:[419c512c]-----a:[30]

可以发现a的在值在线程hook中被修改成功了,并没有出现越界访问异常

TIPS:
1. 10489856/4 关于这个值的获取是通过&b和&a的地址相减得到的,至于为什么除4,是因为&b是int型的指针,而指针的加减法的单位是4个字节,所以需要除以4,这里我也是调试好半边才搞明白的.



drupal号称比wordpress更强大

drupal没有wordpress普及,但是功能应该比wordpress更强大,有以下8个用途:

项目主页:http://drupal.org

1. 文件存储分享站点
使用Drupal创建文件分享,你可以使用 CCK 和 Views ,也包括一些模块,比如Media Mover, Filebrowser 或者 Web File Manager。看看Box.net,你会非常感兴趣的^_^。

2. 社交网站
在社交网络能力方面,Drupal可能是最好的CMS。Drupal提供了强大的用户管理和权限管理系统。但是如果你想创建强大的社交网站,就需要一些模块,见http://drupal.org/node/206724。
你想看一些案例?Imbee 或者 GoingOn。

3. Twitter Clone
建议你不要尝试利用Drupal创建Twitter竞争产品,但是,如果你想整合Twitter功能到你的站点,Drupal的微博模块 可以帮到你。

4. 新闻News portal
如果你想创建新闻站点或杂志站点,Drupal的完美的选择。使用CCK 和 Views ,你可以创建所有的发布内容类型,并且可灵活列表。这样的新闻站点非常之多,比如New York Observer。

5. 博客网络
用Drupal创建博客网站,很轻松,甚至无需额外模块。看看 Wisebread吧。

6. 视频分享站点
这类站点太耗带宽了,如果你决定创建,那么Drupal来帮你实现吧。FlashVideo 模块提供了创建Youtube克隆的强大能力,它整合了CCK,转换视频到FLV,并有分享代码。另外你也可以尝试Media Mover 和 SWF Tools 。MTV UK 站点就是Drupal创建的。

7. 图片分享站点
Image module ,这个模块将派上用场,可让你创建类Flickr站点,很好很强大。MyFinePix 就是Drupal创建的照片分享站点。

8. 类Digg-like news site
感谢 Drigg module, 这个模块可帮助你快速建立Digg克隆站点。流行的设计社交新闻网Designbump在使用Drupal。



linux查看系统的启动时间和运行时间

1. uptime命令

输出:09:32:17 up  8:41,  1 user,  load average: 0.01, 0.00, 0.00

其中8:41代表系统已经运行8小时41分

2.查看/proc/uptime

[yangguang@sim124 ~]$ cat /proc/uptime 
31351.83  31341.94
#第一个数字代表已经运行的时间
 
#可以用date命令来计算出系统的启动时间
[yangguang@sim124 ~]$ date -d "$(awk -F. '{print $1}' /proc/uptime) second ago" +"%Y-%m-%d %H:%M:%S"
 
2011-03-17 00:50:51
 
#使用date命令计算系统的运行时间
[yangguang@sim124 ~]$ cat /proc/uptime| awk -F. '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("系统已运行:%d天%d时%d分%d秒\n",run_days,run_hour,run_minute,run_second)}'
 
系统已运行:084527


字符串的哈希算法hnv的介绍和实现

fnv全称”Fowler/Noll/Vo”,特点是快速而且冲突率比较低,在url hostname或者ip的哈希有广泛的应用, 详细信息请点击这里

下面是fnv在Sheepdog中用法的实现,将任意字符串转化为uint64_t

  /*
   * 64 bit FNV-1a non-zero initial basis
   */
  #define FNV1A_64_INIT ((uint64_t) 0xcbf29ce484222325ULL)
 
  /*
   * 64 bit Fowler/Noll/Vo FNV-1a hash code
   */
 static inline uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval)
 {
     unsigned char *bp = (unsigned char *) buf;
     unsigned char *be = bp + len;
     while (bp < be) {
         hval ^= (uint64_t) *bp++;
         hval += (hval << 1) + (hval << 4) + (hval << 5) +
             (hval << 7) + (hval << 8) + (hval << 40);
     }
     return hval;
 }


worker

sheepdog源码分析之关键模块介绍(一)

1)   Worker工作线程模块

该模块是作为sheepdog工作线程模块,存在多个工作线程,默认NR_WORKER_THREAD =64个工作线程;线程的入口函数为worker_routine,同时struct work_queue中pending_list是双向链表,worker_routine从该链表中读取任务,然后执行,接着将执行过的任务放到struct worker_info中finished_list双向链表,然后向main thread发送一个信号,接着调用bs_thread_request_done来执行finished_list中任务的done函数,该函数的作用是发送响应信息。涉及的文件主要是worker.c和Worker.h

struct  work_queue {

int wq_state;  //

int nr_active;  //当前活跃的任务数目

struct list_head pending_list;//待执行的任务列表

struct list_head blocked_list; //没有用处了

};

struct  worker_info {

struct list_head  worker_info_siblings;//链表的连接器,目前只有一个worker_info

int nr_threads;  //线程个数

pthread_mutex_t  finished_lock; //

struct list_head  finished_list;

/* wokers sleep on this and signaled by tgtd */

pthread_cond_t pending_cond;

/* locked by tgtd and workers */

pthread_mutex_t  pending_lock;

/* protected by pending_lock */

struct work_queue  q;

pthread_mutex_t  startup_lock;

pthread_t  worker_thread[0]; //工作线程的数据结构

};

这里需要特殊说明的是,有关任务Worker的属性WORK_SIMPLE和WORK_ORDERED,及有关block相关的函数,在0.2版本中应该是没有用处的,当时我也迷惑一阵。

Sheepdog存在三类的工作任务:

  • Request: 所有来自客户端或者其他sheep的请求
  • Recovery_work: 数据恢复任务
  • Delete_work: 删除vdi的任务
  • Cpg_event_work: 该任务作为cpg有关集群管理的任务,例如节点加入和send_message消息发送等,sheep保证当前系统只运行一个cpg_event_work任务,从sys->cpg_event_siblings中是未执行的任务。


sheepdog源码分析之关键数据结构介绍

关键数据结构的说明

1.

struct  sd_req {

uint8_t   proto_ver;

uint8_t   opcode; //操作类型

uint16_t   flags;//

uint32_t   epoch;

uint32_t  id;

uint32_t  data_length;

uint32_t   opcode_specific[8];

};

struct  sd_rsp {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t       flags;

uint32_t       epoch;

uint32_t     id;

uint32_t      data_length;

uint32_t      result;

uint32_t        opcode_specific[7];

};

这两个数据结构应该是作为抽象类,可以看出sizeof(struct sd_req) == sizeof(struct sd_rsp),这个是设计者故意为之,因为在发送请求和接收响应时,客户端是使用同一片内存区域;

2.

struct  sd_obj_req {

uint8_t     proto_ver;

uint8_t     opcode;

uint16_t    flags;

uint32_t    epoch;

uint32_t        id;

uint32_t        data_length;

uint64_t        oid;//object id

uint64_t        cow_oid;

uint32_t        copies;//副本个数

uint32_t        tgt_epoch;

uint64_t        offset;

};

struct  sd_obj_rsp {

uint8_t     proto_ver;

uint8_t     opcode;

uint16_t    flags;

uint32_t    epoch;

uint32_t        id;

uint32_t        data_length;

uint32_t        result;

uint32_t        copies;

uint32_t        pad[6];

};

对object进行请求及响应,这里需要说明的一点:object在Sheepdog中作为数据存储单元,分为data_object 和vdi_object,分别存储数据和vdi的元数据,即后面提到的sheepdog_inode的内容,分片大小为4M。不知作者为何分这么小的分片?

struct sd_vdi_req {

uint8_t proto_ver;

uint8_t opcode;

uint16_t flags;

uint32_t epoch;

uint32_t id;

uint32_t data_length;

uint64_t vdi_size; //vdi的大小

uint32_t base_vdi_id;

uint32_t copies;

uint32_t snapid;

uint32_t pad[3];

};

struct sd_vdi_rsp {

uint8_t proto_ver;

uint8_t opcode;

uint16_t flags;

uint32_t epoch;

uint32_t id;

uint32_t data_length;

uint32_t result;

uint32_t rsvd;

uint32_t vdi_id;

uint32_t pad[5];

};

vdi进行有关操作的请求和响应

更多 >



C语言中宏定义的##连接符和#转换符的用法介绍

1. ##连接符的用法

在C语言中,## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串,一般跟在宏定义参数的前面。

2.#转换符的用法
#转化符是将传递的参数作为字符串来进行处理。
例如定义如下宏:

#define pasrse( i ) printf( "token" #i " = %d", token##i ) 
//同时定义如下的变量
int token1 = 1;

则调用parse(1)的打印结果为:

#宏展开: printf("token" "1" "= %d", token1);
token1=1

3.宏定义的可变参数
在宏定义中使用可变参数,只需在参数后面加入”…”,然后使用##连接即可
例如下面的例子:

  #define LOG_INFO(fmt, args...) \
      do {                       \
          log_write("LOG_INFO", __func__, __FILE__, __LINE__, fmt, ##args); \
      }while(0)
//其中log_write的原型为:
  void log_write(const char *level, const char *func, const char *file, int line, const char *fmt,
          ...);


sheepdog源码学习二之代码目录结构介绍

目录结构

include/

  • config.h: 定义公共的宏
  • bitops.h: 有关的位操作,主要是针对oid的使用情况
  • util.h: 公用操作的实现
  • list.h: 双向链表的实现,主要是参考linux内核代码的实现
  • event.h: epoll异步事件模型
  • logger.h: 日志操作
  • net.h: socket网络IO
  • sheepdog_proto.h: sheepdog中用到的操作类型及数据结构的定义:
  • sheep.h: Sheep本身需要的数据结构和操作类型,与sheedog_proto.h为何分开定义暂不清楚

lib/

  • logger.c: 有关日志文件的操作的实现
  • net.c: 有关socket网络IO的实现
  • event.c: 事件模型的有关实现

sheep/

  • sheep_priv.h: 定义相关数据结构和声明相关函数
  • work.h: 定义工作队列对外提供的数据结构和API
  • work.c: 实现工作线程
  • sdnet.c: 对网络IO的进一步封装,包括回调函数的定义
  • group.c: 利用corosync对组进行管理
  • store.c:  sheepdog有关数据存储、epoch和日志的操作
  • vdi.c:    sheepdog中vdi的相关操作
  • sheep.c:  sheep的main函数入口

collie/

  • treeview.h: vdi tree的有关操作
  • treeview.c: vdi tree的实现
  • collie.c: 对sheep进行管理实现


Vim纵向编辑模式的用法

启动方法

在 Vim 命令模式下,移动游标定位于某一个位置,键入 ctrl-v后状态栏上出现 VISUAL BLOCK 字样,即进入纵向编辑模式。移动游标以按需选择待编辑的区域

我们将下面的例子来说明三种用法,序列

10.60.1.191
10.60.1.192
10.60.1.193

修改为序列:

ping -c 4 10.60.5.191 >> resultso
ping -c 4 10.60.5.192 >> resultso
ping -c 4 10.60.5.193 >> resultso

用法

1. 修改选中的列:将ip字段中的1修改为5

1>crtl – v 进入列编辑模式

2> G  移动光标至最后一行,选中所要编辑的列

3> r  进入修改模式

4> 输入数字5,并按esc退出输入模式

结果如下:

10.60.5.191
10.60.5.192
10.60.5.193

2. 前向添加: 添加字符串ping -c 4

1>crtl – v 进入列编辑模式

2> G 移动光标至最后一行,可视块覆盖了第一列

3> I  进入行首插入模式

4> ping -c 4,并按esc退出输入模式

结果如下:

ping -c 4 10.60.5.191
ping -c 4 10.60.5.192
ping -c 4 10.60.5.193

3. 后添加模式:尾部追加 >> result

1>crtl – v 进入列编辑模式

2>G  移动光标至最后一行,可视块覆盖了最后一列

3> A  进入行尾插入模式

4> <<result,并按esc退出输入模式

搞定….

参考资料:http://www.ibm.com/developerworks/cn/linux/l-cn-vimcolumn/index.html?ca=drs-