漫谈消息队列

爱⌒轻易说出口 提交于 2020-01-20 01:55:40

消息队列

今天电话面试,被问及,为什么你框架设计都是用线程创建,不用进程。我说产品比较简单,没必要做那么多。现在想想这样的回答的确肤浅。
在聊进程间通讯前,先说下选用进程和线程的规章,
进程与线程的选择取决以下几点:

1、需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。
2、线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应
3、因为对CPU系统的效率使用上线程更占优,所以多机分布的用进程,多核分布用线程;
4、并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求;
5、需要更稳定安全时,适合选择进程;需要速度时,选择线程更好。

说白了就是:进程是独立堆栈,开销大。线程共享,响应快。由于线程共享,一个线程挂了,进程就挂了。进程独立,所以互不影响。**总结就是第五点:**需要更稳定安全时,适合选择进程;需要速度时,选择线程更好
那么既然是进程,那么进程间如何通讯呢?
进程通信有好多方式:管道,共享内存,消息队列,信号,套接字。
这里说下消息队列,这个是系统维护的IPC,也就说这是系统方法,不需要用户维护,可以理解成工具。
先说几个标准函数:
ftok函数: key_t ftok(const char *pathname, int proj_id);
功能:生成一个key(键值),唯一的。
参数:pathname:ftok根据路径名,提取文件信息,再根据这些文件信息及project ID合成key,该路径可以随便设置。该路径是必须存在的,ftok只是根据文件inode在系统内的唯一性来取一个数值,和文件的权限无关。
proj_id:proj_id是可以根据自己的约定,随意设置。这个数字,有的称之为project ID; 在UNIX系统上,它的取值是1到255。
返回:key(键值),唯一的。

msgget函数: int msgget(key_t key, int msgflg);
功能:创建或取得一个消息队列对象
key:ftok返回值。
msgflg:有flag|mode
flag:可以是0或者IPC_CREAT(不存在就创建)
mode:同文件权限一样
返回:消息队列对象的id,同一个key得到同一个对象

msgsnd函数: int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg);
功能:将msgp消息写入标识为msgid的消息队列
msqid:msgget返回值
msgp:
struct msgbuf {
long mtype; /
message type, must be > 0 /消息的类型必须>0,获取时,通过类型获取相应消息
char mtext[1]; /
message data */长度随意
…xxx…
…xxx…
};
msgsz:要发送的消息的大小 不包括消息的类型占用的4个字节
msgflg: 如果是0 当消息队列为满 msgsnd会阻塞;如果是IPC_NOWAIT 当消息队列为满时 不阻塞 立即返回
返回值:成功返回id 失败返回-1

msgrcv函数: ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:从标识符为msgid的消息队列里接收一个指定类型的消息 并 存储于msgp中 读取后 把消息从消息队列中删除
msqid:msgget返回值
msgp:存放消息的结构体
msgsz:要接收的消息的大小 不包含消息类型占用的4字节
msgtyp:为 0 表示无论什么类型 都可以接收,或者传入结构体的type值
msgflg:如果是0 标识如果没有指定类型的消息 就一直等待;如果是IPC_NOWAIT 则表示不等待

msgctl函数: int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:直接控制消息队列的行为
msgqid :消息队列对象的标识符。
cmd:

cmd 描述 struct msqid_ds *buf
IPC_STAT 取出系统保存的消息队列的msqid_ds 数据,并将其存入参数buf 指向的msqid_ds 结构中 传入指针
IPC_SET 设定消息队列的msqid_ds 数据中的msg_perm 成员。设定的值由buf 指向的msqid_ds结构给出 传入设置好的结构体地址
IPC_EMID 将队列从系统内核中删除 NULL

需要强调的是在IPC_STAT命令中队列的msqid_ds 数据中唯一能被设定的只有msg_perm 成员,其是ipc_perm 类型的数据。而ipc_perm 中能被修改的只有mode,pid 和uid 成员。其他的都是只能由系统来设定的。
返回值: 0 on success
-1 on error: errno = EACCES (No read permission and cmd is IPC_STAT)
EFAULT (Address pointed to by buf is invalid with
IPC_SET and IPC_STAT commands)
EIDRM (Queue was removed during retrieval)
EINVAL (msgqid invalid, or msgsz less than 0)
EPERM (IPC_SET or IPC_RMID command was
issued, but calling process does not have
write (alter) access to the queue)

#include "my.h"
typedef struct{
    long type;
    char name[20];
    int age;
}Msg;
int main()
{
    key_t key = ftok("/home/liudw",'6');
    printf("key:%x\n",key);
    int msgid = msgget(key,IPC_CREAT|O_WRONLY|0777);
    if(msgid<0)
    {   
        perror("msgget error!");
        exit(-1);
    }   
    Msg m;
    puts("please input your type name age:");
    scanf("%ld%s%d",&m.type,m.name,&m.age);
    msgsnd(msgid,&m,sizeof(m)-sizeof(m.type),0);
    return 0;
}
#include "my.h"
typedef struct{
    long type;
    char name[20];
    int age;
}Msg;
int main()
{
    key_t key = ftok("/home/liudw",'6');
    printf("key:%x\n",key);
    int msgid = msgget(key,O_RDONLY);
    if(msgid<0)
    {   
        perror("msgget error!");
        exit(-1);
    }   
    Msg rcv;
    long type;
    puts("please input type you want!");
    scanf("%ld",&type);
    msgrcv(msgid,&rcv,sizeof(rcv)-sizeof(type),type,0);
    printf("rcv--name:%s age:%d\n",rcv.name,rcv.age);
    msgctl(msgid,IPC_RMID,NULL);
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!