1、消息队列
特点:
1、消息队列中的消息是有类型的。
2、消息队列中的消息是有格式的。
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、从消息队列中读出消息,消息队列中对应的数据都会被删除。
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中
注意:
消息队列由消息队列ID来唯一标识
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列可以按照类型来发送/接收消息
消息队列创建函数
1、ftok()
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:通过文件名和目标值共同创造一个键值并返回值
参数:
pathname:任意一个文件名(文件名或者目录名)
proj_id:目标值,范围一般是0~127
返回值:
成功:键值
失败:-1
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
功能:获取文件的信息
参数:
pathname:文件名
buf:文件信息结构体
struct stat {
dev_t st_dev; 设备号
ino_t st_ino; inode结点
mode_t st_mode; 权限
nlink_t st_nlink; 硬链接的个数
uid_t st_uid; 用户id
gid_t st_gid; 用户组id
dev_t st_rdev; 特殊文件的设备号
off_t st_size; 文件大小
blksize_t st_blksize; 文件锁
blkcnt_t st_blocks; 文件块大小
...
}
返回值:
成功:0
失败:-1
例如:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <unistd.h>
#define PATHNAME "file.txt"
#define PRO_ID 100
int main(int argc, char const *argv[])
{
//使用ftok得到的键值是由十六进程的目标值的后两位以及
//当前文件的设备号的后两位以及文件的inode节点号的后四位组成
//使用ftok函数获取键值
key_t key;
if((key = ftok(PATHNAME, PRO_ID)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("key = %#x\n", key);
printf("pro_id = %#x\n", PRO_ID);
//使用stat函数获取文件信息
struct stat buf;
if(stat(PATHNAME, &buf) == -1)
{
perror("fail to stat");
exit(1);
}
printf("dev_num = %#x\n", (int)buf.st_dev);
printf("inode_num = %#x\n", (int)buf.st_ino);
return 0;
}
2、msgget()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:创建一个消息队列,得到消息队列的id
参数:
key:键值,唯一的键值确定唯一的消息队列
如何设置键值:
方法1:任意分配一个数字,但是不具有唯一性
方法2:使用ftok函数获取键值
msgflg:消息队列的访问权限,
一般设置为 IPC_CREAT | IPC_EXCL | 0777
或者 IPC_CREAT | 0777
返回值:
成功:消息队列的id
失败:-1
msgctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:设置或者获取消息队列的信息
参数:
msqid:指定的消息队列的id
cmd:具体操作的指令
IPC_SET 设置属性
IPC_STAT 获取属性
IPC_RMID 删除消息队列
msqid_ds:设置或者获取消息队列的属性
返回值:
成功:0
失败:-1
例如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define err_log(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
err_log("fail to ftok");
}
//使用msgget函数创建一个消息队列
int msgid;
if((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
err_log("fail to msgget");
}
printf("msgid = %d\n", msgid);
system("ipcs -q");
//使用msgctl函数删除消息队列
#if 0
if(msgctl(msgid, IPC_RMID, NULL) == -1)
{
err_log("fail to msgctl");
}
system("ipcs -q");
#endif
return 0;
}
3、msgsnd()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向指定的消息队列发送数据(写操作)
参数:
msqid:消息队列的id
msgp:要写入的数据,需要自己定义结构体
struct struct_name{
long mtype; //消息的编号,必须大于0
char mtext[128]; //消息正文,可以定义多个成员
}
msgsz:消息正文的大小,不包括消息的编号长度
msgflg:标志位
0 阻塞
IPC_NOWAIT 非阻塞
返回值:
成功:0
失败:-1
例如
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define N 128
#define err_log(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
typedef struct{
long msg_type;
char msg_text[N];
}MSG;
#define MSGTEXT_SIZE sizeof(MSG) - sizeof(long)
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
err_log("fail to ftok");
}
//使用msgget函数创建一个消息队列
int msgid;
if((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
err_log("fail to msgget");
}
//printf("msgid = %d\n", msgid);
system("ipcs -q");
//向消息队列发送数据
MSG msg1 = {1, "1:hello world"};
MSG msg2 = {5, "5:nihao chengdu"};
MSG msg3 = {4, "4:hello kitty"};
MSG msg4 = {8, "8:welcome to zpj"};
MSG msg5 = {2, "2:nihao beijing"};
if(msgsnd(msgid, &msg1, MSGTEXT_SIZE, 0) == -1)
{
err_log("fail to msgsnd");
}
if(msgsnd(msgid, &msg2, MSGTEXT_SIZE, 0) == -1)
{
err_log("fail to msgsnd");
}
if(msgsnd(msgid, &msg3, MSGTEXT_SIZE, 0) == -1)
{
err_log("fail to msgsnd");
}
if(msgsnd(msgid, &msg4, MSGTEXT_SIZE, 0) == -1)
{
err_log("fail to msgsnd");
}
if(msgsnd(msgid, &msg5, MSGTEXT_SIZE, 0) == -1)
{
err_log("fail to msgsnd");
}
system("ipcs -q");
//使用msgctl函数删除消息队列
#if 0
if(msgctl(msgid, IPC_RMID, NULL) == -1)
{
err_log("fail to msgctl");
}
system("ipcs -q");
#endif
return 0;
}
4、msgrcv()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,
long msgtyp, int msgflg);
功能:从消息队列中接收数据(读操作),接收的数据会从消息队列中删除
参数:
msqid:消息队列id
msgp:保存接收到的数据的结构体
struct struct_name{
long mtype; //消息的编号,必须大于0
char mtext[128]; //消息正文,可以定义多个成员
}
msgsz:消息正文的大小
msgtyp:设置要接收哪个消息
0 按照写入消息队列的顺序依次读取
>0 只读取消息队列中消息编号为当前参数的第一个消息
<0 只读取消息队列中小于等于当前参数的绝对中内最小的第一个消息
msgflg:标志位
0 阻塞
IPC_NOWAIT 非阻塞
返回值:
成功:接收到的消息正文的长度
失败:-1
例如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define N 128
#define err_log(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
typedef struct{
long msg_type;
char msg_text[N];
}MSG;
#define MSGTEXT_SIZE sizeof(MSG) - sizeof(long)
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
err_log("fail to ftok");
}
//使用msgget函数创建一个消息队列
int msgid;
if((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
err_log("fail to msgget");
}
//printf("msgid = %d\n", msgid);
system("ipcs -q");
//从消息队列中读取数据
//读取的数据根据msgrcv的第四个参数决定,如果没有满足条件的数据,则msgrcv函数将会阻塞,直到有数据为止
MSG msg;
if(msgrcv(msgid, &msg, MSGTEXT_SIZE, -4, 0) == -1)
{
err_log("fail to msgrcv");
}
printf("recv_msg = %s\n", msg.msg_text);
system("ipcs -q");
//使用msgctl函数删除消息队列
#if 0
if(msgctl(msgid, IPC_RMID, NULL) == -1)
{
err_log("fail to msgctl");
}
system("ipcs -q");
#endif
return 0;
}
2、共享内存
共享内存是一种最为高效的进程间通信方式,进程可以直接读写物理内存,而不需要任何数据的拷贝
为了在多个进程间交换信息,物理内存留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间,进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等 。
相关函数
1、shmget()
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:创建一个共享内存
参数:
key:键值,唯一的键值确定唯一的共享内存
size:创建的共享内存的大小
shmflg:共享内存的访问权限,
一般为 IPC_CREAT | 0777
返回值:
成功:共享内存的id
失败:-1
2、shmctl()
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:设置或者获取共享内存你的属性
参数:
shmid:共享内存的id
cmd:执行操作的命令
IPC_STAT 获取共享内存的属性
IPC_SET 设置共享内存的属性
IPC_RMID 删除共享内存
shmid_ds:共享内存的属性结构体
返回值:
成功:0
失败:-1
例如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define err_log(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
int main(int argc, char const *argv[])
{
//获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
err_log("fail to ftok");
}
//创建一个共享内存
int shmid;
if((shmid = shmget(key, 500, IPC_CREAT | 0777)) == -1)
{
err_log("fail to shmget");
}
printf("shmid = %d\n", shmid);
system("ipcs -m");
//删除一个共享内存
#if 0
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
err_log("fail to shmctl");
}
system("ipcs -m");
#endif
return 0;
}
3、shmat()
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存
参数
shmid:共享内存的id
shmaddr:映射的地址,设置为NULL为系统自动分配
shmflg:标志位
0:共享内存具有可读可写权限。
SHM_RDONLY:只读。
返回值:
成功:映射的地址
失败:-1
4、shmdt()
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:解除共享内存的映射
参数:
shmaddr:映射的地址,shmat的返回值
返回值:
成功:0
失败:-1
案例:
发送程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define err_log(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
typedef struct{
int id;
char name[128];
int score;
}MSG;
int main(int argc, char const *argv[])
{
//获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
err_log("fail to ftok");
}
//创建一个共享内存
int shmid;
if((shmid = shmget(key, 500, IPC_CREAT | 0777)) == -1)
{
err_log("fail to shmget");
}
printf("shmid = %d\n", shmid);
system("ipcs -m");
//映射共享内存的地址
//char *addr;
//int *addr;
MSG *addr;
if((addr = shmat(shmid, NULL, 0)) == (void *)-1)
{
err_log("fail to shmat");
}
//往共享内存中写入数据
//strcpy(addr, "hello world");
//strcpy(addr+11, "nihao chengdu");
//*addr = 100;
addr->id = 1001;
strcpy(addr->name, "张三");
addr->score = 90;
//解除共享内存的映射
if(shmdt(addr) == -1)
{
err_log("fail to shmdt");
}
//删除一个共享内存
#if 0
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
err_log("fail to shmctl");
}
system("ipcs -m");
#endif
return 0;
}
接收程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define err_log(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
typedef struct{
int id;
char name[128];
int score;
}MSG;
int main(int argc, char const *argv[])
{
//获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
err_log("fail to ftok");
}
//创建一个共享内存
int shmid;
if((shmid = shmget(key, 500, IPC_CREAT | 0777)) == -1)
{
err_log("fail to shmget");
}
printf("shmid = %d\n", shmid);
system("ipcs -m");
//映射共享内存的地址
//char *addr;
//int *addr;
MSG *addr;
if((addr = shmat(shmid, NULL, 0)) == (void *)-1)
{
err_log("fail to shmat");
}
//读取共享内存中的数据
//printf("msg = %s\n", addr);
//printf("msg = %d\n", *addr);
printf("%d - %s - %d\n", addr->id, addr->name, addr->score);
//解除共享内存的映射
if(shmdt(addr) == -1)
{
err_log("fail to shmdt");
}
//删除一个共享内存
#if 0
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
err_log("fail to shmctl");
}
system("ipcs -m");
#endif
return 0;
}
3、信号量
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
ipcs 查看所有的ipc对象
-q 查看消息队列
-m 查看共享内存
-s 查看信号量
4、socket
socket主要用于网络通信,提供不同主机上的进程之间的通信
socket特点
1、socket也称“套接字”
2、是一种文件描述符,代表了一个通信管道的一个端点
3、类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作
4、得到socket套接字(描述符)的方法调用socket()
ps:此处不占用过多篇幅,后期做网络相关总结时会重点描述。