404频道

学习笔记

Linux处理ctrl+c信号的例子

当按下ctrl+c时如果代码正在执行sleep则会停止睡眠,调用信号处理函数。中断位置可能位于for循环代码段的任意位置,中断位置不可控。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <signal.h>
void h(int s)
{
printf("抽空处理int信号\n");
}
main()
{
int sum=0;
int i;
signal(SIGINT,h);
sigset_t sigs;

for(i=1;i<=10;i++)
{
sum+=i;
sleep(1);
}
printf("sum=%d\n",sum);
printf("Over!\n");
}

信号屏蔽的例子1

当按下ctrl+c时不会调用信号处理函数,当循环执行完毕后会调用信号处理函数,并且printf(“Over!\n”)会被执行。

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
31
32
#include <stdio.h>
#include <signal.h>

void h(int s)
{
printf("抽空处理int信号\n");
}

main()
{
int sum=0;
int i;
// 声明信号集合
sigset_t sigs;
signal(SIGINT,h);
// 清空集合
sigemptyset(&sigs);
// 加入屏蔽信号
sigaddset(&sigs,SIGINT);
// 屏蔽信号
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=1;i<=10;i++)
{
sum+=i;
sleep(1);
}
printf("sum=%d\n",sum);
// 消除屏蔽信号
sigprocmask(SIG_UNBLOCK,&sigs,0);
// 如果在上面按下ctrl+c,在此句不执行
printf("Over!\n");
}

当在循环中按下ctrl+c后,该函数输出结果为:

1
2
3
sum=55
抽空处理int信号
Over!

信号屏蔽的例子2

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
31
32
33
34
35
36
37
#include <stdio.h>
#include <signal.h>
// 信号处理函数
void h(int s)
{
printf("抽空处理int信号\n");
}
main()
{
int sum=0;
int i;
signal(SIGINT,h);
sigset_t sigs,sigp,sigq;
sigemptyset(&sigs);
sigemptyset(&sigp);
sigemptyset(&sigq);

sigaddset(&sigs,SIGINT);
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=1;i<=10;i++)
{
sum+=i;
sigpending(&sigp);
if(sigismember(&sigp,SIGINT))
{
printf("SIGINT在排队!\n");
// 是信号SIGINT有效
sigsuspend(&sigq);
// 函数调用完毕后信号SIGINT无效
}
sleep(1);
}
printf("sum=%d\n",sum);
// 消除屏蔽信号
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("Over!\n");
}

该例子可以实现在指定的代码处处理信号。
其中sigsuspend函数原先如下:

1
int sigsuspend(const sigset_t *mask);

函数解释:屏蔽新的信号,原来的屏蔽信号失效。是一个阻塞函数,该函数屏蔽mask信号;对非mask信号不屏蔽,信号处理函数调用完毕该函数返回;如果非mask信号没有信号处理函数,则此函数不返回。即返回条件:信号发生且信号为非屏蔽信号且信号必须要调用信号处理函数完毕。

Image Title

我想拥有一只雌性袋鼠作为宠物,我会将其取名为”点点”。之所以是雌性是看中了袋鼠的温暖舒适的育儿袋。

如果可以我会给育儿袋上面缝上个拉链,这样我就不会担心放在袋袋里面的东西会掉出来了。

我可以领着我的点点去超市购物,将购买的东西放到袋袋里面,拉链一拉,然后蹦蹦跳跳的就回家了。

晚上吃完饭,我可以领着点点去大街上走走,我可以将我的钱包、手机放到点点的袋袋里,拉链一拉,完全不用担心手机会摔坏。

如果下起了雨或遇到了寒风,我可以钻到袋袋里,露个头在外面,一跳10米远,然后蹦蹦跳跳的就回家了。

当然,这是不现实的,我在做梦,做一个好笑的梦。。。

参加完姐姐的婚礼回到家后,静下心来之后内心莫名的感伤。也许是早晨三点醒四点起床导致身体在下午已经出现了疲惫。也许是生活本来就应该是平淡的,兴奋的多了,也自然疲惫的多,总之要保持一个符合每个人性格的平衡。也许是因为如女朋友同事的例子中一样,当丈夫娶妻过门的时候,丈夫的妹妹是哭的,因为妹妹有恋哥情节,难不成我也有恋姐情节的存在,还好我仅是有点伤感而已,不严重。也许是因为对时间流逝的无奈,总以为我们还没有长大,转眼间姐姐已经步入了婚姻的殿堂,成立了自己的新家庭,逝者如斯。趁着自己还有点文艺范的精神状态,写写这两天的一些感想,要不然明天一接触需要理性思考的计算机语言,这种感性点的状态又会归于平淡,又会在我鲜有的心路历程文章里少了一篇。

