您的位置:首页 > 运维架构 > Linux

linux多线程 & IPC【10】自己写一个系统日志服务

2013-05-13 11:25 741 查看
    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/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$




                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