linux多线程 & IPC【10】自己写一个系统日志服务
来源:互联网 发布:艺术字设计软件下载 编辑:程序博客网 时间:2024/04/30 08:43
unix网络编程【13章 posix共享内存区】 中实现了一个简单的日志服务,使用共享内存实现了没有亲缘关系的进程间的通信。这个共享内存是采用内存映射文件的(用shm_open),匿名的共享内存实现有亲缘关系进程间的通信比较方便,没有亲缘关系的就不知道怎么实现了。
折腾了一个晚上加一个上午,终于整出来了。首先是头文件(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/shrm log_client log_servergcc log_server.c -o log_server -lpthread -g -lrtgcc log_client.c mylog.c -o log_client -lpthread -g -lrt
执行:
administrator@ubuntu:~/test/log_test$ ./log_server &[1] 2759administrator@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总用量 148drwxrwxrwt 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的时候报出总线错误,因为映射到内存里的文件大小为零,怎么可以执行写操作呢!
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_serveradministrator@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] 3440administrator@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 <--客户端 0process 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 <--客户端 1process 3589 add a msg to log (1, Fisrt log test! <--客户端打印的消息)administrator@ubuntu:~/test/log_test$ ./log_client <--客户端2process 3590 add a msg to log (2, Fisrt log test! <--客户端打印的消息)administrator@ubuntu:~/test/log_test$ ./log_client <--客户端3process 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多线程 & IPC【10】自己写一个系统日志服务
- 自己写一个linux的系统调用
- 写得一个linux系统服务脚本
- linux多线程 & IPC【11】《unix网络编程》的系统日志demo
- 多线程写到一个日志文件中
- 发一个自己写的服务程序
- asp.net 自己写一个 WindowS 服务
- Linux系统服务 1 ---- rSyslog日志服务
- Linux 基础学习篇10(系统日志服务)
- 本人自己写的一个银行系统
- 准备写一个自己的博客系统
- 自己通过要求,根据自己最近所学,简要写了一个linux下的server/client服务程序
- 用多线程写一个售票系统
- Linux中用C语言写系统日志
- Linux中用C语言写系统日志
- Linux中用C语言写系统日志
- Linux中用C语言写系统日志
- linux系统日志及其rsyslog服务
- 进程与线程区别
- HBase性能优化方法总结(一):表的设计
- SSDT Hook的妙用-对抗ring0 inline hook
- AJAX 入门笔记
- 搭建linux C语言开发环境——redhat9+vim+ctags+taglist+cscope
- linux多线程 & IPC【10】自己写一个系统日志服务
- 游戏笔记,敌人朝向玩家和跟踪代码
- android电源管理-概述
- jsp 多个按钮 页内提交
- C实现打印log到文件中的通用方面
- Ring3下注入DLL的另类方法,能过杀软和游戏NP(源码)
- Android实现伸缩弹力分布菜单效果
- 判断给定的图是否是有向无环图
- 多线程下的单例设计模式