小的时候总是羡慕姐姐在城里的生活,每年在仅有的几次见面中总能感受到一个不一样的姐姐。那时的我对于城市的大小、城市的好坏还没有概念,仅仅知道城市要比农村好不少倍,仅仅知道姐姐是城里的,是城市小朋友的代言人,是引以为荣的小榜样。也许下面这张小时候的珍贵照片最能反应出城市孩子和农村孩子之间的区别。
Image Title
记得大约上八年级寒假的时候见过姐姐写的字,字迹铿锵有力且独特,不是常见的楷体字,由特定的字体加上自己的独到之处柔和而成。分明是经过训练方能造就,我从未在我认识的同学中见过能够写出如此华丽的字体,即使有书法天赋的同学由于没有经过特定的练习,字体往往都是课本上常见的楷体字。因为从上高中起,我已经开始渐渐地融入曾经向往的城市生活,直到如今我也算半个城市人。我和姐姐之间的差距在不断的缩短。当然会发现原来城市生活也有城市生活的弊端,如果让我选择一次我还会选择在农村度过我的童年,那种各种玩各种贴近大自然的生活是在高楼间无法体会到的。

姐姐终于找到了可以寄托的归宿,姐夫是很优秀的。姐姐属于离不开爱情的类型,但身体却不是很好,也许是姐夫喊姐姐玉妹的缘由。期望在外来姐夫能够好好照顾姐姐,同时姐姐应该多注意身体,多吃粗粮。

一个我想象不到的地方是随着现在人民文化水平的提高,婚礼的举办方式正在朝着复杂化的方向发展。现在的婚礼已经将旧有的当地风俗和现代的风俗结合在了一起,而且这两者是叠加的关系,意味着风俗越来越多,需要处理的事情越来越复杂。其实婚礼的作用无非就是热闹、喜庆, 我实在是搞不太明白复杂和热闹之间是一个什么样的关系。我期望我婚礼是简单的,简单的不能再简单,我甚至期望不需要亲朋好友的过多参与,我甚至不期望举办复杂的婚礼仪式,热闹的背后是家人的操劳,是长达数月的准备。我只希望能有一次难忘的旅行,两个人的旅行就足够幸福。

祝福的含义我是不理解的,祝福原本是一个人向另外一个人的未来的美好祝愿,就是说些客套话,说些不切实际的话。其实一个人很难对另外一个人的外来向好的方面发展做出贡献,就如同大臣们天天喊着万岁万岁万万岁,却未见过哪个皇帝超过百岁一般。很多人都会向姐姐的婚礼说出祝福,但有多少是过后还记得自己曾经说过的。我在这里同样祝福姐姐和姐夫新婚快乐,生活幸福,和睦相处,恩恩爱爱,当然我是发自内心的,而且我的话是有文字可考究的。同样在给姐姐的红包里我写下了“原寻寻觅觅,现卿卿我我,年年岁岁情爱深,岁岁年年无不同”的祝福,这里同样做一个备忘。

目前的婚庆已经完全市场化,只要是服务项没有不收费的可能性,而且往死里要。没有信仰的民族是个可怕的民族,如果马克思主义不能所谓一种信仰,那么中国人中的绝大多数是没有信仰的。即使大家上初中、高中、大学、研究生阶段都在马克思的理论,在当前的国情下估计也没有多少人能视马克思如珍宝。如果在国外我估计会出现在教堂举办婚礼非常廉价,甚至免费的可能性,因为能够为新郎新娘主持婚礼本身就是一件非常荣幸的事情,金钱不是最重要的,当然我不了解海外的真实情况。总感觉一件本来可以免费的事情只要跟金钱沾边总会变味。

中国父母的典型形象是碎碎念,对于子女总会絮絮叨叨那么一箩筐。在不经意间,子女已经长大成人,可在父母的眼中子女永远是未长大的孩子。中国的父母在子女身上花费了太多的心血,以至于子女比自己更重要。

