<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

统计

  • 随笔 - 44
  • 文章 - 0
  • 评论 - 86
  • 引用 - 0

常用链接

留言簿(6)

随笔分类(31)

随笔档案(44)

Mining

最新随笔

搜索

  •  

最新评论

阅读排行榜

评论排行榜

通过例子学习 CPPUnit

下面是CPPUnit的一个简单例子.

class SimpleCalcTest : public CPPUNIT_NS::TestFixture
{
    CPPUNIT_TEST_SUITE( SimpleCalcTest );
    CPPUNIT_TEST( testAdd );
    CPPUNIT_TEST( testSub );
    CPPUNIT_TEST( testMul);
    CPPUNIT_TEST( testDiv );
    CPPUNIT_TEST_SUITE_END();

private :
    SimpleCalculator 
* sc;

public:
    
virtual void setUp()
    {
        sc 
= new SimpleCalculator();
    }
    
virtual void tearDown()
    {
        delete sc;
    }

    
void testAdd(){
        CPPUNIT_ASSERT_EQUAL( sc
->add(5,6), 11);
    }

    
void testSub(){
        CPPUNIT_ASSERT_EQUAL( sc
->sub(5,6), -1 );
    }

    
void testMul(){
        CPPUNIT_ASSERT_EQUAL( sc
->mul(5,6), 30 );
    }

    
void testDiv(){
        CPPUNIT_ASSERT_EQUAL( sc
->div(12,6), 2 );
    }
};

// 把这个TestSuite注册到名字为"alltest"的TestSuite中, 如果没有定义会自动定义
// 也可以CPPUNIT_TEST_SUITE_REGISTRATION( MathTest );注册到全局的一个未命名的TestSuite中.
CPPUNIT_TEST_SUITE_REGISTRATION( SimpleCalcTest, "alltest" );

int main()
{
    CPPUNIT_NS::TestResult r;
    CPPUNIT_NS::TestResultCollector result;
    r.addListener( 
&result );
    
    
// 从注册的TestSuite中获取特定的TestSuite, 没有参数获取全局的未命名的TestSuite.
    CPPUNIT_NS::TestFactoryRegistry::getRegistry("alltest").makeTest()->run( &r );
    CPPUNIT_NS::TextOutputter 
out&result, std::cout );
    
out.write();
    
return 0;
}

从上面的代码可以看到, 使用CPPUnit 主要是两个步骤:
1. 创建TestSuite
首先从CPPUNIT_NS::TestFixture 生成一个子类, 然后用宏 CPPUNIT_TEST_SUITE, CPPUNIT_TEST, CPPUNIT_TEST_SUITE_END 来定义要测试的各个小单元, 并且实现CPPUNIT_TEST 中定义的类函数; 在每个类函数中使用 CPPUNIT_ASSERT, CPPUNIT_ASSERT_MESSAGE, CPPUNIT_FAIL, CPPUNIT_ASSERT_EQUAL, CPPUNIT_ASSERT_EQUAL_MESSAGE, CPPUNIT_ASSERT_DOUBLES_EQUAL 等来对结果进行断言.

然后通过宏 CPPUNIT_TEST_SUITE_REGISTRATION 将测试类注册到TestSuite中.

2. Main
在main程序中对TestSuite 进行测试.


CPPUnit的更详细的资料可以查看:
IBM 的 便利的开发工具 CppUnit 快速使用指南  比较详细的介绍了CppUnit
VCKBase的 CppUnit测试框架入门 详细的介绍了VC6下MFC Dialog下的CPPUnit的使用
CSDN 的 如何使用CppUnit做单元测试 介绍了VC6的MFC 下的CPPUnit的使用, 和VCKBase的实现稍微有点差别
Meng Yan ( 孟岩 ) 的文章 CPPUnit Lite 对CPPUnit的使用和宏进行了简单的分析



posted @ 2006-10-18 20:59 泡泡牛 阅读(3567) | 评论 (0)编辑 收藏
在Linux下实现对Microsoft Access Database(.mdb)查询访问

介绍了如何在Linux访问MDB数据库, 感觉非常有用:)

You will need the following:
Linux ( I’m running RedHat 9.0)
PHP
Apache
UnixODBC
MDBTools

INSTRUCTIONS
1) Download the UnixODBC RPM, found here. I installed unixODBC version 2.2.5.1.
rpm -ivh unixODBC-2-2.5-1.i386.rpm

2) Download the MDBTools rpm, found here. I installed mdbtools version 0.5.1. Read limitations!
rpm -ivh mdvtools-0.5-1.i386.rpm

3) Download the MDBTools ODBC driver rpm. Again I installed version 0.5-1.i386.rpm. Read limitations!
rpm -ivh mdbtools-odbc-0.5-1.i386.rpm

4) Add the mdbtools driver to your unixODBC config.
Create a new text file. NON-LINUX user: Beware do not do this on windows as you might get werid new lines, use vi.

[MDBToolsODBC]
Description = MDB Tools ODBC drivers
Driver = /usr/lib/libmdbodbc.so.0
Setup =
FileUsage = 1
CPTimeout =
CPReuse =


NOTE: The driver may be in /usr/local/lib/libmdbodbc.so.0. This happens if you build from source and use the make install command. The RPM puts it in /usr/lib.
Now install the driver using the file you created. This is called a template file.
odbcinst -i -d -f template.file

5) Define the datasource name. This is done in the /etc/odbc.ini file. So pull up this file in vi or another text editor and add the following lines.

[Dogs]
Description = Microsoft Access Database of Dogs
Driver = MDBToolsODBC
Database = /var/data/my_dog_db.mdb
Servername = localhost
UserName =
Password =
port = 5432


That’s it you should now have an odbc connection available. I will demonstrate using php, this assumes that your php is compiled with UnixODBC support, the version that ships with Redhat 9 does if yours does not then you can learn how here.

