概述
管道
无名管道(PIPE)
:半双工的通信方式,而且只能在亲缘关系的进程间使用。一个进程在由pipe创建管道后,再fork出子进程,通过管道实现父子进程间的通信。
Int pipe(int fd[2]);
fd[0] 读;fd[1] 写;
写端:
close(fd[0]);
write(fd[1], buf, buf_sz);
读端:
close(fd[1]);
read(fd[0], buf, sizeof(buf));
有名管道(FIFO)
:半双工的通信方式,可以在无亲缘关系的进程间通信。严格先进先出
int mkfifo(filename, mode);
例:
int create_open_fifo(char *fifoname, int open_mode) {
int ret = -1;
int fd = -1;
ret = mkfifo(fifoname, 0664);
if (ret == -1 && errno != EEXIST) print_err("mkfifo failed");
fd = open(fifoname, open_mode);
if (fd == -1) print_err("open failed");
return fd;
}
fd = create_open_fifo(FIFONAME1, O_RDONLY);
read(fd1, buf, sizeof(buf));
scanf("input data:%s", buf);
write(fd2, buf, sizeof(buf));
demo:
```c
/*************************************************************************
> File Name: fifo_server.c
> Author:
> Mail:
> Created Time: 2020年06月02日 星期二 22时40分17秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<strings.h>
#include<signal.h>
#include<unistd.h>
#define FIFONAME1 "./fifo1.txt"
#define FIFONAME2 "./fifo2.txt"
void print_err(char *str) {
perror(str);
exit(-1);
}
int create_open_fifo(char *fifoname, int open_mode) {
int ret = -1;
int fd = -1;
ret = mkfifo(fifoname, 0664);
if (ret == -1 && errno != EEXIST) print_err("mkfifo failed");
fd = open(fifoname, open_mode);
if (fd == -1) print_err("open failed");
return fd;
}
void signal_fun(int signo) {
remove(FIFONAME1);
remove(FIFONAME2);
exit(-1);
}
int main() {
int ret = -1;
char buf[100] = {0};
int fd1 = -1;
int fd2 = -1;
// 1.创建FIFO
fd1 = create_open_fifo(FIFONAME1, O_RDONLY);
fd2 = create_open_fifo(FIFONAME2, O_WRONLY);
ret = fork();
if (ret > 0) {
// 父进程通过管道1读数据
signal(SIGINT, signal_fun);
while(1) {
bzero(buf, sizeof(buf));
read(fd1, buf, sizeof(buf));
printf("recv: %sn", buf);
}
} else if (ret == 0) {
// 子进程通过管道2写数据
while(1) {
bzero(buf, sizeof(buf));
scanf("input data:%s", buf);
write(fd2, buf, sizeof(buf));
}
}
return 0;
}
/*************************************************************************
> File Name: fifo_server.c
> Author:
> Mail:
> Created Time: 2020年06月02日 星期二 22时40分17秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<strings.h>
#include<signal.h>
#define FIFONAME1 "./fifo1.txt"
#define FIFONAME2 "./fifo2.txt"
void print_err(char *str) {
perror(str);
exit(-1);
}
int create_open_fifo(char *fifoname, int open_mode) {
int ret = -1;
int fd = -1;
ret = mkfifo(fifoname, 0664);
if (ret == -1 && errno != EEXIST) print_err("mkfifo failed");
fd = open(fifoname, open_mode);
if (fd == -1) print_err("open failed");
return fd;
}
void signal_fun(int signo) {
remove(FIFONAME1);
remove(FIFONAME2);
exit(-1);
}
int main() {
int ret = -1;
char buf[100] = {0};
int fd1 = -1;
int fd2 = -1;
// 1.创建FIFO
fd1 = create_open_fifo(FIFONAME1, O_WRONLY);
fd2 = create_open_fifo(FIFONAME2, O_RDONLY);
ret = fork();
if (ret > 0) {
// 父进程通过管道1写数据
signal(SIGINT, signal_fun);
while(1) {
bzero(buf, sizeof(buf));
scanf("input data:%s", buf);
write(fd1, buf, sizeof(buf));
}
} else if (ret == 0) {
// 子进程通过管道2读数据
while (1){
bzero(buf, sizeof(buf));
read(fd2, buf, sizeof(buf));
printf("recv: %sn", buf);
}
}
return 0;
}
消息队列:
可以实现网状交叉通信,但是不能实现大规模数据
1.int msgget(key_t key, int msgflg)
key:通过ftok(“file.txt”, ‘a’)生成 msgflag:0664|IPC_CREAT
返回值: 成功:返回消息队列的标识符;出错:-1,错误原因存于error中
2.int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg)
msgid:通过msgget得到的返回值;
msgp:发送给队列的消息。 是一个结构体:
struct s_msg{ /msgp定义的参照格式/
long type; / 必须大于0,消息类型 */
char mtext[256]; /消息正文,可以是其他任何类型/
} msgp;
msgsz:mtext的长度
msgflg: 0:阻塞;IPC_NOWAIT:不阻塞
返回值:成功返回0; 失败返回-1,错误原因存于error中
3.*ssize_t msgrcv(int msqid, void msgp, size_t msgsz, long msgtyp, int msgflg);
msgid/msgp/msgsz/msgflg:同上
msgtyp:0接收第一个消息; >0:接收类型等于msgtyp的第一个消息; <0:接收类型等于或者小于msgtyp绝对值的第一个消息
返回值:成功返回实际读取到的消息数据长度; 失败返回-1,错误原因存于error中
4.*int msgctl(int msqid, int cmd, struct msqid_ds buf)
msqid:同上
cmd:
IPC_STAT:获得msgid的消息队列属性存到buf中
IPC_SET:设置消息队列的属性,要设置的属性需先存储在buf中
IPC_RMID:删除消息队列 msgctl(msgid, IPC_RMID, NULL);
#include <stdio.h>
#include "securec.h"
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<signal.h>
#define MSG_FILE "./file.txt"
#define MSG_SIZE 1024
int msgid = -1;
struct msgbuf {
long mtype; /*放消息编码,必须>0*/
char mtext[MSG_SIZE]; /*消息内容*/
};
void print_err(char *str) {
perror(str);
exit(-1);
}
void signal_fun(int signo) {
msgctl(msgid, IPC_RMID, NULL);
exit(-1);
}
int create_or_open_msgque(void) {
int msgid = -1;
key_t key = -1;
int fd = 0;
/*创建一个消息队列的专用文件,ftok会用到这个文件的路径名*/
fd = open(MSG_FILE, O_RDWR|O_CREAT, 0664);
if (fd == -1) print_err("open fail");
/*利用存在的文件路径名和八位整形数,计算出key*/
key = ftok(MSG_FILE, 'a');
if (key == -1) print_err("ftok fail");
/*利用key创建、或者获取消息队列*/
msgid = msgget(key, 0664|IPC_CREAT);
if (msgid == -1) print_err("msgget fail");
return msgid;
}
int main(int argc, char **argv)
{
int ret = -1;
long recv_msgtype = 0;
if (argc != 2) {
printf("./a.out recv_msgtypen");
exit(-1);
}
/*通过命令行输入得到要接收的消息类型*/
recv_msgtype = atol(argv[1]);
msgid = create_or_open_msgque();
ret = fork();
if (ret > 0) {
// 父进程发送消息
struct msgbuf msg_buf = {0};
while(1) {
signal(SIGINT, signal_fun);
bzero(msg_buf, sizeof(msg_buf));
/*封装消息包*/
read(0, msg_buf.mtext, sizeof(msg_buf.mtext));
printf("input msgtypen");
read(0, msg_buf.mtype, sizeof(msg_buf.mtype));
/*发送消息包*/
msgsnd(msgid, msg_buf.mtext, MSG_SIZE, 0);
}
} else if (ret == 0) {
// 子进程接收消息
struct msgbuf msg_buf = {0};
while(1) {
bzero(msg_buf, sizeof(msg_buf));
/*接收消息包,返回值为字符长度*/
ret = msgrcv(msgid, &msg_buf, MSG_SIZE, recv_msgtype, 0);
if (ret > 0) {
printf("%sn", msg_buf.mtext);
}
}
}
return 0;
}
共享内存:
调用API让操作系统在物理内存上开辟出一大段共享缓存空间,让各自进程空间与共享内存建立映射关系。
1.得到一个共享内存标识符或创建一个共享内存对象:
int shmget(key_t key, size_t size, int shmflg)
key: 同msgget中的key
size:新建的共享内存大小
shmflg:0664|IPC_CREAT
2.把共享内存区对象映射到调用进程的地址空间:
**void shmat(int shmid, const void shmaddr, int shmflg)
shmid:通过shmget获取的共享内存标识符
shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
shmflg:0:可读可写;SHM_RDONLY:为只读模式,其他为读写模式
返回值:成功:附加好的共享内存地址; 失败返回-1,错误原因存于error中
3.断开共享内存连接,即取消映射 —所有映射都取消,才会取消,进程结束会自动取消
*int shmdt(const void shmaddr)
shmaddr:连接的共享内存的起始地址
返回值:成功:0; 失败返回-1,错误原因存于error中
4.共享内存管理 —释放共享内存,必须明确释放,进程结束不会自动释放
*int shmctl(int shmid, int cmd, struct shmid_ds buf)
cmd:
IPC_STAT:获得shmid的共享内存属性存到buf中
IPC_SET:设置共享内存的属性,要设置的属性需先存储在buf中
IPC_RMID:删除共享内存 shmctl(shmid, IPC_RMID, NULL);
返回值:成功:0; 失败返回-1,错误原因存于error中
demo:
写端:
#include <stdio.h>
#include "securec.h"
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<signal.h>
#include<strings.h>
#include<string.h>
#define SHM_FILE "./file.txt"
#define SHM_SIZE 4096//一页为4k,即4096
int shmid = -1;
void *shmaddr = NULL;
void print_err(char *str) {
perror(str);
exit(-1);
}
void signal_fun(int signo) {
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
remove("./fifo.txt");
remove(SHM_FILE);
exit(-1);
}
int get_peer_PID() {
int ret = -1;
int fifofd = -1;
/* 创建有名管道文件 */
ret = mkfifo("./fifo.txt", 0664);
if(ret == -1 && errno == EEXIST) print_err("mkfifo fail");
/* 以只读方式打开管道 */
fifofd = open("./fifo.txt", O_RDONLY);
if(fifofd == -1) print_err("open fail");
/* 读管道,获取读共享内存进程的PID */
int peer_pid;
ret = read(fifofd, &peer_pid, sizeof(peer_pid));
if (ret == -1) print_err("read fail");
return peer_pid;
}
void create_or_open_shm(void) {
key_t key = -1;
int fd = 0;
fd = open(SHM_FILE, O_RDWR|O_CREAT, 0664);
if (fd == -1) print_err("open fail");
/*利用存在的文件路径名和八位整形数,计算出key*/
key = ftok(SHM_FILE, 'b');
if (key == -1) print_err("ftok fail");
/*利用key创建、或者获取共享内存*/
shmid = shmget(key, SHM_SIZE, 0664|IPC_CREAT);
if (shmid == -1) print_err("shmget fail");
}
char buf[300] = {"aaaaaaaaaaaaaaasdddddddddddddddfffffffffffffggggggggggggg"};
int main(int argc, char **argv)
{
int peer_id = -1;
/* 给SIGINT信号注册捕获函数,用于删除共享内存、管道、文件等 */
signal(SIGINT, signal_fun);
/* 使用有名管道获取读共享内存进程的PID */
peer_id = get_peer_PID();
/* 创建或者获取共享内存 */
create_or_open_shm();
/* 建立映射 */
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) print_err("shmat error");
//写内存
while(1) {
/*写内容到共享内存*/
memcpy(shmaddr, buf, sizeof(buf));
kill(peer_pid, SIGUSR1);
sleep(1);
}
return 0;
}
读端:
#include <stdio.h>
#include "securec.h"
#include <stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<strings.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<signal.h>
#include<strings.h>
#include<string.h>
#define SHM_FILE "./file.txt"
#define SHM_SIZE 4096//一页为4k,即4096
int shmid = -1;
void *shmaddr = NULL;
void print_err(char *str) {
perror(str);
exit(-1);
}
void signal_fun(int signo) {
if (SIGINT == signo) {
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
remove("./fifo.txt");
remove(SHM_FILE);
exit(-1);
} else if (SIGUSR1 == signo) {
}
}
void snd_self_PID() {
int ret = -1;
int fifofd = -1;
ret = mkfifo("./fifo.txt", 0664);
if (ret == -1 && errno == EEXIST) print_err("mkfifo fail");
/* 以只写方式打开文件 */
fifofd = open("./fifo.txt", O_WRONLY);
if (fifofd == -1) print_err("open fail");
int pid = getpid();
ret = write(fifofd, &pid, sizeof(pid));//发送PID
if (ret == -1) print_err("write fail");
}
void create_or_open_shm(void) {
key_t key = -1;
int fd = 0;
fd = open(SHM_FILE, O_RDWR|O_CREAT, 0664);
if (fd == -1) print_err("open fail");
/*利用存在的文件路径名和八位整形数,计算出key*/
key = ftok(SHM_FILE, 'b');
if (key == -1) print_err("ftok fail");
/*利用key创建、或者获取共享内存*/
shmid = shmget(key, SHM_SIZE, 0664|IPC_CREAT);
if (shmid == -1) print_err("shmget fail");
}
int main(int argc, char **argv)
{
/* 给SIGUSR1注册一个空捕获函数,用于唤醒pause函数 */
signal(SIGUSR1, signal_fun);
signal(SIGINT, signal_fun);
/* 创建有名管道,将当前进程的PID发送给共享内存的写进程 */
snd_self_PID();
/* 创建或者获取共享内存 */
create_or_open_shm();
/* 建立映射 */
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) print_err("shmat error");
//读内存
while(1) {
pause();
printf("%sn", (char*)shmaddr);
bzero(shmaddr, SHM_SIZE);
//这里写内存有缺陷:如果没有数据会一直判断
//改进:同步!!!当共享内存没有数据时,读进程休眠,当写进程把数据写完后,将读进程唤醒。
//实现同步的方法:(1)信号;(2)信号量
/*
if (strlen((char*)shmaddr) != 0) {
printf("%sn", (char*)shmaddr);
bzero(shmaddr, sizeof(shmaddr));
}*/
}
return 0;
}
信号量
1.创建或获取一个信号量集标识符
int semget(key_t key, int nsems, int semflg)
key:同上
nsems:信号量集中信号量个数
semflg:0664|IPC_CREAT
返回值:成功:返回信号量集的标识符;失败返回-1,错误原因存于error中
2.初始化信号量集
int semctl(int semid, int semnum, int cmd, union semun arg)
semid:信号量集标识符
semnum:信号量集数组上的下标,表示某一个信号量
cmd:控制选项,IPC_RMID和SETVAL最常用
IPC_STAT、IPC_SET、IPC_RMID 都与消息队列和共享内存类似。
SETVAL:通过第四个参数,给集合中semnum编号的信号量设置一个int初值。
需要自己定义;
union semun {
short val;
/*SETVAL用的值*/
struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构**/
unsigned short* array; /*SETALL、GETALL用的数组值*/
struct seminfo *buf;
/*为控制IPC_INFO提供的缓存*/
} arg;
联合体使用实例:
1.当需要指定struct semid_ds缓存时:
union semun sem_un;
struct semid_ds buff;
sem_un.buf = &buff;
semctl(semid, 0, IPC_STAT, sem_un);
2.当需要指定信号量的int初始值时:
union semun sem_un;
sem_un.val = 1;
semctl(semid, 0, IPC_STAT, sem_un);
设置初始值:
semctl(semid, semnum, SETVAL, val);
删除信号量:
for(int i = 0; i < nsems; i++)
semctl(semid, i, IPC_RMID);
3.完成对信号量的P(-1)操作或V(+1)操作
int semop(int semid, struct sembuf *sops, unsigned nsops)
semid:信号量集标识符
sops:等价于struct sembuf sops[]
nsops:指定数组元素个数
//不需要自己定义,包含在semop头文件中
struct sembuf {
unsigned short sem_num;//信号量编号
short sem_op;//为-1,p操作;+1,V操作
short sem_flg;//IPC_NOWAIT不阻塞,SEM_UNDO:防止死锁
}
最后
以上就是超帅百合为你收集整理的IPC管道的全部内容,希望文章能够帮你解决IPC管道所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复