一个活动的缺点是非常容易被人察觉的,而活动的优点往往不容易被人发觉。因此,要想顺利完成一个活动是不容易的。举办婚礼的时候我发现有很多事先未准备妥当的地方,而这些恰恰是最容易被发现的,而做得特别好的地方却是较难被人们发现。

年龄之间的代沟还是比较明显的,这一点在酒桌上特别能够体现,在酒桌上话语最多的永远是上一辈人,因为年轻人跟上一辈人在思想上还是有较大的差距,这是好事。如果两代人之间差距过小,说明这个社会变化太慢。年轻人应该吸取上一代人的长处,去其槽粕,取其精华。我想未来的酒桌文化会大变样。

我目前的理想生活状态是平淡充实的,我喜欢静下心来学习,我喜欢那种学习的充实感,我喜欢跟同龄人人心贴心的交谈,而不是在噪杂的环境中大家泛泛而谈。我的性格中腼腆的一面表现在我在与人沟通时内心的想法总会羞于表达,比如很难从我的嘴里向父母说出生日快乐之类的话语,向姐姐当面说出新婚快乐,白头偕老。一方面想着跟人沟通,另一方面却羞与吐露自己的内心想法,貌似有点矛盾的样子。

人类本该是感性的,接触的外部环境多了之后会渐渐趋于理性,当然人类的科技进步需要理性。理性多了,自然感性就会少一些。这几年跟计算机打交道的时间成了生活、工作的最重要部分,我大部分生活状态处于理性状态,心情以平淡居多。包括我现在写这篇文章,也是在用理性的思维方式来写感性的文章,换成在高中时期写的文章却是用感性的思维方式来写感性的文章。

我的性格未曾被工作改变,甚至是相貌在工作三年的时间里也未曾有大的变动。我现在仍然看上去像个学生,路人也经常会将我当成一名学生。女朋友说这是我不成熟的表现,我不知道成熟如何去界定,我认为我每天学到的东西都在助我向成熟迈进,直到我老去的那一刻我也不可能成熟,因为只要我还有进步我就不会成熟。我对于三年来的未改变我引以为豪,至少我没有被这个残缺的社会给同化掉,至少我还拥有自己的思想,同时也证明了一点我在大学期间思想就已经在保持了一种相对稳定的状态。我不认为工作后在官场上混的如鱼得水,在酒桌上的机智多变是一种成熟的表现,我对此持悲观态度。

行行出状元。今天看到我姑在玩QQ农场、QQ牧场的游戏,熟练程度相当高。由于我姑空闲时间较多,常常一个人在家,已经玩了多年的游戏。我姑对电脑的使用还处在初级阶段,平常电脑的作用还定位在玩农场的阶段。只要做的次数多了,行行都可以出状元。

本来想着把自己参加完姐姐婚礼之后的感想记录下来,结果写着写着开始有些怀旧,写自己的居多,困的不知所云,到此为止。不知等我老去的那天当我读到此文章时会是一种什么样的心情,难不成还会跟现在一样有点压抑的感觉?

system函数

函数包含在C语言的标准库中,在头文件stdlib.h中声明如下:

1
int system(const char *command);

该函数会创建一个独立的进程,该进程拥有独立的进程空间,为阻塞函数,只有当新进程执行完毕该函数才返回。

返回值:可以通过返回值来获取新进程的main函数的返回值,返回值保存在int类型的第二个字节即8-15比特,可以通过向右移位或者利用宏WEXITSTATUS(status)来获取新进程的返回值。其中WEXITSTATUS(status)宏包含在头文件<sys/wait.h>中。

这里以调用ls命令为例来展示用法:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
int r=system("ls");
// 用右移位的方式来获取新进程的返回值
//printf("%d\n",r>>8&255);
// 用WEXITSTATUS(status)宏的方式来获取新进程的返回值
printf("%d\n",WEXITSTATUS(r));
}

popen函数

函数包含在头文件stdio.h中,相关函数如下:

1
2
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

popen函数在父子进程之间建立一个管道,其中type指定管道的类型,可以为”r”或”w”即只读或可写。在shell中的管道符”|”即采用此函数来实现。popen函数为阻塞函数,函数的具体用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
main()
{
char buf[1024];
FILE *f=popen("ls","r");
// 根据管道获取文件描述符
int fd=fileno(f);
int r;
while((r=read(fd,buf,1024))>0)
{
buf[r]=0;
printf("%s\n",buf);
}
close(fd);
// 关闭管道
pclose(f);
}

