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的时候报出总线错误,因为映射到内存里的文件大小为零,怎么可以执行写操作呢!


【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_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$ 



原创粉丝点击