UNIX进程间通信方式
目录
1.无名管道
1.1example:
1.1.1无名管道为一种半双工通信方式,需要双方通信时,需要建立起两个管道,只能用于父子进程和兄弟进程之间的通信。
1.1.2无名管道单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
1.1.3数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
1.2实现方式
#include<unistd.h>
1.实现函数
int pipe(int fd[2])
//fp[0] 读取数据
//fd[1] 写入数据
1.3实例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t pid;
int pfd[2];
pipe(pfd);
pid = fork();//创建子进程
if (pid < 0) {
perror("open:");
return -1;
} else if (pid == 0) {
char buffer[100] = "helloworld\n";
while (1) {
write(pfd[1], buffer, strlen(buffer));//子进程用于写入
sleep(1);
}
} else {
char buffer[100];
while (1) {
if (read(pfd[0], buffer, 100) > 0) {//父进程用于读取
printf("recv from %s\n", buffer);
sleep(1);
} else {
break;
}
}
}
}
2.有名管道
2.1.摘要
1.不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。
2.即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。
3.FIFO严格遵循先进先出(first in first out)(类似于队列),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。
2.2.实现方式:
#include <sys/stat.h>
1.实现函数
int mkfifo(const char * pathname, mode_t mode)
//const char *pathname fifo文件的路径
//mode_t mdoe 创建的fifo文件的权限
2.3.实例:
//Fifo_Write
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
if (mkfifo("/fifofile", 0666) == -1) {
perror("faild:");
return 0;
}//创建fifo管道文件。
int fd;
if ((fd = open("/fifofile", O_WRONLY)) < 0) {
perror("open fifofile error");
return -1;
}//打开管道文件
char buffer[100] = "hello\n";
while (1) {
write(fd, buffer, strlen(buffer));//写入数据
sleep(1);
}
}
//Fifo_Read
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
int fd;
if ((fd = open("/fifofile", O_RDONLY)) < 0) {
perror("faild");
return 0;
}//打开管道文件
char buffer[100];
while (1) {
read(fd, buffer, 100);//读取数据
printf("-->%s", buffer);
sleep(1);
}
}
3.共享内存
3.1.摘要
共享内存,顾名思义,就是两个或多个进程都可以访问的同一块内存空间,一个进程对这块空间内容的修改可为其他参与通信 的进程所看到的。显然,为了达到这个目的就需要做两件事:一件是在内存划出一块区域来作为共享区;另一件是把这个区域映 射到参与通信的各个进程空间。共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数 据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
3.2.实现方式
#include <sys/shm.h>
1. 获取共享内存
shmget(key_t key,size_t size,int shmflg)
//key_t key ftok函数获取的id
//size_t size 需要获取的共享内存大小
//int shmflg 标志位
2. 映射共享内存
shmat(int shmid,const void *shmaddr,int shmflg)
//int shmid 获取的共享内存id号
//const void *shmaddr 需要映射的地址,设置为NULL系统自动映射
//int shmflg 标识位
3. 撤销共享内存
shmdt(const void *shmaddr)
//const void *shmaddr 映射的共享内存地址
4. 指定共享内存控制
shmct(int shmid,int cmd,struct shmid_ds *buf)
//int shmid 获取的共享内存id号
//int cmd 需要执行的命令
//struct shmid_ds *buf
3.3.实例
//ShareMemory read
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
int main() {
int shm_id;
key_t key_id;
if ((key_id = ftok(".", 100)) < 0) {
perror("get share memory is failed");
exit(0);
}
if ((shm_id = shmget(key_id, 512, IPC_CREAT | 0666)) < 0) {
perror("get sharememory is faild!");
exit(0);
}//获取shareMemory id值
char *addr;
if ((addr = (char *)shmat(shm_id, NULL, 0666)) == NULL) {
perror("mapping the share memory is faild!");
exit(0);
}//映射共享内存
printf("-->%s", addr);
shmdt((void *)addr);//撤销共享内存
shmctl(key_id, IPC_RMID, NULL);//删除共享内存
sleep(1);
return 0;
}
//ShareMemory write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
int shm_id;
key_t key_id;
if ((key_id = ftok(".", 100)) < 0) {
perror("get share memory is failed");
exit(0);
}
if ((shm_id = shmget(key_id, 512, IPC_CREAT | 0666)) < 0) {
perror("get sharememory is faild!");
exit(0);
}//获取shm_memory id值
char *addr;
if ((addr = (char *)shmat(shm_id, NULL, 0666)) == NULL) {
perror("mapping the share memory is faild!");
exit(0);
}//映射共享内存
strcpy(addr, "hello");
shmdt(addr);//撤销共享内存映射
return 0;
}
4.消息队列
4.1. 摘要:
消息队列的实质是一个存放消息的链表,该链表由内核维护;消息队列中的每个消息可以视为一条记录,消息包括一个长整型的类型字段和需要传递的数据。消息队列由消息队列标识符(queue ID)标识,对消息队列有读权限的进程可以从队列中读取消息,对消息队列有写权限的进程可以按照规则,向其中添加消息。
与管道相比,消息队列的通信方式更为灵活:它提供有格式的字节流,无需通信双方额外约定数据传输格式;其中的消息设定为不同类型,又被分配了不同的优先级,新添加的消息总是在队尾,但接收消息的进程可以读取队列中间的数据。此外,消息队列也降低了读写进程间的耦合强度:若接收消息的进程没有接收到消息,发送消息的进程无须等待,可以继续发送消息,消息的读写双方只需关注各自功能的实现情况即可。
与FIFO类似,消息队列可以实现无亲缘关系进程间的通信,且独立于通信双方的进程之外,若没有删除内核中的消息队列,即便所有使用消息队列的进程都已终止,消息队列仍存在于内核中,直到内核重新启动、管理命令被执行或调用系统接口删除消息队列时,消息队列才会真正被销毁。
系统中的最大消息队列数与系统中最大消息数都有一定限制,分别由宏MSGMNI和宏MSGTOL定义;消息队列的每个消息中所含数据块的长度以及队列中所含数据块的总长度也有限制,分别由宏MSGMAX和MSGMNB定义。
4.2. 实现方式
#include <sys/msg.h>
1. 消息格式
typedef struct {
long int type;//固定格式
char msg[N];//可变
}
2. 获取消息队列
msgget(key_t key,int msgflg)
//key_t key ftok函数获取的id
//int msgflg 标识位
3. 发送消息到消息队列
msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg)
//int msgid msgget函数获取的id
//void *msgp 消息指针
//size_t 消息长度(除去消息格式里面的类型大小)
//int msgflg 标志位
4. 从消息队列获取消息
msgrcv(int msgid,const void *msgp,size_t msgsz,long type,int msgflg)
//int msgid msgget函数获取的id
//const void *msgp 存放消息的指针
//size_t msgsz 获取消息的长度
//long type 获取的消息类型
//int msgflg 标志位
5. 对指定消息队列进行控制
msgctl(int msgid,int cmd,struct msgid_ds *buf);
//int msgid 指定的消息队列id号
//int cmd 需要对指定消息队列执行的指令
//struct msgid_ds *buf仅当cmd设置为IPC_STAT或IPC_SET时有效。
4.3. 实例
//发送消息到消息队列
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct {
long type;
char msg[64];
} MSG;
#define LEN (sizeof(MSG) - sizeof(long))
int main() {
key_t id1;
int msgid;
id1 = ftok(".", 33);
if (id1 == -1) {
printf("get key id is fail!");
exit(0);
}
msgid = msgget(id1, IPC_CREAT | 0666);
if (msgid == -1) {
printf("get the msgid is fail!");
exit(0);
}
printf("msgid is --->%d\nshmid is --->%d\n", msgid, shmid);
MSG MSG1;
strcpy(MSG1.msg, "hello,world");
MSG1.type = 1;
msgsnd(msgid, &MSG1, LEN, 0);
}
//从消息队列进行接收
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/types.h>
typedef struct {
long type;
char msg[64];
} MSG;
#define LEN (sizeof(MSG) - sizeof(long))
int main() {
key_t id1;
int msgid;
id1 = ftok(".", 33);
if (id1 == -1) {
printf("get key id is fail!");
exit(0);
}
msgid = msgget(id1, IPC_CREAT | 0666);
if (msgid == -1) {
printf("get the msgid is fail!");
exit(0);
}
MSG MSG1;
msgrcv(msgid, &MSG1, LEN, -3, IPC_NOWAIT);
printf("--->%s\n", MSG1.msg);
}
5. 信号灯(System V 信号灯)
5.1 摘要
用于多个进程在共享某一部分资源时进行进程同步。
5.2 实现方式
#include <sys/sem.h>
1. 打开/创建信号灯
int semget(key_t key,int nsems,int semflg)
//key_t key ftok函数得到的key值
//int nsems 需要创建的信号灯个数
//int semflg 标志位
2. 指定信号灯进行控制
int semctl(int semid,int semnum,int cmd)
//int semid semget 得到的id值
//int semnum 操作的信号灯索引
//int cmd 需要执行的指令
3. P/V操作
int semop(int semid, struct sembuf *sop,size_t nsops)
struct sembuf
{
short sem_num;//信号灯索引
short sem_op;//执行的操作-1为p操作1为v操作
short sem_flg;//标志位
}
//int semid semget函数得到的id
//struct sembuf *sop
//size_t nsops 需要操作的信号灯个数。
5.3. 实例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#define sem_read 0
#define sem_write 1
union semun {
int val;
};
void poperation(int index, int sem_id) {
struct sembuf sem1;
sem1.sem_num = index;
sem1.sem_op = -1;
sem1.sem_flg = 0;
semop(sem_id, &sem1, 1);
}
void voperation(int index, int sem_id) {
struct sembuf sem2;
sem2.sem_num = index;
sem2.sem_op = 1;
sem2.sem_flg = 0;
semop(sem_id, &sem2, 1);
}
int main() {
key_t ipc_key;
int shm_id, sem_id;
ipc_key = ftok(".", 111);
sem_id = semget(ipc_key, 2, IPC_CREAT | 0666); //创建两个信号灯
shm_id = shmget(ipc_key, 512, IPC_CREAT | 0666);
if (shm_id < 0) {
perror("create share memory:");
exit(0);
}
if (sem_id < 0) {
perror("create signal:");
exit(0);
}
union semun val_wr, val_re;
val_wr.val = 0;
val_re.val = 1;
semctl(shm_id, 0, SETVAL, val_wr);
semctl(shm_id, 1, SETVAL, val_re); // 初始化两个信号灯(读信号灯和写信号灯)
pid_t sub_id;
sub_id = fork();
if (sub_id < 0) {
perror("create sub process is failed");
exit(0);
} else if (sub_id == 0) {
char *addr = (void *)shmat(shm_id, NULL, IPC_CREAT | 0666);
while (1) {
poperation(sem_read, sem_id);
printf("---->%s", addr);
voperation(sem_write, sem_id);
}
} else {
char *addr = (void *)shmat(shm_id, NULL, IPC_CREAT | 0666);
while (1) {
poperation(sem_write, sem_id);
fgets(addr, 10, stdin);
voperation(sem_read, sem_id);
}
}
}