exec系列函数

该系列函数并不创建新的进程,而是将程序加载到当前进程的代码空间来执行并替换当前进程的代码空间,在exec*函数后面的代码将无法执行。

1
2
3
4
5
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

fork函数

该函数非常常用。函数原型如下:

1
pid_t fork(void);

调用该函数会产生一个子进程,该子进程不仅复制了父进程的代码空间、堆、栈,而且还复制了父进程的执行位置。之后父子进程同时执行,通常由于操作系统任务调度的原因,子进程会先执行。父进程和子进程之间的并不会共享堆、栈上的数据,可以通过文件或共享内存的方式来通讯。

返回值:该函数父进程返回子进程的id,子进程返回0。通常在代码中通过返回值来判断是子进程还是父进程,用来执行不同的代码。

如果父进程先结束,则子进程会成为孤儿进程,子进程仍然可以继续执行。进程数中的根进程init会成为该子进程的父进程。

如果子进程先结束,则子进程会成为僵尸进程。僵尸进程并不再占用内存和CPU资源,但是会在进程数中看到僵尸进程。因此代码中必须对僵尸进程的情况做处理,通常的处理办法为采用wait函数和信号机制。子进程在结束的时候会向父进程发送一个信号SIGCHLD,整数值为17。父进程扑捉到该信号后通过调用wait函数来回收子进程的资源。其中wait函数为阻塞函数,会一直等待子进程结束。wait函数返回子进程退出时的状态码。下面通过实例演示一下当子进程先结束时父进程怎样回收子进程。

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
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void processChildProcess()
{
printf("receive the child process end\n");
wait();
}

int main()
{
int pid;
pid = fork();
if (pid > 0)
{
// 父进程
signal(SIGCHLD, processChildProcess);
while(1)
{
sleep(1);
}
}
else
{
// 子进程
sleep(1);
printf("child process end\n");
}
}

关于fork的详细理解可以看这里:一个fork的面试题

前段时间公司领导安排我完成一个android的小项目,功能为完成手机短信的分类功能。由于考虑到可能会和某运营商合作,项目需求多变,项目名到项目死掉的那一刻也没有想好,暂定叫短信管家。前段时间参加了今年软考中的高级项目管理师考试,对项目管理有一定的了解,由于在实际的工作中没有参与项目经理的角色,对项目管理的知识理论和实际结合还有一定差距。我一向是喜欢挑人毛病的,本文也不例外,将结合项目管理的知识领域来分析项目中存在的问题,并给出纠正的措施。本文并不想按照项目管理的44个过程组来写,不想受项目管理理论知识条条框框的约束。

项目简介

在2013年元旦后上班第一天,经过领导简单介绍项目后,项目正式开始启动。项目参与人员包括我和一名美工。由于公司没有做android项目的经验,我是公司唯一懂点android项目的技术人员,我之前也仅仅是自学过一点。经过两个星期的学习之后开始了android项目的开发,春节前领导希望看到一个版本出来。离春节放假还有三天,美工搞出一个首界面来,我仿照首界面基本搞定。后来领导一看不符合需求,其实领导之前就看过首界面效果图,只是没有仔细看。当然领导也不知道要做出个什么样的东西,估计心想着技术人员弄出个什么样的东西来再修改。
年后,对需求开会进行了重新整理,通过思维导图的方式跟领导确定需求,就需求中存在的疑问进行确认。不过后来事实证明经过领导确认的需求也是在不断变更。这次的需求要远比上次复杂,将原来的短信分类从一级短信分类更改为了二级短信分类。公司没有互联网行业产品的经验,美工给出的效果图也仅是从桌面端来考虑移动产品的设计。我最终决定自己开始重新设计软件的界面,参考了众多同类软件,花费的时间比较多。大约到2013年4月底产品已经基本可用,经过我自己的测试和领导的使用效果还算可以。后来发现不支持彩信,经过我的一番研究后发现现有的产品功能要对彩信支持需要耗费非常大的工作量,因为软件本身的功能打破了android系统短信的设计,也难怪了android的应用中没有功能相似的产品出现。这也成为了该项目失败的直接原因。

可行性研究

技术可行性

项目的起源来自领导的短信泛滥,领导想着能够搞一个拦截垃圾短信并对短信进行管理的软件将是酷的一件事情。领导接触过一些android,片面的认为这种方案在技术上可行。我也做了一个调研,发现现有的android软件项目中没有此功能的产品。

