本文始作于2012年2月16日,刊登于人人网,于2013年2月13日迁移至此
今天晚上可算把pc.c写个像样出来:
1 #include <stdio.h> 2 #include <string.h> 3 #include <pthread.h> 4 #include <stdlib.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <semaphore.h> 8 #include <sys/types.h> 9 #include <sys/stat.h> 10 11 void err_sys(char *); 12 13 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 14 15 /* 测试信号量 */ 16 main() 17 { 18 int fd, buf, cfo, i, j, n; 19 sem_t *semp, *full, *empty; 20 21 buf = 0; 22 i = 1; 23 cfo = 0; 24 n = 3; 25 sem_unlink("mutex"); 26 sem_unlink("full"); 27 sem_unlink("empty"); 28 semp = sem_open("mutex", O_CREAT, 0777, 1); 29 full = sem_open("full", O_CREAT, 0777, 0); 30 empty = sem_open("empty", O_CREAT, 0777, 10); 31 fd = open("/home/spiderhunt2011/lab/tmp", O_RDWR); 32 //printf("semp->value: %ld\n", semp->__align); 33 if (fork() == 0) 34 while (1) { 35 sem_wait(empty); 36 sem_wait(semp); 37 //pthread_mutex_lock(&mutex); 38 cfo = lseek(fd, 0, SEEK_CUR); 39 lseek(fd, 0, SEEK_END); 40 i = write(fd, &buf, sizeof(int)); 41 lseek(fd, cfo, SEEK_SET); 42 printf("write: %d\n", buf++); 43 //pthread_mutex_unlock(&mutex); 44 sem_post(semp); 45 sem_post(full); 46 } 47 else { 48 for (i = 0; i < n; ++i) { 49 if (fork() == 0) { 50 while (1) { 51 sem_wait(full); 52 sem_wait(semp); 53 //pthread_mutex_lock(&mutex); 54 read(fd, &buf, sizeof(int)); 55 printf("%dread: %d\n", i, buf); 56 //pthread_mutex_unlock(&mutex); 57 sem_post(semp); 58 sem_post(empty); 59 } 60 } 61 } 62 } 63 close(fd); 64 }
出来的效果:

上面我选中的就是这四个a.out进程。还有给你看一下缓冲去里面是什么样的:

原来我们平时向.c文件里写的都是ASCII码而不是真正的数字啊,这才是真正的数字,文本编辑器是把文件里面的内容按照ASCII一个一个显示的,囧。
这个实验果然够难啊,因为这个实验里真的是是实打实的编码啊,而且这不是数据结构的编码,可以算是直接操作底层的操纵编码了,我觉得这次实验才是真正意义上的操作系统编码。这实验的前半部分,也就是刚刚完成的这个pc.c代表的是在用户态对内核态的调用,训练我们掌握未知系统调用的能力。sunner的意思应该是让我们经历一次从未知系统调用到熟练掌握的历程。
在技术方面,我学会了如何使用open(),write(),read(),这三个系统调用,还有lseek()。其实学这四个函数更重要的是了解掌握Linux是怎么处理文件的,尤其在学lseek()的时候。原来系统对文件最基本的读取是用cfo这个东西来实现的,以前在学C++的时候就总不明白为什么在文件读取的时候会自动的读取接下来的位置而不是重头再读取,如果我程序需要重头再读取的话该怎么办呢?现在知道了,其实在对任何文件操作的时候都在挪动文件指针这个东西,操作一次就挪动一下,每次操作都标记好现在读到哪里了或者写到哪里了。如果想从某个位置读某个位置的文件就用lseek()改变cfo,这都是可以的,这其实都是可以灵活操作的。还有就是怎么使用POSIX的信号量函数sem_open(),sem_wait(),sem_post()。其实前面的那个垃圾的pc.c之所以用不了应该就是在用完信号量之后都没有unlink(),所以第一次运行的结果跟之后每次运行的效果差那么大。通过一步一步的小尝试,这都是可以被检测出来从而避免的。都怪当时太着急了,觉得差不多了就开始写最终的pc.c,最后连修改的勇气都没有,根本没法调试,因为都不知道那个地方错了,因为不可信的地方太多了,可能出错的地方太多了,没法改了,就得重来!除此之外就是顺便学会了怎么在Linux下查看进程运行情况,以及怎么杀死进程,在运行pc.c的时候因为是一个父进程生出来几个子进程,所以按Ctrl+C根本杀不死他们,所以就得用终端暴力解决问题。要杀死进程首先得知道他的id,用shell> ps -ef | grep a.out就可以显示出名字叫做a.out的进程的信息了,信息里面的第二排就是他的id号,然后shell> kill 2353 2354 2355 2356就杀死了这几个小进程。shell> pstree是个人性化的指令,它打印了系统当前的“进程树”,很直观地显示各个进程之间的关系,他们是怎么来的,很好用。
在经验方面,就是明白了,怎么做实验。就是通过一次一次的尝试,不断的写代码,一次加进去一个功能,然后一次一次的调试,一次一次的改,让你的代码一步一步接近最终的功能,向最终代码一小步一小布稳定的挪过去,如果太着急了,直接一次性加了一堆功能,那出来的代码就不可信了。因为每个功能每个加进来之后都需要严格的测试和调试以及调整跟之前代码的配合才允许加入下一个功能的。我觉得学到的经验才是更重要的收获。毕竟这只是给今后的一次示范,就像大一的时候学C++一样,目的倒不是让你掌握C++,而是给你今后学别的语言做一次示范,给以后打个样子。目的是为了让你看看,学一门计算机语言会经历哪些事,以后在学习新的语言的时候就明白先要怎么办,然后再怎么办,每个地方该怎么学,就心里有数了,不用再找老师了。事实证明,现在C++大家基本上都不记得什么了,汇编更是,都学了自己喜欢的语言,但是在学别的语言的事后都马上就学会了,基本上驾"新"就熟,轻松加愉快,这都是C/C++当时给我们打得底子,这些东西不是白学得,一直都在起作用,你不知道而已,所以还是要郑重感谢当年的C++。
最后在把用到的参考的链接发一下:
POSIX semaphore: sem_open, sem_close, sem_post, sem_wait
这个实验里涉及到的资料很难找,基本上都百度不到想要的,所以就找到这两个珍贵的文献资源,另外我发先“博客园”里面的文章很适合我的胃口啊,因为我发现经常有这种情况,就是查某个相关问题,在百度上很难搜到,最后搜到的位置都是博客园,所以我觉得博客园里面一定住着一堆跟我一样的喜欢底层的搞技术的人。更巧的是,我在博客园还发现了有个跟我现在做一样事情的人,他是大三软件学院的,他把自己的实验都几下来了,而且讲的非常仔细,这可非常难得。于是我就决定注册了,在看“博客园”资料的时候发现他的排名竟然在CSDN前面,在博客界的前十名,而且是专门面向IT的,在注册的时候发现要是想开通博客必须得额外申请,而且还是真人手工判定,我觉得这种环境认真,然后我就申请了,今天下午起来发现通过了。这是我第一个博客,都不知道写点啥好,还有点舍不得写。呃。。。选主题都斟酌了好长时间。我打算现在这住着,如果情况乐观的话,就把人人上的东西搬到上面去。等用博客用熟了,将来买台主机搭建真正的个人网站,不过主机是租的,不是永远都属于你的,所以博客园还是个长久的选择,毕竟前十名的不会轻易倒下,所以还是两边都备份吧,以后的事。现在先用好博客。
我的博客园:SpiderHunt2011不过现在还什么都没有呢,以后会有的。
浙公网安备 33010602011771号