消息队列使用+关于printf缓冲区的问题
简单来讲
printf是一个行缓冲函数,先写到缓冲区,满足条件后,才将缓冲区刷到对应文件中,刷缓冲区的条件如下:
1 缓冲区填满
2 写入的字符中有"\n" "\r"
3 调用fflush手动刷新缓冲区
4 调用scanf要从缓冲区中读取数据时,也会将缓冲区内的数据刷新
当然,执行printf的进程或线程结束后,会自动调用fflush刷新缓冲区。
缓冲区大小1024字节。
重点只有这些,后面的可以不用看(* ̄︶ ̄)
故事的开头
某次用C在写一个简单的消息队列使用时,出现了一个问题,服务端在接收消息时使用while循环使其可以一直接收,但是发现循环中的printf不起作用了????
上代码
//msgcom.h
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#define MSGKEY 1234
struct msgtype
{
long mtype;
int text;
};
//req.c
#include "msgcom.h"
void main()
{
struct msgtype buf;
int qid,pid;
qid = msgget(MSGKEY,IPC_CREAT|0666);
buf.mtype=1;
printf("Please input a number:");
scanf("%d",&buf.text);
msgsnd(qid,&buf,sizeof(buf.text),IPC_NOWAIT|04000);
msgrcv(qid,&buf,sizeof(buf.text),2,MSG_NOERROR);
printf("Request received a message from server: %d\n",buf.text);
}
//serv.c
#include "msgcom.h"
int main()
{
struct msgtype buf;
int qid;
if ((qid=msgget(MSGKEY,IPC_CREAT|0666))==-1)
return (-1);
while (1)
{
msgrcv(qid,&buf,sizeof(buf.text),1,MSG_NOERROR);
printf("Server receive a request from process: %d\n",buf.text);
buf.mtype=2;
buf.text=buf.text+1;
msgsnd(qid,&buf,sizeof(buf.text),IPC_NOWAIT|04000);
}
}
分析一下
代码看着是不是没有什么问题,实际上也确实没有什么大问题。但是!如果把serv.c中
printf("Server receive a request from process: %d\n",buf.text);
替换成
printf("Server receive a request from process: %d",buf.text);
问题就来了
req.c运行毫无问题,serv.c却怎么也不输出。
看过前面的缓冲区的知识当然一眼看穿,但是无奈当时没想到啊。
debug的心路历程
第一阶段:先前前后后都printf一下吧,发现什么也没有发生,没有任何输出。
第二阶段:msgrcv是会阻塞的,把msgrcv去掉试试,emmmmm,疯狂输出
所以是因为msgrcv阻塞了,所以后面的语句没有执行?并不,不然为什么第一阶段放在msgrcv之前的printf也没有输出,而且req.c正常输出了serv.c返回的数据?
第三阶段:req.c是正常的,看看有什么区别啊。第一眼,while循环。好的,循环去掉,正常了。
那么,为什么呢?
捋捋思路,首先,这样的执行看不出问题,从req那边看是正常的,这边没有输出,也看不出异常
while (1)
{
msgrcv(qid,&buf,sizeof(buf.text),1,MSG_NOERROR);
// printf("Server receive a request from process: %d",buf.text);
buf.mtype=2;
buf.text=buf.text+1;
msgsnd(qid,&buf,sizeof(buf.text),IPC_NOWAIT|04000);
}
下面这样,疯狂输出,这个很好理解
while (1)
{
// msgrcv(qid,&buf,sizeof(buf.text),1,MSG_NOERROR);
printf("Server receive a request from process: %d",buf.text);
buf.mtype=2;
buf.text=buf.text+1;
msgsnd(qid,&buf,sizeof(buf.text),IPC_NOWAIT|04000);
}
下面这样,没有问题
// while (1)
// {
msgrcv(qid,&buf,sizeof(buf.text),1,MSG_NOERROR);
printf("Server receive a request from process: %d",buf.text);
buf.mtype=2;
buf.text=buf.text+1;
msgsnd(qid,&buf,sizeof(buf.text),IPC_NOWAIT|04000);
// }
所以,问题代码精简为
while (1)
{
msgrcv(qid,&buf,sizeof(buf.text),1,MSG_NOERROR);
printf("Server receive a request from process: %d",buf.text);
}
三者不能共存?逗呢
似乎陷入僵局。
但是!前面有一个很重要的线索,就是在msgrcv甚至while之前加printf,也不输出。
像这样
printf("test");
while (1)
{
msgrcv(qid,&buf,sizeof(buf.text),1,MSG_NOERROR);
printf("Server receive a request from process: %d",buf.text);
}
test也不输出,没道理啊。不应该执行到printf(“test”)的时候马上输出吗?没有立即输出,没有立即输出……缓冲了?缓冲区?
想到这个,上网搜索printf+缓冲区,看到文章最开头的知识点,OK,破案了。
printf没有加"\n",不刷新,不输出。
缓冲区满?似乎1024字节,一次算你五十个,你得不停尝试20次,才有可能看到结果(尝试了一下,二十四次之后就一次性输出了(* ̄︶ ̄))。
去掉msgrcv,不阻塞,while疯狂循环,缓冲区迅速填满输出,所以看上去正常输出了,其实不是一个一个输出的,是一次输一堆。
去掉while循环,一次执行,程序结束之后自动刷新,所以输出。
结案。