linux多线程 & IPC【10】自己写一个系统日志服务
2013-05-13 11:25
741 查看
unix网络编程【13章 posix共享内存区】 中实现了一个简单的日志服务,使用共享内存实现了没有亲缘关系的进程间的通信。这个共享内存是采用内存映射文件的(用shm_open),匿名的共享内存实现有亲缘关系进程间的通信比较方便,没有亲缘关系的就不知道怎么实现了。
折腾了一个晚上加一个上午,终于整出来了。首先是头文件(mylog.h),给出了结构体的定义和一些宏定义:
然后是服务器端的实现:log_server.c
提供给客户端的函数写在mylog.c中:
客户端调用:log_client.c
编译:
执行:
注意:
【1】
【2】
顺序不要乱,先等待full信号量,再等待mutex信号量,否则会造成死锁。
【3】
#define FILE_MODE 00600
服务器端和客户端打开文件的模式很重要,必须保证user的读写权限。可以向上面这样,也可以:
【4】shm_open的O_EXCL可要可不要,因为已经用unlink删除过了。而客户端则不能用O_EXEL。
【5】mmap的时候,应该使用MAP_SHARED,不要用MAP_PRIVATE。前者将修改立即写入文件中,而不同进程用读文件来通信。如果不立即写入,那么跟没写有
多少区别呢?至少会引起很久的延迟,导致出错。
服务器用shared,客户端用private,会导致客户端的消息得不到处理,因为服务器从来就没有接到过消息:
服务器用private,会导致初始化工作没有写入文件,那么启动客户端的时候,假设默认初始化内容为0,那么就会认为空的槽已经没有了,就一直阻塞在那里。:
【6】将
这样,启动服务器以后,在10秒内运行多次客户端程序,里面的槽就会填满,然后再次调用客户端的时候,就会阻塞在那里等待信号量了。而服务器端每10秒监测是否需要处理,如果需要,就去拿到mutex锁,随后处理一条记录,再交出mutex锁,槽就空出来一个了。运行过程:
折腾了一个晚上加一个上午,终于整出来了。首先是头文件(mylog.h),给出了结构体的定义和一些宏定义:
#ifndef _MYLOG_H_ #define _MYLOG_H_ #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/mman.h> #include <semaphore.h> #define N_MSG 64 // message的个数 #define LEN_MSG 256 // 每个message的长度 #define FILE_MODE S_IRUSR | S_IWUSR //rwxrwxrwx #define px_ipc_name(x) (x) //空的宏定义,暂时没有用 struct msg_ctl_str { sem_t mutex; sem_t empty; sem_t full; int nput; // 下一个可以由生产者放置log信息的index序号 int nread; // 已经处理完毕的消息序号,初始为-1,这样,下一个要处理的就是0 char msgdata[N_MSG][LEN_MSG]; } msg_ctl_str; void my_syslog(const char *msg, const char *name); //给客户端的函数 #endif
然后是服务器端的实现:log_server.c
#include "mylog.h" int main (int argc, char *argv[]) { struct msg_ctl_str *pmc; int fd; int i; char arg[256]; //缺省参数 if(argc<2) strncpy(arg,"tmp_name",100); else strncpy(arg, argv[1],100); //防止重名,先删除这个名字的共享内存对象 shm_unlink(arg); fd = shm_open(arg, O_RDWR|O_CREAT|O_EXCL,S_IRUSR | S_IWUSR); if(fd==-1) { printf("open failed!\n"); exit(2); } //内存映射 pmc = mmap (NULL, sizeof (struct msg_ctl_str), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ftruncate(fd,sizeof(struct msg_ctl_str)); if (pmc == MAP_FAILED) { printf ("map failed!\n"); exit (3); } //初始化 sem_init (&(pmc->mutex), 1, 1); //信号量初始化为1,用于互斥访问 sem_init (&(pmc->empty), 1, N_MSG); //信号量初始化为N_MSG,表示有N_MSG个空槽可用 sem_init (&(pmc->full), 1, 0); //信号量初始化为0,表示初始时没有消息要处理 pmc->nput=0; //下一个可以放消息的位置序号从0开始 pmc->nread=-1; // 已经处理完毕的消息index for (i=0; i<N_MSG;i++) //初始化数据区 { pmc->msgdata[i][0]='\0'; } // 循环处理 while (1) { sem_wait(&(pmc->full)); //等待有消息的到来,没有消息的话一直阻塞在这里 sem_wait(&(pmc->mutex)); //取得互斥 // 处理!就是打印到标准输出而已 ++(pmc->nread); pmc->nread %= N_MSG; printf("The %d th msg:%s\n",pmc->nread, pmc->msgdata[pmc->nread]); // sem_post(&(pmc->mutex)); sem_post(&(pmc->empty)); //empty的位置增加了一个 } //应该不会执行貌似 if (munmap (pmc, sizeof (msg_ctl_str) ) == -1) { printf ("ummap!\n"); exit (2); } return 0; }
提供给客户端的函数写在mylog.c中:
#include "mylog.h" void my_syslog(const char *msg, const char *name) { struct msg_ctl_str *pmc; int fd; char arg[256]; if(name==NULL) strncpy(arg,"tmp_name",100); else strncpy(arg, name,100); fd = shm_open(px_ipc_name(arg), O_RDWR, FILE_MODE); if(fd==-1) { printf("open failed!\n"); exit(2); } pmc = mmap (NULL, sizeof (msg_ctl_str), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (pmc == MAP_FAILED) { printf ("map failed!\n"); exit (3); } sem_wait(&(pmc->empty)); //等待有消息的到来,没有消息的话一直阻塞在这里 sem_wait(&(pmc->mutex)); //取得互斥 // 处理!就是打印到标准输出而已 strncpy(pmc->msgdata[pmc->nput], msg, 255); printf("process %d add a msg to log (%d, %s)\n", getpid(), pmc->nput, pmc->msgdata[pmc->nput]); pmc->msgdata[pmc->nput][255]='\0'; pmc->nput++; pmc->nput = pmc->nput%N_MSG; // sem_post(&(pmc->mutex)); sem_post(&(pmc->full)); //empty的位置增加了一个 }
客户端调用:log_client.c
#include "mylog.h" int main() { my_syslog("Fisrt log test!\n", NULL); return 0; }
编译:
#!/bin/sh rm log_client log_server gcc log_server.c -o log_server -lpthread -g -lrt gcc log_client.c mylog.c -o log_client -lpthread -g -lrt
执行:
administrator@ubuntu:~/test/log_test$ ./log_server & [1] 2759 administrator@ubuntu:~/test/log_test$ jobs [1]+ 运行中 ./log_server & administrator@ubuntu:~/test/log_test$ ./log_client process 2760 add a msg to log (0, Fisrt log test! ) The 0 th msg:Fisrt log test! administrator@ubuntu:~/test/log_test$ ./log_client process 2764 add a msg to log (1, Fisrt log test! <--这句话是客户端产生的 ) The 1 th msg:Fisrt log test! <--这句话时服务器产生的 administrator@ubuntu:~/test/log_test$ ./log_client process 2765 add a msg to log (2, Fisrt log test! <--这句话是客户端产生的 ) The 2 th msg:Fisrt log test! <--这句话时服务器产生的 administrator@ubuntu:~/test/log_test$ ll /run/shm 总用量 148 drwxrwxrwt 2 root root 200 2013-05-13 11:14 ./ drwxr-xr-x 20 root root 740 2013-05-13 09:58 ../ -rw------- 1 administrator administrator 4232 2013-05-13 11:02 lp -rw------- 1 administrator administrator 4232 2013-05-13 10:53 name1 -r-------- 1 administrator administrator 67108904 2013-05-13 10:28 pulse-shm-107720862 -r-------- 1 administrator administrator 67108904 2013-05-13 09:58 pulse-shm-2781352149 -r-------- 1 lightdm lightdm 67108904 2013-05-13 09:58 pulse-shm-2938786323 -r-------- 1 administrator administrator 67108904 2013-05-13 09:58 pulse-shm-3468252451 -r-------- 1 administrator administrator 67108904 2013-05-13 09:58 pulse-shm-4062759694 -rw------- 1 administrator administrator 16440 2013-05-13 11:14 tmp_name
注意:
【1】
ftruncate(fd,sizeof(struct msg_ctl_str));这句话一定要加上,否则会出现总线错误,搞了好多次了。不然的话,服务器端在初始化mutex的时候报出总线错误,因为映射到内存里的文件大小为零,怎么可以执行写操作呢!
【2】
sem_wait(&(pmc->full)); //等待有消息的到来,没有消息的话一直阻塞在这里 sem_wait(&(pmc->mutex)); //取得互斥 // 处理!就是打印到标准输出而已 ++(pmc->nread); printf("The %d th msg:%s\n",pmc->nread, pmc->msgdata[pmc->nread]); // sem_post(&(pmc->mutex)); sem_post(&(pmc->empty)); //empty的位置增加了一个
顺序不要乱,先等待full信号量,再等待mutex信号量,否则会造成死锁。
【3】
#define FILE_MODE 00600
服务器端和客户端打开文件的模式很重要,必须保证user的读写权限。可以向上面这样,也可以:
#define FILE_MODE S_IRUSR | S_IWUSR如何没有权限,会导致shm_open打开失败。
【4】shm_open的O_EXCL可要可不要,因为已经用unlink删除过了。而客户端则不能用O_EXEL。
【5】mmap的时候,应该使用MAP_SHARED,不要用MAP_PRIVATE。前者将修改立即写入文件中,而不同进程用读文件来通信。如果不立即写入,那么跟没写有
多少区别呢?至少会引起很久的延迟,导致出错。
服务器用shared,客户端用private,会导致客户端的消息得不到处理,因为服务器从来就没有接到过消息:
administrator@ubuntu:~/test/log_test$ ./log_server & [2] 3392 [1] 已终止 ./log_server administrator@ubuntu:~/test/log_test$ jobs [2]+ 运行中 ./log_server & administrator@ubuntu:~/test/log_test$ ./log_client process 3393 add a msg to log (0, Fisrt log test! ) administrator@ubuntu:~/test/log_test$ ./log_client process 3394 add a msg to log (0, Fisrt log test! ) administrator@ubuntu:~/test/log_test$ ./log_client process 3395 add a msg to log (0, Fisrt log test! <--没有更新计数器,所以每次都是写入到第0个位置——其实并没有真正写入文件呢。 ) administrator@ubuntu:~/test/log_test$
服务器用private,会导致初始化工作没有写入文件,那么启动客户端的时候,假设默认初始化内容为0,那么就会认为空的槽已经没有了,就一直阻塞在那里。:
administrator@ubuntu:~/test/log_test$ ./log_server & [1] 3440 administrator@ubuntu:~/test/log_test$ jobs [1]+ 运行中 ./log_server & administrator@ubuntu:~/test/log_test$ ./log_client
【6】将
N_MSG 64改为4.然后在服务器循环的最后加入10秒延迟:
// 循环处理 while (1) { sem_wait(&(pmc->full)); //等待有消息的到来,没有消息的话一直阻塞在这里 sem_wait(&(pmc->mutex)); //取得互斥 // 处理!就是打印到标准输出而已 ++(pmc->nread); printf("The %d th msg:%s\n",pmc->nread, pmc->msgdata[pmc->nread]); // sem_post(&(pmc->mutex)); sem_post(&(pmc->empty)); //empty的位置增加了一个 usleep(10000000); }
这样,启动服务器以后,在10秒内运行多次客户端程序,里面的槽就会填满,然后再次调用客户端的时候,就会阻塞在那里等待信号量了。而服务器端每10秒监测是否需要处理,如果需要,就去拿到mutex锁,随后处理一条记录,再交出mutex锁,槽就空出来一个了。运行过程:
administrator@ubuntu:~/test/log_test$ ./log_server & <--服务器启动 [1] 3587 administrator@ubuntu:~/test/log_test$ jobs <--查看后台作业 [1]+ 运行中 ./log_server & administrator@ubuntu:~/test/log_test$ ./log_client <--客户端 0 process 3588 add a msg to log (0, Fisrt log test! <--客户端打印的消息 ) The 0 th msg:Fisrt log test! <--服务器马上进行了处理,随后休眠10秒。此时所有的槽都空着 administrator@ubuntu:~/test/log_test$ ./log_client <--客户端 1 process 3589 add a msg to log (1, Fisrt log test! <--客户端打印的消息 ) administrator@ubuntu:~/test/log_test$ ./log_client <--客户端2 process 3590 add a msg to log (2, Fisrt log test! <--客户端打印的消息 ) administrator@ubuntu:~/test/log_test$ ./log_client <--客户端3 process 3591 add a msg to log (3, Fisrt log test! <--客户端打印的消息 ) administrator@ubuntu:~/test/log_test$ ./log_client <--客户端4,此时加入到0位置,所有的槽都满了 process 3592 add a msg to log (0, Fisrt log test! ) administrator@ubuntu:~/test/log_test$ ./log_client <--客户端5,此时没有空槽了,阻塞 The 1 th msg:Fisrt log test! <--10秒时间到,服务器处理位置【1】处的消息 process 3593 add a msg to log (1, Fisrt log test! <--位置1空了出来,客户端5的请求得到了响应,消息被加入到位置1 ) administrator@ubuntu:~/test/log_test$ ./log_client <--客户端6,此时没有空槽,阻塞 The 2 th msg:Fisrt log test! <--又过了10秒,服务器处理消息,位置2空了出来 process 3594 add a msg to log (2, Fisrt log test! <--客户端6的请求得到响应,被加入到位置2 ) administrator@ubuntu:~/test/log_test$ The 3 th msg:Fisrt log test! <--服务器端每10秒依次处理剩下的3,0,1,2消息。 The 0 th msg:Fisrt log test! The 1 th msg:Fisrt log test! The 2 th msg:Fisrt log test! administrator@ubuntu:~/test/log_test$
相关文章推荐
- Linux 基础学习篇10(系统日志服务)
- 运维笔记11(linux系统日志rsyslogd,采集日志分类存放服务journalctld,时间同步服务chronyd)
- 171206 8周3次课 rsync通过服务同步、linux系统日志、screen工具
- 利用Unix/Linux的IPC机制仿真一个音乐厅门票订售系统
- mysql dba系统学习(10)innodb引擎的redo log日志的原理 mysql dba系统学习(11)管理innodb引擎的redo log日志的一个问题
- 在linux或者unix操作系统中在系统的引导的时候会开启很多服务,这些服务就叫做守护进程。守护进程是在后台运行不与任何控制终端关联,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于
- 自己动手做一个迷你 Linux 系统
- 10.32/10.33 rsync通过服务同步 10.34 linux系统日志 10.35 scre
- Linux入职基础-5.24_可执行程序arpwatch做成系统一个服务(应用实战8)
- linux系统编程:自己动手写一个who命令
- Linux-09、10-如何选择合适的Linux系统版本学习&服务器领域如何选择适合的CentOS Linux版本(L003-23,24)
- rsync通过服务同步,Linux系统日志,screen工具
- 12.6 rsync通过服务同步 linux系统日志 screen工具
- 32.rsync服务同步,Linux系统日志,screen工具
- linux系统编程:自己动手写一个cp命令
- rsync通过服务同步、 linux系统日志 、screen工具
- 刚学习时自己写的一个linux系统初始化脚本
- 发现维护的自己编写linux 系统检查脚本一个bug (syslog\message)
- 一个同步日志系统的简单实现 log for c (linux 平台)
- x4412开发板&ibox卡片电脑项目实战10-使用buildroot搭建linux文件系统