分类 技术志 下的文章

在Linux上工作

Linux上学习Linux内核我想应该是最好的方法了。Linux对我来说绝对是一个新鲜环境,搭建在Linux上的工作环境就是我的首要工作,这篇blog记录的就是我在Linux上的工作环境,也希望对大家有些借鉴意义。

我的Linux是在一个多月以前安装的[注1],安装的版本是Fedora Core 4。我使用的是本地磁盘映像安装,磁盘映像文件很大,总共4个,大约2.4G体积。安装过程倒是没有像网上很多人说得那样不顺利,包括修改、合并分区在内大约用了3个小时就看到Linux的桌面了。

进入Linux首先映入眼帘的的就是Linux桌面,我选择了GNOME(GNU Network Object Model Environment)桌面,不为什么,就是因为它流行。下一步就是熟悉这个新环境了,如基本的系统设置、网络设置以及个性化定制等,这些不详述。

工作环境是一个常用软件的集合,在Windows下自不必说了,那些软件都是耳熟能详了。但是在Linux下又有哪些软件可以作为替代品呢?带着这样的目的,我开始了搭建Linux工作环境的历程。另外王垠(http://learn.tsinghua.edu.cn:8080/2001315450)曾在其主页上介绍过不少好用的工具软件,这里很多软件也都是源于王垠的介绍。

Linux下的软件安装一般有两种方法:
(1) 通过rpm方式
安装:rpm -i your-package.rpm
卸载:rpm -e your-package

(2) 通过源代码编译方式
源代码编译三部曲:configure –> make –> make install

我的Linux工作环境
(1) 强大的Bash
以前在Solaris上开发使用的都是C shell,而Linux默认的Shell却是Bash Shell。我初始感觉Bash Shell与C Shell不同之处包括可以自动匹配补齐命令行、支持UP和DOWN ARROW来选择前一个和后一个命令行。对于一个非系统工程师的开发人员来说有一份得心应手的Shell配置文件足矣。下面是我的一份配置文件,简单而灵活,关键一点是它完全能够满足我的需求:
/* .bashrc */
# Tony Bai's .bashrc

#
# Source global definitions
#
if [ -f /etc/bashrc ]; then
        . /etc/bashrc   # –> Read /etc/bashrc, if present.
fi

#
# Greetings
#
echo "*********************************"
echo "***     This is Tony Bai      ***"
echo "*** Welcome to my linux world ***"
echo "*********************************"

function _exit()        # function to run upon exit of shell
{
        echo "********************"
        echo "***   Bye Bye!   ***"
        echo "*** Welcome Back ***"
        echo "********************"
}
trap _exit EXIT

#
# Export environment variables
#
CVSROOT=:pserver:tony@127.0.0.1:/export/home/cvs/CVS-ROOT
PROJDIR=/home/administrator/proj/example
PATH=.:$PATH:$HOME/bin:.local/bin

export CVSROOT
export PROJDIR

#
# User specific aliases and functions
#

# System command set
alias rm='rm -i'
alias mv='mv -i'
alias mkdir='mkdir -p'
alias h='history'
alias which='type -all'
alias ..='cd ..'
alias path='echo -e ${PATH//:/\\n}'
alias du='du -kh'
alias df='df -kTh'
alias la='ls -Al'               # show hidden files
alias ls='ls -hF –color'       # add colors for filetype recognition
alias lx='ls -lXB'              # sort by extension
alias lk='ls -lSr'              # sort by size
alias lc='ls -lcr'              # sort by change time
alias lu='ls -lur'              # sort by access time
alias lr='ls -lR'               # recursive ls
alias lt='ls -ltr'              # sort by date
alias lm='ls -al |more'         # pipe through 'more'

# Compile
alias gcc='gcc -Wall'

# System info Viewer
alias cpu='cat /proc/cpuinfo'
alias mem='cat /proc/meminfo'
alias version='cat /proc/version'
alias ipconfig='/sbin/ifconfig'

# Project info
alias cdinc='cd $PROJDIR/include'
alias cdsrc='cd $PROJDIR/src'

另外修改.bashrc后别忘了执行'bash'使配置修改生效。

(2) 输入法
毕竟是开发中文程序,中文输入发必不可少。虽觉得Fedora自带的“智能拼音”不错,但是“小企鹅输入法(free Chinese Input Toy for X)”的定制功能却让我更加垂涎。遂在小企鹅输入法网站上下载了专门为Fedora Core 4制作的rpm。安装后我们就可以修改~/.fcitx/config文件来订制你个性化的输入法了,如果你在Windows上使用微软输入法习惯了,我们完全可以把“小企鹅输入法”变成Linux上的“微软输入法”。

(3) 浏览器
无论在任何平台上我们都不能忽略网络世界的存在,在Windows上有IE,在Linux上我们有Mozilla Firefox这一新宠儿。关于Firefox的资料太多太多,我想这里就毋庸讳言了。

(4) 邮件工具
Evolution, 一款在使用习惯上颇为接近于Microsoft Outlook的邮件客户端及个人信息管理程序,如果你是用惯了Outlook的用户,那么Evolution将是你在Linux上的一个不错的选择。Evolution是Linux自带的程序,无需你下载安装了。

(5) 编辑器
对于一名程序员来说获得一得心应手的编辑器就好比如虎添翼一般。Linux给你提供了多种选择,既有图形界面的,又有基于终端的。不过VI/VIM仍然是我的最爱。

(6) 开发工具
由于做后台服务端开发,所以必不可少的需要Gcc, make等工具, Linux上还默认提供automake, autoconf等工具,免去了你手工编写Makefile的烦恼,不过要掌握这些工具也需要一个过程,自己权衡吧^_^。

(7) 词典工具
王垠在其文章中提到了WordNet,对该软件的新颖的概念很是感兴趣,遂down了一个,不过遗憾的是没有编译通过,至今未找到原因。

(8) 娱乐工具
程序员在工作之余都喜欢看看电影,而MPlayer又是被公认在Linux下最好的媒体播放软件。遗憾的是我的机器上没有声卡,不能听到MPlayer输出的优美音乐。

(9) 办公工具
由于公司的文档都是由微软的工具产生的,要想在Linux下阅读和修改可不是件容易事。试过了Linux自带的OpenOffice,PPT文档还可以,Word文档简直就不堪入目了。王垠推荐将Word等先转换为html网页再查看,我很懒嫌麻烦。想起金山最新推出的WPS2005在Windows下的效果还不错,希望金山也能尽快推出Linux下的WPS版本。来解决这一使用Linux办公的最大难题。

(10) 通讯工具
对于使用QQ的人,LumaQQ相信是最好的选择;而Gaim是一个支持多种IM协议的工具,只是上手不是很容易罢了。

初接触Linux,试用了上面的一些工具,还处于经验积累阶段。

[注1]
我是参考http://fedora.Linuxsir.org上的安装说明一步一步做的,感觉还不错。

APR源代码分析-线程同步篇

在线程同步方面,Posix标准定义了3种同步模型,分别为互斥量条件变量读写锁。APR也“浅”封装了这3种模型,只是在“读写锁”一块儿还没有全部完成。

线程同步的源代码的位置在$(APR_HOME)/locks目录下,本篇blog着重分析unix子目录下的thread_mutex.c、thread_rwlock.c和thread_cond.c文件的内容,其相应头文件为(APR_HOME)/include/apr_thread_mutex.h、apr_thread_rwlock.h和apr_thread_cond.h。

由于APR的封装过于“浅显”,实际上也并没有多少值得分析的“靓点”。所以本篇实际上是在讨论线程同步的3种运行模型。

一、互斥量
互斥量是线程同步中最基本的同步方式。互斥量用于保护代码中的临界区,以保证在任一时刻只有一个线程或进程访问临界区。

1、互斥量的初始化
在POSIX Thread中提供两种互斥量的初始化方式,如下:
(1) 静态初始化
互斥量首先是一个变量,Pthread提供预定义的值来支持互斥量的静态初始化。举例如下:
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
既然是静态初始化,那么必然要求上面的mutex变量需要静态分配。在APR中并不支持apr_thread_mutex_t的使用预定值的静态初始化(但可以变通的利用下面的方式进行静态分配的mutex的初始化)。

(2) 动态初始化
除了上面的情况,如果mutex变量在堆上或在共享内存中分配的话,我们就需要调用一个初始化函数来动态初始化该变量了。在Pthread中的对应接口为pthread_mutex_init。APR封装了这一接口,我们可以使用下面方式在APR中初始化一个apr_thread_mutex_t变量。
        apr_thread_mutex_t *mutex = NULL;
        apr_pool_t  *pool = NULL;
        apr_status_t  stat;

        stat = apr_pool_create(&pool, NULL);
        if (stat != APR_SUCCESS) {
                printf("error in pool %d\n", stat);
        } else {
                printf("ok in pool\n");
        }

        stat = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pool);
        if (stat != APR_SUCCESS) {
                printf("error %d in mutex\n", stat);
        } else {
                printf("ok in mutex\n");
        }