经济可行性

支出分析:由于项目主要参与人员仅有我一人,支出的成本较小。收益分析:现在的互联网产品都是先圈用户再考虑盈利,我们这个软件产品也不例外。

运行环境可行性

对于产品的运营存在两种方式:1、跟某地方运营商合作;2、自己单独发布。

总体而言,由于项目较小,对可行性研究基本忽略,没有科学、可观、公证的对项目进行可行性分析。

范围管理

在项目立项的时候仅有一个产品的大致方向,做一款android系统中的短信分类管理类应用。从始至终领导对于产品的理解在不断变化,每次变化都需要修改很多代码,跟领导对于导致产品研发过程做了很多无用功。
在项目进行到一个月后,我认识到了项目这样下去反复修改不可能有好的结果,因此跟领导召开了需求定义的会议,就产品的需求用思维导图的方式就项目的范围进行了定义,会后就需求中没有考虑到的问题跟领导进行了沟通确认。此次会议之后项目的需求基本敲定,虽然之后领导有需求变更的情况,但是相对较少。

进度管理

由于公司初次开展android类项目,对项目的进度很难掌控。项目开始时负责市场部的领导给出了一个月之内完成的计划,我心想我自己加把劲应该能搞定吧,因为最初的项目需求还比较简单。我用了两个星期的时间来学习android知识,用两个星期的时间来开发,结果到最后我才刚根据美工提供的效果图完成了首页,我总是不能很好的估算自己的工作进度。这篇《为什么程序员总是不能准确预估工作量》的文章或许能替我解释些什么。
在项目范围确定之后我对自己工作的进度管理也不够好,第一次尝试做android项目,未知因素太多,编码中总能遇到这样那样的问题。

成本管理

项目的整个成本应该我的人力成本占了绝大部分,本来软件项目中的人力成本就是占的比重较多的,何况主要是我一个人参与的项目,自然我那微薄的人力占了项目成本的大部分。至于后期项目的盈利并没有过多考虑,现在的互联网行业本来就是先圈用户后盈利。

质量管理

我在项目的编码过程中,一般一个小功能完成之后都会进行详尽的功能测试。由于功能不负责,产品的质量只要多测试一般问题就不大了。

人力资源管理

项目的初期我就感觉项目不可能成功,但是领导坚持要做,而且我一个做C++的程序员来写android的代码,我当时居然仅有听从领导的意识,没有适当的表达自己的想法。当时怀着一己之心认为可能一个月项目就完成了,完成之后工作依旧,还可以熟练熟练android的开发,谁知一练手就是做了五个月,而到最后项目仍然没有成型。
由于一直对项目持悲观态度,始终认为我自己开发出来的产品我都不想去用,用户怎么可能去用。长期自己独自做,兼任了产品经理、美工、码农的工作,做的过程中还要担心着需求的变更,在项目进行到中后期的时候, 缺少激励因素,对工作激情不够,这也影响了项目的进度。

沟通管理

在项目的开发过程中基本能保持没完成一个关键功能跟领导汇报一次项目进展。在项目中沟通方面存在不少的问题,尤其以前期严重。前期由于跟领导的需求不一致,导致做了很多的重复性劳动。领导对项目的需求本来就不够明确,一旦有好的想法就加到项目中,这也导致了跟领导的沟通困难,因为这样的沟通效率太低。

风险管理

项目可研阶段对技术可行性分析研究不够,导致了后期在对彩信进行分类处理的时候遇到了技术上的难题。当然前期的可研阶段做充分的技术可行性研究也是不太现实了,因为本来技术对公司而言就是未知的。最好的办法就是对自己不熟悉而且短期内不能实现盈利的项目不参与。

文档管理

由于项目管理中要求每个阶段都有输出的文档,这么繁琐的文档我相信中国找不出几家软件公司能够办到,能办到的估计都已经死掉了。此项目中仅有范围定义的思维导图、我在编写某个具体的功能代码前绘制的流程图,应该说文档及其少。但我目前不认为这有什么不对,我还是坚信某些情况下看代码比看文档要来的更快,特别是看我这种android菜鸟写的代码。更何况此项目的需求在不断随着领导的想法而变化,文档都没法写,写文档的成本太高。

我的一些反思:

在项目中我也存在不少问题。

  1. 项目开始阶段应该向领导表达清楚自己的想法,阐明自己对项目的看法。
  2. 对项目的进度没有很好的把握。
  3. 对技术较为熟悉之后可以适当的学习后期开发功能需要技术,比如一直没有研究彩信的技术实现,导致后期在开发彩信功能时遇到技术问题。如果能够提前学习相关技术就能够提前预支项目的风险。

shell中的环境变量

查看环境变量

  1. 通过env命令可以查看所有的环境变量
  2. 通过echo $环境变量名方式来查看单个环境变量

设置环境变量

export命令来设置环境变量

在程序中该如何获取和设置环境变量呢?

通过main函数的第三个参数

通常大家接触比较多的是两个参数的main函数,实际上还有一个包含三个参数的main函数,第三个参数为包含了系统的环境变量的二级指针。
用法如下:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main ( int argc, char *argv[], char *arge[])
{
while (*arge)
{
printf("%s\n", *arge);
arge++;
}
return 0;
}

实例将会输出该用户的所有环境变量。

通过外部环境变量environ

该变量定义在“unistd.h”头文件中,定义为:extern char **environ;
用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

extern char **environ;

int main ( int argc, char *argv[] )
{
char **env = environ;
while (*env)
{
printf("%s\n", *env);
env++;
}
return 0;
}

实例将会输出该用户的所有环境变量。

通过系统函数

在C语言的头文件“stdlib.h”中定义了三个和环境变量相关的函数:

1
2
3
char *getenv(const char *name);
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);

比较简单,不再举例。

在Linux的头文件sys/mman.h中提供了两个用来分配内存的函数:mmap和munmap,函数定义原型如下:

1
2
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *start, size_t length);

mmap说明

返回值:内存映射后返回虚拟内存的首地址。
参数:
start为指定的映射的首地址,该地址应该没有映射过,如果为0则有系统指定位置。
length为映射的空间大小,真正分配空间大小为(length/pagesize+1)。
prot为映射的权限,分为四种未指定(PROT_NONE)、读(PROT_READ)、写(PROT_WRITE)、执行(PROT_EXEC)。如果为PROT_WRITE,则直接可以PROT_READ。
flags:映射方式,分为内存映射和文件映射。内存映射:匿名映射。当值为文件映射是后面两个参数才有效。常用的值有:MAP_ANONYMOUS、MAP_SHARED、MAP_PRIVATE。
fd:映射的文件描述符。
offset为从文件的偏移位置开始映射。

munmap说明

从start位置开始释放length个字节的内存。

应用举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>

main()
{
int *p = mmap(NULL,
getpagesize(),
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED,
0,
0);
munmap(p, getpagesize());
}

其中getpagesize()函数的作用为获取一个页的大小,系统默认为4K。

当linux中的函数内部出错时通常函数会返回-1,并且将错误码保存到全局变量errno中,用来表示错误代码。errno全局变量包含在头文件errno.h文件中。下面给出三种打印错误信息的方法。

perror函数

应用举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
FILE *fp ;
fp = fopen( "/root/noexitfile", "r+" );
if ( NULL == fp )
{
perror("error : ");
}
return 0;
}

输出如下:
Permission denied

strerror函数

strerror函数原型为:char *strerror(int errnum);将参数errnum转换为对应的错误码。
应用举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
FILE *fp ;
fp = fopen( "/root/noexitfile", "r+" );
if ( NULL == fp )
{
printf("%s\n", strerror(errno));
}
return 0;
}

输出如下:
Permission denied

printf中的%m打印

应用举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
FILE *fp ;
fp = fopen( "/root/noexitfile", "r+" );
if ( NULL == fp )
{
printf("%m\n");
}
return 0;
}

输出如下:
Permission denied

Linux系统中提供了两个在堆中分配空间的底层函数,函数原型如下:

void *sbrk(intptr_t increment);
int brk(void *end_data_segment);

两个函数的作用均为从堆中分配空间,并且在内部维护一个指针,指针的值默认为NULL。如果内部指针为NULL,则得到一页的空闲地址,系统默认为4K字节。指针向后移动即为分配空间,指针向前移动为释放空间。当内部指针的位置移动到一个页的开始位置时,整个页会被操作系统回收。brk为绝对改变位置,sbrk为相对改变位置。

sbrk函数

在sbrk函数中,参数increment为要增加的字节数,increment可以为负数。当increment为负数时表示释放空间。当increment==0时,内部指针位置不动。函数调用成功返回内部指针改变前的值,失败返回(void *)-1。
函数使用举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <unistd.h>

int main()
{
int *p0 = sbrk(0);
// 打印堆地址
printf("this site of p0 is : %d\n", p0);

int *p1 = sbrk(1000);
// 这里仍然打印的是第一次的堆地址
printf("this site of p1 is : %d\n", p1);

int *p2 = sbrk(1);
// 打印第一次堆地址+1000后的地址
printf("this site of p2 is : %d\n", p2);

// 回到初始堆地址,释放空间
sbrk(-1001);

int *p3 = sbrk(0);
// 检查是否回到初始地址
printf("this site of p3 is : %d\n", p3);
}

输出如下内容:
this site of p0 is : 264622080
this site of p1 is : 264622080
this site of p2 is : 264623080
this site of p3 is : 264622080

brk函数

在brk函数中,函数作用为将当前的内部指针移动到end_data_segment位置。成功返回0,失败返回-1。
函数使用举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <unistd.h>

int main()
{
int *p0 = sbrk(0);
printf("this site of p0 is : %d\n", p0);

brk(p0 + 1000);
printf("this site is : %d\n", sbrk(0));

brk(p0 + 1001);
printf("this site is : %d\n", sbrk(0));

brk(p0);
printf("this site is : %d\n", sbrk(0));
}

函数输出如下:
this site of p0 is : 206979072
this site is : 206983072
this site is : 206983076
this site is : 206979072

适用场景:

内存的管理方式比malloc和free更加灵活,适合申请不确定的内存空间的情况,特别适合同类型的大块数据。如果用malloc则可能存在申请内存空间过多浪费的情况,过少时需要重新调用realloc来重新申请内存的情况。速度比malloc快。

前端时间折腾了一段时间的Github博客,终于搞明白了jekyll,开始写了一篇博客发现问题比较多。比如中文编码问题,Github对makedown的支持问题,Github的文章同步问题,网速问题。总体而言,感觉用Github来写博客还不是非常满意。

今天下午偶然想起了前段时间看过faxbox的一篇文章,今天下载下来尝试,果然非常酷。优点如下:

  • 博客放在了Dropbox不会丢失。当然没有Github酷,Github可以通过版本管理来查看历史版本。貌似Dropbox本身有版本管理的功能。
  • 写博客的方式简捷。Github的jekyll还需要利用rake命令来创建文章,这样才能保证文章的头部含有YAML标签。而faxbox直接编辑文本文件即可。
  • 文本编辑器给力。可以通过实时预览功能查看makedown的解析情况,当然也可以通过在线的makedown编辑器stackedit来编写makedown。
  • 网速比Github快。测试一下感觉网速还比较快。
  • 可以修改模板,fork模板的方式简捷更酷。
  • 对中文的支持好。因为本来就是本土化的软件当然对中文全力支持。
  • 代码语法高亮。测试了一下默认的模板支持语法高亮。
  • 自带的模板更漂亮。自带的模版虽然不多,但是有说明是适合博客还是相册。
  • 网站自带网站分析工具。可以通过简单的浏览网页就可以知道网站的访问情况,虽然farbox在访问量达到1W之后要收费,按照目前的价格,但是我个人觉得比较值,希望farbox能够一直坚持做下去。做的好我愿意付费。

缺点:

  • 经过一下午的了解,暂时没有发现博客可以分类的功能,不过这个功能我暂时可以不需要。以后准备先写些博客在Github和Faxbox上同步更新,以便多比较比较这两种Geek的写博客方式。后来发现是有文章分类功能的,具体操作为将文章放入不同的文件夹中,文件夹的名字即为分类的名字,这种方式比jekyll的YAML语法方式要好用。
  • 感觉评论功能有点鸡肋。博客还没写完有事工作,准备续写时发现有人留言指正错误,想回复谢谢,发现无法直接回复。用多说代替之,方法为直接将多说的js脚本存入新建的comment_js.md文件中即可,非常简洁。
  • Linux下的用户不太方便。由于farbox软件仅有windows版和mac版,dropbox在linux有相应版本,在linux下的用户不能享受到farbox编辑工具带来的方便。相反在linux下结合git和vim来编写博客却比较方便。

PS:
Github博客地址:kuring.github.com
Fixbox博客地址:kuring.faxbox.com

0%