So I will write a quick php script to query my dogs database and print out the names and weights of my dogs.

$myDB = odbc_connect(”Dogs”,”",”");
$query = “select name, weight from dog_list”;
$result = odbc_exec($myDB, $query);
while (odbc_fetch_row($result)) {
print “Name = ” . odbc_result($result,”name”);
print “
Weight = ” . odbc_result($result,”weight”);
}


If you get a php error that says odbc_connect is not a function then see if you have php-odbc installed. Do rpm -qa php-odbc. If you see php-odbc returned then you have it if not install it., the rpm is available on the redhat discs.

Limitations:
- As of the time of writing this entry MDBTools did not support write access to the mdb files. This was fine for my purposes as I was reading data in and sticking it into a mysql database.
- There is a bug in MDBTools v0.5 which does not allow you to query tables or columns with an underscore. This was a bug I hit early on, but it has been fixed in new version 0.6 but that has not been released as of the time of writing this article. So I recompiled the 0.5 source code with the fix from the 0.6 CVS repository. I have bundled it into a 0.5 release and have the two rpms mentioned above here:
mdbtools-0.5-1.i386.rpm
mdbtools-odbc-0.5-1.i386.rpm

I would check the official download site before using my hacked version as I’m sure this bug will be fixed in 0.6 (plus rumor has write access will be present as well).

posted @ 2006-10-09 11:19 泡泡牛 阅读(5763) | 评论 (0)编辑 收藏
压力测试工具 - Siege

对Web进行压力测试有很多工具, 比如Microsoft的application center test (ACT), 还有Mercury 的 LoadRunner, Apache的ab(Apache benchmark), 作为开源软件的Siege 等。

LoadRunner是一个商业软件,其功能非常的强大,可以自定义HTTP的头, 访问的URL, 以及各种访问并发规则等.
apache的ab做重复压力测试不错,但是每次只能测试一个链接.
Siege(英文意思是围攻)设计用于WEB开发这评估应用在压力下的承受能力:可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。

Siege可以从http://www.joedog.org/siege/获得, 它包含了一组压力测试工具:


siege (1)
----------

使用样例:
创建任务列表文件:www.xxx.com.url
http://www.xxx.com/a.html
http://www.xxx.com/b.html
....

siege -c 20 -r 2 -f www.chedong.com.url
参数说明:
-c 20 并发20个用户
-r 2 重复循环2次
-f www.xxx.com.url 任务列表:URL列表

输出样例:
** Siege 2.59
** Preparing 20 concurrent users for battle. 这次“战斗”准备了20个并发用户
The server is now under siege.. done. 服务在“围攻”测试中:
Transactions: 40 hits 完成40次处理
Availability: 100.00 % 成功率
Elapsed time: 7.67 secs 总共用时
Data transferred: 877340 bytes 共数据传输:877340字节
Response time: 1.65 secs 相应用时1.65秒:显示网络连接的速度
Transaction rate: 5.22 trans/sec 平均每秒完成5.22次处理:表示服务器后台处理的速度
Throughput: 114385.92 bytes/sec 平均每秒传送数据:114385.92字节
Concurrency: 8.59 最高并发数 8.59
Successful transactions: 40 成功处理次数
Failed transactions: 0 失败处理次数

注意:由于速度很快,可能会达不到并发速度很高就已经完成。Response time显示的是测试机器和被测试服务器之间网络链接状况。Transaction rate则表示服务器端任务处理的完成速度。


为了方便增量压力测试,siege还包含了一些辅助工具:
bombardment (1)
---------------

用于按照增量用户压力测试:
bombardment urlfile.txt 5 3 4 1
初始化URL列表:urlfile.txt
初始化为:5个用户
每次增加:3个用户
运行:4次
每个客户端之间的延迟为:1秒


siege2csv.pl (1)
----------------
siege2csv.pl将bombardment的输出变成CSV格式:

Time Data Transferred Response Time Transaction Rate Throughput Concurrency Code 200 (note that this is horribly broken.)
242 60.22 603064 0.02 4.02 10014.35 0.08
605 59.98 1507660 0.01 10.09 25136.05 0.12
938 59.98 2337496 0.02 15.64 38971.26 0.26
1157 60 2883244 0.04 19.28 48054.07 0.78


参考:
开源测试工具:http://www.opensourcetesting.org/performance.php

posted @ 2006-10-09 11:17 泡泡牛 阅读(1520) | 评论 (0)编辑 收藏
BBLean - Windows 资源管理器的另外一个选择

Windows的默认GUI shell是资源管理器explorer.exe,bblean可以把windows装点成Xwindow的样子,还有四个虚拟桌面。

bblean的下载:http://bb4win.sourceforge.net/bblean/,属于绿色软件,解压缩即可。其主运行程序是blackbox.exe,桌面上点击右键即可看到设置菜单。设置和插件的修改都很简单,都是用配置文件设置的,用纯文本编辑器打开扩展名为.rc的文件就可以看到各种参数设置。修改以后重新启动blackbox.exe。在bblean工具条和插件显示工具条上点击鼠标右键或者ctrl+鼠标右键,能够看到设置选项。

bblean能够很轻松的设置各种样式显示效果,比如longhorn/vista才会有的alpha透明效果,一些插件包括hue值也可以调节,一直觉得Xwindow比ms window好看,确实如此。

bblean可以通过各种插件,支持不同的功能。插件下载列表:http://xoblite.net/plugins.html,推荐iconbox、calendar,放在桌面上比较实用。


在使用上做简单的说明:
1. 绿色软件且体积小(139k),解压即可,无须安装,双击blackbox.exe启动。
2. 在桌面上点击右键,所有的操作全在这个弹出菜单里了。
3. 弹出菜单以后,左键点击下级菜单标题可以拖动将其摆在桌面上的任何地方。
4. 对任务栏以及任何插件,Ctrl+右键可以对它们进行配置。
5. 选择你喜欢的样式:
   右键菜单>Styles
6. 使用清晰的字体:
   右键菜音>BlackBox>Conifguration>Graphics>Global Font Override  选中
7. 还可以让bblean代替explorer成为默认的窗口管理器:
   右键菜单>BlackBox>Install>Install
8. 退出bblean返回windows:
   右键菜单>BlackBox>Quit
9. 如果bblean异常问题。别慌,不要忘记windows下的Ctrl+Alt+Del。


比较好的网站:

BBLean的快捷键的介绍
http://bb4win.sourceforge.net/bblean/docs/bblean_inc1.htm
http://bb.nlc.no
http://box.crackmonkey.us
http://themes.freshmeat.net/browse/920
http://bb4win.sourceforge.net/latest/Latest
http://www.desktopian.org/bb/plugins.html
http://xoblite.net/plugins.html

posted @ 2006-10-09 10:19 泡泡牛 阅读(2922) | 评论 (7)编辑 收藏
Diff 算法

Diff 在Linux下非常有用, 最近在对定向蜘蛛的研究中, 发现网页的信息大部分在Diff部分, 所以考虑了Diff的原理.

1. Unix Diff