2、互斥锁的软弱性所在
互斥锁之软弱性在于其是一种协作性锁,其运作时对各线程有一定的要求,即“所有要访问临界区的线程必须首先获取这个互斥锁,离开临界区后释放该锁”,一旦某一线程不遵循该要求,那么这个互斥锁就形同虚设了。如下面的例子:
举例:我们有两个线程,一个线程A遵循要求,每次访问临界区均先获取锁,然后将临界区的变量x按偶数值递增,另一个线程B不遵循要求直接修改x值,这样即使在线程A获取锁的情况下仍能修改临界区的变量x。

static apr_thread_mutex_t       *mutex  = NULL;
static int                                x       = 0;
static apr_thread_t             *t1     = NULL;
static apr_thread_t             *t2     = NULL;

static void * APR_THREAD_FUNC thread_func1(apr_thread_t *thd, void *data)
{
        apr_time_t      now;
        apr_time_exp_t  xt;

        while (1) {
                apr_thread_mutex_lock(mutex);
                now = apr_time_now();
                apr_time_exp_lt(&xt, now);
                printf("[threadA]: own the lock, time[%02d:%02d:%02d]\n", xt.tm_hour, xt.tm_min,
                         xt.tm_sec);
                printf("[threadA]: x = %d\n", x);
                if (x % 2 || x == 0) {
                        x += 2;
                } else {
                        printf("[threadA]: Warning: x变量值被破坏,现重新修正之\n");
                        x += 1;
                }
                apr_thread_mutex_unlock(mutex);
                now = apr_time_now();
                apr_time_exp_lt(&xt, now);
                printf("[threadA]: release the lock, time[%02d:%02d:%02d]\n", xt.tm_hour, xt.tm_min,
                         xt.tm_sec);
                sleep(2);
        }

        return NULL;
}