Linux Diff 的基本原理在文 [ Dynamic Programming | http://www.sbc.su.se/~pjk/molbioinfo2001/dynprog/dynamic.html ] 中介绍的很详细了.如果一个文件有n行, 则需要对一个n*n的矩阵进行计算以得到最佳匹配.


2. 贪心算法

在[ Windiff原理初探 | http://www.2maomao.com/blog/how-windiff-works-continued-1/ ]中看到一个贪心算法, 感觉在某种情况下还是比较适合的,但是有些情况还是存在一些问题.

贪心算法的基本思路是: 从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。

首先还是简化问题:每个行根据内容映射到一个整型值, 这就将整个问题简化为整形数组的比较,姑且称之为Anew和Aold。

程序处理流程简述:
while (还没到头)
{
   while (还可以继续找,还可以更贪心)
   {
      找到下一个匹配的(依次对ANew的元素,查找其在AOld中的位置)
      if (找的到)
         计算其贪心值,如果当前更贪则用这个匹配做当前最佳匹配
      else
         break;
   }
}
输出剩余的未匹配节点。

其中“还可以更贪心”是如何判定的呢?

首先定义左臂(leftHand,Anew里面新匹配位置距上一次被采用的匹配的距离),右臂(rightHand,Aold里面新匹配位置距上一次被采用的匹配的距离)。

要找一个目标,在这里我给定的目标是左右平衡情况下的最近匹配。比如一个匹配左臂1,右臂10,另一个匹配是左臂3,右臂3,这时候倾向于选择后一个匹配。
为了公式化和便于计算,我采用一个简单的具有这个逻辑的函数:leftHand*leftHand + rightHand*rightHand的值(贪心值)最小。

定义了这个目标以后,你会发现只要左臂过长,lefthand自身的平方超过上个候选匹配的贪心值,则可以停止往下计算了。

然后这个循环继续下去,直到找到所有的匹配,对每两个匹配之间,如果有内容,则表示有Add/Delete/Modify发生,根据左臂右臂是否为0可以明显区分。

举个例子:
Anew Aold
1     1
2     1
3     3
2     2
4     4
首次匹配找到1<-->1,匹配立即停止,因为1*1 + 1*1 = 2,2*2 > 2,所以没有比较进行下去了.

然后往下找到2<-->2,这时候左臂等于1,右臂等于3,(注意臂长是相对上一次被采用的匹配的),1*1 + 3*3 = 10,当前贪心值是10;然后往下找到3<-->3,左臂为2,右臂为2,2*2 + 2*2 = 8,这个匹配优于上一个匹配;然后继续往下找到2<-->2(左边第二个2),左臂3,右臂3,3*3本身的平方已经超过目前的贪心值8,没有必要再继续往下匹配,这一轮匹配查询结束。

这里可以看出采用平方和做贪心算式的好处,很快可以收敛,而且符合“左右平衡”以及“最近匹配”。
后面2和4的分析略去。

但是这个算法存在一个问题,它的算法只针对单行最优, 而无法实现多行的最优, 比如
Anew Aold
a    a
b    a
c    b

像上面的两个文件, Anew:1 匹配 Aold:1, 但是应该使 Anew:1 匹配 Aold:2, 这样子才可以使Anew中序列ab 与 Aold的序列ab匹配.


3. 下一步工作

 * 调整贪心值计算函数
 * 贪心算法 + 部分动态规划


posted @ 2006-10-08 19:23 泡泡牛 阅读(4259) | 评论 (1)编辑 收藏
Windows 下Eclipse的C++开发环境配置

安装
----
总共需要下面的文件
1. Eclipse的CDT 插件
   http://www.eclipse.org/cdt/
2. MinGW, 主要提供编译C/C++文件的GCC、GDB 和 Make
   http://www.mingw.org/download.shtml

安装CDT插件和MinGW,然后修改Windows的环境变量(设MinGW安装在D:\MinGW)
"PATH" = "D:\MinGW\bin;%PATH%"
"LIBRARY_PATH" = "D:\MinGW\lib"
"C_INCLUDE_PATH" = "D:\MinGW\include"
"CPLUS_INCLUDE_PATH" = "D:\MinGW\include\c++\3.2.3;D:\MinGW\include\c++\3.2.3\mingw32;D:\MinGW\include\c++\3.2.3\backward;D:\MinGW\include"

Eclipse 具体使用
------------
1. 新建项目
   打开eclipse,new->project->Standard Make C++ Project
2. 配置Make命令
   在Project->Properties->C/C++ make project
   build command 的“make”改为“mingw32-make”,再按“应用” “确定”
3. 新建源代码文件和Makefile文件
4. 配置生成目标
window->show view ->make targets  add maketargets  按“ok”,再双击这个maketargets 它就自动帮你make 生成exe
5. 在Project->Properties->C/C++ Make Project->Binary Parser 把ELF Parser改成PE Windows Parser
6. 大功告成  Run->Run as->C Local Application

posted @ 2006-09-15 18:58 泡泡牛 阅读(14205) | 评论 (3)编辑 收藏
Linux 下的线程读写锁

有一种写优先读写锁,有如下特点:
1)多个读者可以同时进行读
2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

在Solaris 中直接提供了读写锁, 但是在Linux 中只提供了线程的读写锁, 这里记录了一些读写锁的资料.

1.Solaris .vs. Linux Posix 库函数

Solaris 库(lib 线程)Linux POSIX 库(libp 线程)操作
sema_destroy()sem_destroy()销毁信号状态。
sema_init()sem_init()初始化信号。
sema_post()sem_post()增加信号。
sema_wait()sem_wait()阻止信号计数。
sema_trywait()sem_trywait()减少信号计数。
mutex_destroy()pthread_mutex_destroy()销毁或禁用与互斥对象相关的状态。
mutex_init()pthread_mutex_init()初始化互斥变量。
mutex_lock()pthread_mutex_lock()锁定互斥对象和块,直到互斥对象被释放。
mutex_unlock()pthread_mutex_unlock()释放互斥对象。
cond_broadcast()pthread_cond_broadcast()解除对等待条件变量的所有线程的阻塞。
cond_destroy()pthread_cond_destroy()销毁与条件变量相关的任何状态。
cond_init()pthread_cond_init()初始化条件变量。
cond_signal()pthread_cond_signal()解除等待条件变量的下一个线程的阻塞。
cond_wait()pthread_cond_wait()阻止条件变量,并在最后释放它。
rwlock_init()pthread_rwlock_init()初始化读/写锁。
rwlock_destroy()pthread_rwlock_destroy()锁定读/写锁。
rw_rdlock()pthread_rwlock_rdlock()读取读/写锁上的锁。
rw_wrlock()pthread_rwlock_wrlock()写读/写锁上的锁。
rw_unlock()pthread_rwlock_unlock()解除读/写锁。
rw_tryrdlock()pthread_rwlock_tryrdlock()读取非阻塞读/写锁上的锁。
rw_trywrlock()pthread_rwlock_trywrlock()写非阻塞读/写锁上的锁。


2.使用mutex 来实现

设置三个互斥信号量:
rwmutex        用于写者与其他读者/写者互斥的访问共享数据
rmutex        用于读者互斥的访问读者计数器readcount
nrmutex        用于写者等待已进入读者退出,所有读者退出前互斥写操作

var   rwmutex,rmutex,nrmutex:semaphore:=1,1,1;  
int   readcount=0;

cobegin
    reader begin
        P(rwmutex);
        P(rmutex);
        readcount++;
        if (readcount == 1) P(nrmutex);  //有读者进入,互斥写操作
        V(rmutex);
        V(rwmutex);  //及时释放读写互斥信号量,允许其它读、写进程申请资源读数据;
        
        P(rmutex);
        readcount--;
        if(readcount == 0) V(nrmutex);  //所有读者退出,允许写更新
        V(rmutex);
    End
    
    writer begin
        P(rwmutex);    //互斥后续其它读者、写者
        P(nrmutex);    //如有读者正在读,等待所有读者读完
        写更新;
        V(nrmutex);    //允许后续新的第一个读者进入后互斥写操作  
        V(rwmutex);    //允许后续新读者及其它写者
    End  
coend  

3. 利用pthread_cond_* & pthread_mutex_* 实现rw_lock

#include <pthread.h>
#include 
<cstdlib>
#include 
<ctime>
#include 
<iostream>
using namespace std;

class RWLock {

private :
    pthread_mutex_t cnt_mutex;
    pthread_cond_t rw_cond;
    
int rd_cnt, wr_cnt;

    RWLock(
const RWLock&);
    RWLock
& operator= (const RWLock&);

public :
    RWLock(): rd_cnt(
0),wr_cnt(0)
        {
            pthread_mutex_init(
&cnt_mutex, NULL);
            pthread_cond_init(
&rw_cond, NULL);
        }

    
void get_shared_lock()
        {
            pthread_mutex_lock(
&cnt_mutex);
            
while (wr_cnt >0)
                {
                    pthread_cond_wait(
&rw_cond,&cnt_mutex);
                }
            rd_cnt
++;
            pthread_mutex_unlock(
&cnt_mutex);
        }

    
void release_shared_lock()
        {
            pthread_mutex_lock(
&cnt_mutex);
            rd_cnt
--;
            
if (0 == rd_cnt)
                {
                    pthread_cond_signal(
&rw_cond);
                }
            pthread_mutex_unlock(
&cnt_mutex);
        }

    
void get_exclusive_lock()
        {
            pthread_mutex_lock(
&cnt_mutex);
            
while (rd_cnt+wr_cnt>0)
                {
                    pthread_cond_wait(
&rw_cond,&cnt_mutex);
                }
            wr_cnt
++;
            pthread_mutex_unlock(
&cnt_mutex);
        }

    
void release_exclusive_lock()
        {
            pthread_mutex_lock(
&cnt_mutex);
            wr_cnt
--;
            pthread_cond_broadcast(
&rw_cond);
            pthread_mutex_unlock(
&cnt_mutex);
        }

    
~RWLock()
        {
            pthread_mutex_destroy(
&cnt_mutex);
            pthread_cond_destroy(
&rw_cond);
        }
};

class Test
{

private :    
    RWLock 
lock;
    
    
static void* shared_task_handler(void* arg)
        {
            Test
* testptr = static_cast<Test*>(arg);

            testptr
->lock.get_shared_lock();
            
//do the shared task here
            testptr->lock.release_shared_lock();
        }


    
static void * exclusive_task_handler(void * arg)
        {
            Test
* testptr = static_cast<Test*>(arg);
            testptr
->lock.get_exclusive_lock();
            
//do the exclusive task here
            testptr->lock.release_exclusive_lock();
        }

public :
    typedef 
void* (*ThreadFunc) (void*);

    
void start()
        {
            srand(time(NULL));

            
const int THREADS_NO=rand()%100;
            pthread_t
* threads = new pthread_t[THREADS_NO];

            
for(int i=0; i<THREADS_NO; i++)
                {
                    ThreadFunc tmpfunc 
= rand()%2? shared_task_handler : exclusive_task_handler;
                    
if (pthread_create(threads+i,NULL,tmpfunc,this))
                        {
                            cerr 
<< "pthread_create fails" << endl;
                            exit(
1);
                        }
                }

            
for(int i=0; i<THREADS_NO; i++)
                {
                    pthread_join(threads[i],NULL);
                }

            delete[] threads;
        }
};

int main()
{
    Test tmptest;
    tmptest.start();
}

--------------------------
参考:
Solaris 執行緒與同步機制之實例
Posix线程编程指南

posted @ 2006-09-07 19:12 泡泡牛 阅读(8928) | 评论 (0)编辑 收藏
Linux C编程 - 线程

Linux 系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库 libpthread.a。

1. 线程的创建和使用
线程的创建是用下面的几个函数来实现的.

int  pthread_create(pthread_t  * thread,pthread_attr_t  * attr,  void   * ( * start_routine)( void   * ), void   * arg);
void  pthread_exit( void   * retval);
int  pthread_join(pthread  * thread, void   ** thread_return);

pthread_create创建一个线程,thread是用来表明创建线程的ID,attr指出线程创建时候的属性,我们用NULL来表明使用缺省属性.start_routine函数指针是线程创建成功后开始执行的函数,arg是这个函数的唯一一个参数.表明传递给start_routine 的参数.
pthread_exit函数和exit函数类似用来退出线程.这个函数结束线程,释放函数的资源,并在最后阻塞,直到其他线程使用
pthread_join函数等待它.然后将*retval的值传递给**thread_return.由于这个函数释放所以的函数资源,所以 retval不能够指向函数的局部变量.
pthread_join和wait调用一样用来等待指定的线程.

下面展示一个最简单的多线程程序。

#include  < stdio.h >
#include 
< pthread.h >
void  thread( void )
{
 
int  i;
 
for (i = 0 ;i < 3 ;i ++ )
 printf(
" This is a pthread.\n " );
}

int  main( void )
{
 pthread_t id;
 
int  i,ret;
 ret
= pthread_create( & id,NULL,( void   * ) thread,NULL);
 
if (ret != 0 )
 {
  printf (
" Create pthread error!\n " );
  exit (
1 );
 }
 
for (i = 0 ;i < 3 ;i ++ )
  printf(
" This is the main process.\n " );
 pthread_join(id,NULL);
 
return  ( 0 );
}

2. 修改线程的属性
上面用pthread_create函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为NULL。属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。

#include  < pthread.h >
pthread_attr_t attr;
pthread_t tid;

/* 初始化属性值,均设为默认值 */
pthread_attr_init(
& attr);
pthread_attr_setscope(
& attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(
& tid,  & attr, ( void   * ) my_function, NULL);

线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD_CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。


另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。

#include  < pthread.h >
#include 
< sched.h >
pthread_attr_t attr;
pthread_t tid;
sched_param param;
int  newprio = 20 ;

pthread_attr_init(
& attr);
pthread_attr_getschedparam(
& attr,  & param);
param.sched_priority
= newprio;
pthread_attr_setschedparam(
& attr,  & param);
pthread_create(
& tid,  & attr, ( void   * )myfunction, myarg);

[参考]
http://fanqiang.chinaunix.net/a4/b2/20010508/113838.html
http://fanqiang.chinaunix.net/a4/b8/20010811/0905001105.html

posted @ 2006-08-04 18:46 泡泡牛 阅读(1076) | 评论 (0)编辑 收藏
Linux下C编程 进程通信 (IPC)

在Linux中存在下面几种进程间通信方式:

1.POSIX无名信号量
2.System V信号量
3.System V消息队列
4.System V共享内存
5.管道(FIFO)

--------------------------------------------------------------------------------
1。POSIX无名信号量
如果你学习过操作系统,那么肯定熟悉PV操作了.PV操作是原子操作.也就是操作是不可以中断的,在一定的时间内,只能够有一个进程的代码在CPU上面执行.在系统当中,有时候为了顺利的使用和保护共享资源,大家提出了信号的概念. 假设我们要使用一台打印机, 如果在同一时刻有两个进程在向打印机输出,那么最终的结果会是什么呢.为了处理这种情况,POSIX标准提出了有名信号量和无名信号量的概念,由于 Linux只实现了无名信号量,我们在这里就只是介绍无名信号量了. 信号量的使用主要是用来保护共享资源,使的资源在一个时刻只有一个进程所拥有.为此我们可以使用一个信号灯.当信号灯的值为某个值的时候,就表明此时资源不可以使用.否则就表>示可以使用. 为了提供效率,系统提供了下面几个函数 
POSIX的无名信号量的函数有以下几个:

int  sem_init(sem_t  * sem, int  pshared,unsigned  int  value);
int  sem_destroy(sem_t  * sem);
int  sem_wait(sem_t  * sem);
int  sem_trywait(sem_t  * sem);
int  sem_post(sem_t  * sem);
int  sem_getvalue(sem_t  * sem);

sem_init创建一个信号灯,并初始化其值为value.pshared决定了信号量能否在几个进程间共享.由于目前Linux还没有实现进程间共享信号灯,所以这个值只能够取0.
sem_destroy是用来删除信号灯的.
sem_wait调用将阻塞进程,直到信号灯的值大于0.这个函数返回的时候自动的将信号灯的值的件一.
sem_post和sem_wait相反,是将信号灯的内容加一同时发出信号唤醒等待的进程..
sem_trywait和sem_wait相同,不过不阻塞的,当信号灯的值为0的时候返回EAGAIN,表示以后重试.
sem_getvalue得到信号灯的值.
由于Linux不支持,我们没有办法用源程序解释了.


2。System V信号量
信号灯的主要用途是保护临界资源(在一个时刻只被一个进程所拥有). System V信号量的函数主要有下面几个.

key_t ftok( char   * pathname, char  proj);
int  semget(key_t key, int  nsems, int  semflg);
int  semctl( int  semid, int  semnum, int  cmd,union semun arg);
int  semop( int  semid, struct  sembuf  * spos, int  nspos);

struct  sembuf{
short  sem_num;  /*  使用那一个信号  */
short  sem_op;  /*  进行什么操作  */
short  sem_flg;  /*  操作的标志  */
};

ftok函数是根据pathname和proj来创建一个关键字.
semget创建一个信号量.成功时返回信号的ID,key是一个关键字,可以是用ftok创建的也可以是IPC_PRIVATE表明由系统选用一个关键字. nsems表明我们创建的信号个数.semflg是创建的权限标志,和我们创建一个文件的标志相同.
semctl对信号量进行一系列的控制.semid是要操作的信号标志,semnum是信号的个数,cmd是操作的命令.经常用的两个值是:SETVAL(设置信号量的值)和IPC_RMID(删除信号灯).arg是一个给cmd的参数.
semop是对信号进行操作的函数.semid是信号标志,spos是一个操作数组表明要进行什么操作,nspos表明数组的个数. 如果 sem_op大于0,那么操作将sem_op加入到信号量的值中,并唤醒等待信号增加的进程. 如果为0,当信号量的值是0的时候,函数返回,否则阻塞直到信号量的值为0. 如果小于0,函数判断信号量的值加上这个负值.如果结果为0唤醒等待信号量为0的进程,如果小与0函数阻塞.如果大于0,那么从信号量里面减去这个值并返回.

下面我们一以一个实例来说明这几个函数的使用方法.

#define  PERMS S_IRUSR|S_IWUSR
void  init_semaphore_struct( struct  sembuf  * sem, int  semnum, int  semop, int  semflg)
{
 
/*  初始话信号灯结构  */
 sem
-> sem_num = semnum;
 sem
-> sem_op = semop;
 sem
-> sem_flg = semflg;
}
int  del_semaphore( int  semid)
{
 
/*  信号灯并不随程序的结束而被删除,如果我们没删除的话(将1改为0)
 可以用ipcs命令查看到信号灯,用ipcrm可以删除信号灯的
 
*/
 
#if  1
 
return  semctl(semid, 0 ,IPC_RMID);
 
#endif
}

int  main( int  argc, char   ** argv)
{
 
char  buffer[MAX_CANON], * c;
 
int  i,n;
 
int  semid,semop_ret,status;
 pid_t childpid;
 
struct  sembuf semwait,semsignal;

 
if ((argc != 2 ) || ((n = atoi(argv[ 1 ])) < 1 ))
 {
  fprintf(stderr,
" Usage:%s number\n\a " ,argv[ 0 ]);
  exit(
1 );
 }
 
 
/*  使用IPC_PRIVATE 表示由系统选择一个关键字来创建   */
 
/*  创建以后信号灯的初始值为0  */
 
if ((semid = semget(IPC_PRIVATE, 1 ,PERMS)) ==- 1 )
 {
  fprintf(stderr,
" [%d]:Acess Semaphore Error:%s\n\a " ,
  getpid(),strerror(errno));
  exit(
1 );
 }

 
/*  semwait是要求资源的操作(-1)  */
 init_semaphore_struct(
& semwait, 0 , - 1 , 0 );

 
/*  semsignal是释放资源的操作(+1)  */
 init_semaphore_struct(
& semsignal, 0 , 1 , 0 );

 
/*  开始的时候有一个系统资源(一个标准错误输出)  */
 
if (semop(semid, & semsignal, 1 ) ==- 1 )
 {
  fprintf(stderr,
" [%d]:Increment Semaphore Error:%s\n\a " , getpid(), strerror(errno));
  
if (del_semaphore(semid) ==- 1 )
   fprintf(stderr,
" [%d]:Destroy Semaphore Error:%s\n\a " , getpid(), strerror(errno));
  exit(
1 );
 }

 
/*  创建一个进程链  */
 
for (i = 0 ;i < n;i ++ )
  
if (childpid = fork())  break ;

 sprintf(buffer,
" [i=%d]-->[Process=%d]-->[Parent=%d]-->[Child=%d]\n " , i,getpid(),getppid(),childpid);
 c
= buffer;

 
/*  这里要求资源,进入原子操作  */
 
while (((semop_ret = semop(semid, & semwait, 1 )) ==- 1 ) && (errno == EINTR));
 
if (semop_ret ==- 1 )
 {
  fprintf(stderr,
" [%d]:Decrement Semaphore Error:%s\n\a " ,
  getpid(),strerror(errno));
 }
 
else
 {
  
while ( * c != ' \0 ' )fputc( * c ++ ,stderr);
  
/*  原子操作完成,赶快释放资源  */
  
while (((semop_ret = semop(semid, & semsignal, 1 )) ==- 1 ) && (errno == EINTR));
  
if (semop_ret ==- 1 )
   fprintf(stderr,
" [%d]:Increment Semaphore Error:%s\n\a " , getpid(),strerror(errno));
 }

 
/*  不能够在其他进程反问信号灯的时候,我们删除了信号灯  */
 
while ((wait( & status) ==- 1 ) && (errno == EINTR));
 
/*  信号灯只能够被删除一次的  */
 
if (i == 1 )
  
if (del_semaphore(semid) ==- 1 )
 fprintf(stderr,
" [%d]:Destroy Semaphore Error:%s\n\a " , getpid(),strerror(errno));
 exit(
0 );
}


3。SystemV消息队列
为了便于进程之间通信,我们可以使用管道通信 SystemV也提供了一些函数来实现进程的通信.这就是消息队列.

int  msgget(key_t key, int  msgflg);
int  msgsnd( int  msgid, struct  msgbuf  * msgp, int  msgsz, int  msgflg);
int  msgrcv( int  msgid, struct  msgbuf  * msgp, int  msgsz,
long  msgtype, int  msgflg);
int  msgctl(Int msgid, int  cmd, struct  msqid_ds  * buf);

struct  msgbuf {
long  msgtype;    /*  消息类型  */
/*  其他数据类型  */
}

msgget函数和semget一样,返回一个消息队列的标志.
msgctl和semctl是对消息进行控制.
msgsnd和msgrcv函数是用来进行消息通讯的.msgid是接受或者发送的消息队列标志. msgp是接受或者发送的内容.msgsz是消息的大小. 结构msgbuf包含的内容是至少有一个为msgtype.其他的成分是用户定义的.对于发送函数msgflg指出缓冲区用完时候的操作.接受函数指出无消息时候的处理.一般为 0. 接收函数msgtype指出接收消息时候的操作.
如果msgtype=0,接收消息队列的第一个消息.大于0接收队列中消息类型等于这个值的第一个消息.小于0接收消息队列中小于或者等于 msgtype绝对值的所有消息中的最小一个消息. 我们以一个实例来解释进程通信.下面这个程序有server和client组成.先运行服务端后运行客户端.

服务端 server.c

#define    MSG_FILE "server.c"
#define    BUFFER 255
#define    PERM S_IRUSR|S_IWUSR

struct  msgtype {
long  mtype;
char  buffer[BUFFER + 1 ];
};

int  main()
{
    
struct  msgtype msg;
    key_t key;
    
int  msgid;

    
if ((key = ftok(MSG_FILE, ' a ' )) ==- 1 )
    {
        fprintf(stderr,
" Creat Key Error:%s\a\n " ,strerror(errno));
        exit(
1 );
    }

    
if ((msgid = msgget(key,PERM | IPC_CREAT | IPC_EXCL)) ==- 1 )
    {
        fprintf(stderr,
" Creat Message  Error:%s\a\n " ,strerror(errno));
        exit(
1 );
    }

    
while ( 1 )
    {
        msgrcv(msgid,
& msg, sizeof ( struct  msgtype), 1 , 0 );
        fprintf(stderr,
" Server Receive:%s\n " ,msg.buffer);
        msg.mtype
= 2 ;
        msgsnd(msgid,
& msg, sizeof ( struct  msgtype), 0 );
    }
    exit(
0 );
}


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

客户端(client.c)

#define    MSG_FILE "server.c"
#define    BUFFER 255
#define    PERM S_IRUSR|S_IWUSR

struct  msgtype {
long  mtype;
char  buffer[BUFFER + 1 ];
};

int  main( int  argc, char   ** argv)
{
 
struct  msgtype msg;
 key_t key;
 
int  msgid;

 
if (argc != 2 )
 {
  fprintf(stderr,
" Usage:%s string\n\a " ,argv[ 0 ]);
  exit(
1 );
 }

 
if ((key = ftok(MSG_FILE, ' a ' )) ==- 1 )
 {
  fprintf(stderr,
" Creat Key Error:%s\a\n " ,strerror(errno));
  exit(
1 );
 }

 
if ((msgid = msgget(key,PERM)) ==- 1 )
 {
  fprintf(stderr,
" Creat Message  Error:%s\a\n " ,strerror(errno));
  exit(
1 );
 }

 msg.mtype
= 1 ;
 strncpy(msg.buffer,argv[
1 ],BUFFER);
 msgsnd(msgid,
& msg, sizeof ( struct  msgtype), 0 ); 
 memset(
& msg, ' \0 ' , sizeof ( struct  msgtype));
 msgrcv(msgid,
& msg, sizeof ( struct  msgtype), 2 , 0 );
 fprintf(stderr,
" Client receive:%s\n " ,msg.buffer);
 exit(
0 );
}  

注意服务端创建的消息队列最后没有删除,我们要使用ipcrm命令来删除的.
4。SystemV共享内存
还有一个进程通信的方法是使用共享内存.SystemV提供了以下几个函数以实现共享内存.

int  shmget(key_t key, int  size, int  shmflg);
void   * shmat( int  shmid, const   void   * shmaddr, int  shmflg);
int  shmdt( const   void   * shmaddr);
int  shmctl( int  shmid, int  cmd, struct  shmid_ds  * buf);

shmget和shmctl没有什么好解释的.size是共享内存的大小.
shmat是用来连接共享内存的.shmdt是用来断开共享内存的.
shmaddr,shmflg我们只要用0代替就可以了.在使用一个共享内存之前我们调用 shmat得到共享内存的开始地址,使用结束以后我们使用shmdt断开这个内存. 

#define  PERM S_IRUSR|S_IWUSR
int  main( int  argc, char   ** argv)
{
 
 
int  shmid;
 
char   * p_addr, * c_addr;
 
if (argc != 2 )
 {
  fprintf(stderr,
" Usage:%s\n\a " ,argv[ 0 ]);
  exit(
1 );
 }

 
if ((shmid = shmget(IPC_PRIVATE, 1024 ,PERM)) ==- 1 )
 {
  fprintf(stderr,
" Create Share Memory Error:%s\n\a " ,strerror(errno));
  exit(
1 );
 }
 
if (fork())
 {
  p_addr
= shmat(shmid, 0 , 0 );
  memset(p_addr,
' \0 ' , 1024 );
  strncpy(p_addr,argv[
1 ], 1024 );
  exit(
0 );
 }
 
else
 {
  c_addr
= shmat(shmid, 0 , 0 );
  printf(
" Client get %s " ,c_addr);
  exit(
0 );
 } 


这个程序是父进程将参数写入到共享内存,然后子进程把内容读出来.最后我们要使用ipcrm释放资源的.先用ipcs找出ID然后用ipcrm shm ID删除. 

5、管道(FIFO)
管道有无名管道和有名管道两种,无名管道一般在父子进程中使用。
无名管道的使用方法一般是:
#include <unistd.h>
int pipe(int filedes[2]);
filedes[
0]用于读出数据,读取时必须关闭写入端,即close(filedes[1]);
filedes[
1]用于写入数据,写入时必须关闭读取端,即close(filedes[0])。

无名管道的使用方法是:
#include <sys/types.h>
#include 
<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

读写管道与读写文件的操作相同。



posted @ 2006-08-04 18:05 泡泡牛 阅读(3406) | 评论 (1)编辑 收藏
mmap使用

利用mmap实现的一个文件拷贝例子

/*
 * gcc -Wall -O3 -o copy_mmap copy_mmap.c
 
*/
#include 
< stdio.h >
#include 
< stdlib.h >
#include 
< string .h >    /*  for memcpy  */
#include 
< strings.h >
#include 
< sys / mman.h >
#include 
< sys / types.h >
#include 
< sys / stat.h >
#include 
< fcntl.h >
#include 
< unistd.h >

#define  PERMS 0600

int  main (  int  argc,  char   *  argv[] )
{
    
int           src, dst;
    
void          * sm,  * dm; 
    
struct  stat  statbuf;

    
if  ( argc  !=   3  )
    {
        fprintf( stderr, 
"  Usage: %s <source> <target>\n " , argv[ 0 ] );
        exit( EXIT_FAILURE );
    }
    
if  ( ( src  =  open( argv[ 1 ], O_RDONLY ) )  <   0  )
    {
        perror( 
" open source "  );
        exit( EXIT_FAILURE );
    }
    
/*  为了完成复制,必须包含读打开,否则mmap()失败  */
    
if  ( ( dst  =  open( argv[ 2 ], O_RDWR  |  O_CREAT  |  O_TRUNC, PERMS ) )  <   0  )
    {
        perror( 
" open target "  );
        exit( EXIT_FAILURE );
    }
    
if  ( fstat( src,  & statbuf )  <   0  )
    {
        perror( 
" fstat source "  );
        exit( EXIT_FAILURE );
    }
    
/*
     * 参看前面man手册中的说明,mmap()不能用于扩展文件长度。所以这里必须事
     * 先扩大目标文件长度,准备一个空架子等待复制。
     
*/
    
if  ( lseek( dst, statbuf.st_size  -   1 , SEEK_SET )  <   0  )
    {
        perror( 
" lseek target "  );
        exit( EXIT_FAILURE ); 
    } 
    
if  ( write( dst,  & statbuf,  1  )  !=   1  )
    {
        perror( 
" write target "  );
        exit( EXIT_FAILURE );
    } 
    
    
/*  读的时候指定 MAP_PRIVATE 即可  */
    sm 
=  mmap(  0 , ( size_t )statbuf.st_size, PROT_READ,
               MAP_PRIVATE 
|  MAP_NORESERVE, src,  0  );
    
if  ( MAP_FAILED  ==  sm )
    {
        perror( 
" mmap source "  );
        exit( EXIT_FAILURE );
    }
    
/*  这里必须指定 MAP_SHARED 才可能真正改变静态文件  */
    dm 
=  mmap(  0 , ( size_t )statbuf.st_size, PROT_WRITE,
               MAP_SHARED, dst, 
0  );
    
if  ( MAP_FAILED  ==  dm )
    {
        perror( 
" mmap target "  );
        exit( EXIT_FAILURE );
    }
    memcpy( dm, sm, ( size_t )statbuf.st_size );
    
/*
     * 可以不要这行代码
     *
     * msync( dm, ( size_t )statbuf.st_size, MS_SYNC );
     
*/
    
return ( EXIT_SUCCESS );

mmap()好处是处理大文件时速度明显快于标准文件I/O,无论读写,都少了一次用户空间与内核空间之间的复制过程。操作内存还便于设计、优化算法。

文件I/O操作/proc/self/mem不存在页边界对齐的问题,但至少Linux的mmap()的最后一个形参offset并未强制要求页边界对齐,如果提供的值未对齐,系统自动向上舍入到页边界上。malloc()分配得到的地址不见得对齐在页边界上。
       
/proc/self/mem和/dev/kmem不同。root用户打开/dev/kmem就可以在用户空间访问到内核空间的数据,包括偏移0处的数 据,系统提供了这样的支持。显然代码段经过/proc/self/mem可写映射后已经可写,无须mprotect()介入。


参考:
Linux环境进程间通信(五): 共享内存(上) 对mmap的介绍很详细

posted @ 2006-07-27 19:48 泡泡牛 阅读(10909) | 评论 (1)编辑 收藏
仅列出标题
共5页: 1 2 3 4 5