static void * APR_THREAD_FUNC thread_func2(apr_thread_t *thd, void *data)
{
        apr_time_t      now;
        apr_time_exp_t  xt;
        while (1) {
                x ++;
                now = apr_time_now();
                apr_time_exp_lt(&xt, now);
                printf("[threadB]: modify the var, time[%02d:%02d:%02d]\n", xt.tm_hour, xt.tm_min,  xt.tm_sec);
                sleep(2);
        }

        return NULL;
}

int main(int argc, const char * const * argv, const char * const *env)
{
        apr_app_initialize(&argc, &argv, &env);
        apr_status_t stat;

        //…

        /*
         * 创建线程
         */
        stat = apr_thread_create(&t1, NULL, thread_func1, NULL, pool);
        stat = apr_thread_create(&t2, NULL, thread_func2, NULL, pool);

         //…

        apr_terminate();
        return 0;
}
//output
… …
[threadA]: own the lock, time[10:10:15]
[threadB]: modify the var, time[10:10:15]
[threadA]: x = 10
[threadA]: Warning: x变量值被破坏,现重新修正之
[threadA]: release the lock, time[10:10:15]
当然这个例子不一定很精确的表明threadB在threadA拥有互斥量的时候修改了x值。

二、条件变量
互斥量一般用于被设计被短时间持有的锁,一旦我们不能确定等待输入的时间时,我们可以使用条件变量来完成同步。我们曾经说过I/O复用,在我们调用poll或者select的时候实际上就是在内核与用户进程之间达成了一个协议,即当某个I/O描述符事件发生的时候内核通知用户进程并且将处于挂起状态的用户进程唤醒。而这里我们所说的条件变量让对等的线程间达成协议,即“某一线程发现某一条件满足时必须发信号给阻塞在该条件上的线程,将后者唤醒”。这样我们就有了两种角色的线程,分别为
(1) 给条件变量发送信号的线程
其流程大致为:
{
        获取条件变量关联锁;
        修改条件为真;
        调用apr_thread_cond_signal通知阻塞线程条件满足了;—— (a)
        释放变量关联锁;
}
(2) 在条件变量上等待的线程
其流程大致为:
{
        获取条件变量关联锁;
        while (条件为假) { ——————— (c)
                调用apr_thread_cond_wait阻塞在条件变量上等待;—— (b)
        }
        修改条件;
        释放变量关联锁;
}
上面两个流程中,理解三点最关键:
a) apr_thread_cond_signal中调用的pthread_cond_signal保证至少有一个阻塞在条件变量上的线程恢复;在《Unix网络编程 Vol2》中也谈过这里存在着一个race。即在发送cond信号的同时,该发送线程仍然持有条件变量关联锁,那么那个恢复线程的apr_thread_cond_wait返回时仍然拿不到这把锁就会再次挂起。这里的这个race要看各个平台实现是如何处理的了。
b) apr_thread_cond_wait中调用的pthread_cond_wait原子的将调用线程挂起,并释放其持有的条件变量关联锁;
c) 这里之所以使用while反复测试条件,是防止“伪唤醒”的存在,即条件并未满足就被唤醒。所以无论怎样,唤醒后我都需要重新测试一下条件,保证该条件的的确确满足了。

条件变量在解决“生产者-消费者”问题中有很好的应用,在我以前的一篇blog中也说过这个问题。

三、读写锁
前面说过,互斥量把想进入临界区而又试图获取互斥量的所有线程都阻塞住了。读写锁则改进了互斥量的这种霸道行为,它区分读临界区数据和修改临界区数据两种情况。这样如果有线程持有读锁的话,这时再有线程想读临界区的数据也是可以再获取读锁的。读锁和写锁的分配规则在《Unix网络编程 Vol2》中有详细说明,这里不详述。

四、小结
三种同步方式如何选择?场合不同选择也不同。互斥量在于完全同步的临界区访问;条件变量在解决“生产者-消费者”模型问题上有独到之处;读写锁则在区分对临界区读写的时候使用。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats