1、无名管道
/*无名管道双向通信试验*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd_fs[2],fd_sf[2];
if(pipe(fd_fs) || pipe(fd_sf)) /*创建管道*/
{
perror("pipe init error.");
return -1;
}
pid_t pid = fork();
if(pid < 0)
{
error("fork error.");
return -1;
}
else if(pid == 0)
{
close(fd_fs[1]);
close(fd_sf[0]);
char buf_recs[30] = {0};
/*son receive*/
if(!read(fd_fs[0],buf_recs,sizeof(buf_recs)))
{
perror("read error.");
close(fd_fs[0]);
return -1;
}
char buf_sf[30] = "hello father";
if(!write(fd_sf[1],buf_sf,sizeof(buf_sf))) /*son send*/
{
perror("son sen error.");
close(fd_sf[1]);
return -1;
}
printf("son receive:%s\n",buf_recs);
close(fd_fs[0]);
close(fd_sf[1]);
}else{
/*father send*/
close(fd_fs[0]);
close(fd_sf[1]);
char buf_fs[30] = "hello son.";
char buf_recf[30] = {0};
if(!write(fd_fs[1],buf_fs,sizeof(buf_fs))) /*father send*/
{
perror("write error.");
close(fd_fs[1]);
return -1;
}
if(!read(fd_sf[0],buf_recf,sizeof(buf_recf))) /*father receive*/
{
perror("father receive error.");
close(fd_sf[0]);
return -1;
}
printf("father receive :%s\n",buf_recf);
close(fd_fs[1]);
close(fd_sf[0]);
/*father receive*/
}
return 0;
}
2、有名管道
头文件:
#include <unistd.h>
#include <fcntl.h>
int mkfifo(const char*filename,mode_t mode);
特点:
(1)能够用于任何进程间通信(同一主机)
(2)半双工通信
(3)使用文件IO进程操作
(4)有管道文件,但实际操作的是内核中的管道
管道读写注意点:
(1)当管道中无数据时,该管道会阻塞。
(2)如果读进程不读走管道中的数据,写操作将一直阻塞
(3)只有在管道的读存在时才有意思,否则,将传来内核SIGPIPE的信号(通常是Broken pipe)
无名管道示例程序
/*fifo_w.c write*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main()
{
/*创建有名管道*/
int ret = mkfifo("myfifo","0666");
if(ret < 0 errno != EEXIST) //忽略管道文件已经存在的情况
{
perror("mkfifo error.");
return -1;
}
/*open*/
int fd = open("myfifo",O_ERONLY);
if(fd < 0)
{
perror("open error.");
return -1;
}
char buf[100] = {0};
fgets(buf,sizeof(buf),stdin);
ret = write(fd,buf,sizeof(buf));
if(ret < 0)
{
perror("write error.");
return -1;
}
close(fd);
return 0;
}
/*fifo_r.c read*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main()
{
/*创建有名管道*/
int ret = mkfifo("myfifo","0666");
if(ret < 0 errno != EEXIST) //忽略管道文件已经存在的情况
{
perror("mkfifo error.");
return -1;
}
/*open*/
int fd = open("myfifo",O_RDONLY);
if(fd < 0)
{
perror("open error.");
return -1;
}
char buf[100] = {0};
//fgets(buf,sizeof(buf),stdin);
ret = read(fd,buf,sizeof(buf));
if(ret < 0)
{
perror("read error.");
return -1;
}
printf("%s\n", buf);
close(fd);
return 0;
}
总结:
(1)不管是有名管道还是无名管道,一个管道只能实现单向通信,要实现双向通信,就必须在创建一个管道。
(2)不管是又名管道还是无名管道,本质都是内核空间
3、信号
(1)信号是一种异步通信方式
(2)信号都是由内核产生的
kill :可以给任何进程发信号 kill(pid,信号)
reise : 只能给自己发 reise(信号)
信号安装:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//signum :指定信号
//handler :
//(1)SIG_IGN :忽略该信号
//(2)SIG_DFL : 采用系统默认方式处理信号
//(3)定义信号处理函数
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
/* 信号安装 */
signal(SIG_INT,SIG_IGN);
while(1)
{
printf("helloworld\n");
}
return 0;
}
pause函数:pause() : 挂起当前进程,知道收到信号为止
用法:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
pause(); 挂起
printf("hello world\n");
return 0;
}
alarm函数
unsigned int alarm(unsigned int seconds)
seconds:指定秒数。
4、共享内存
共享内存是一种最为高效的进程间通信方式。
IPC对象:
1、由内核创建,存在于内核中
2、通过ID号进行访问
共享内存示例代码
/*共享内存进程write*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int int main(int argc, char const *argv[])
{
/* 得到key值
*key_t ftok(const char *pathname,int proj_pid)
*/
key_t key = ftok("/tmp",68); //随意传参,两个进程一样1-255
if(key < 0)
{
perror("ftok error.");
return -1;
}
/*创建共享内存*/
/*int shmget(key )*/
int shmID = shmget(key,100,IPC_CREAT | 0666);
if(shmID < 0)
{
perror("shmget error.");
return -1;
}
/*共享内存映射*/
void *p = shmat(shmID,NULL,0);
if((void *)-1 == p)
{
perror("shmat error.");
shmctl(shmID,IPC_RMID,NULL);
return -1;
}
/*操作*/
puts("please input:");
fgets(p,100,stdin);
/*取消映射*/
shmdt(p);
return 0;
}
/*共享内存进程read*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int int main(int argc, char const *argv[])
{
/*****************************************************/
/* 得到key值
*key_t ftok(const char *pathname,int proj_pid)
*/
key_t key = ftok("/tmp",68); //随意传参,两个进程一样1-255
if(key < 0)
{
perror("ftok error.");
return -1;
}
/*创建共享内存*/
/*int shmget(key )*/
int shmID = shmget(key,100,IPC_CREAT | 0666);
if(shmID < 0)
{
perror("shmget error.");
return -1;
}
/*共享内存映射*/
char *p = (void *)shmat(shmID,NULL,0);
if((void *)-1 == p)
{
perror("shmat error.");
shmctl(shmID,IPC_RMID,NULL);
return -1;
}
puts(p);
//取消映射
shmdt(p);
//删除共享内存
shmctl(shmId, IPC_RMID, NULL);
return 0;
}
消息队列
(1)消息队列是IPC对象的一种
(2)消息队列由消息队列ID来唯一标识
(3)可以添加消息读取消息
*(4)消息队列可以安装类型来发送/接受消息
(5) 存在与内核中
/*msg_send*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msgbuf
{
long msgtype;
char data[20];
};
int main(int argc, char const *argv[])
{
// 创建消息
key_t key = ftok("./",89);
if(key < 0)
{
perror("ftok error.");
return -1;
}
int msgID = msgget(key,IPC_CREAT | 0666);
if(msgID < 0)
{
perror("msgget error.");
return -1;
}
// 添加消息
struct msgbuf buf = {1,"hello"};
int ret = msgsnd(msgID,&buf,sizeof(buf.data),0);
if(ret < 0)
{
perror("msgsnd error.");
msgctl(msgID,IPC_RMID,NULL);
return -1;
}
puts("send ok");
return 0;
}
/*msg_receive*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
struct msgbuf
{
long msgtype;
char data[20];
};
int main(int argc, char const *argv[])
{
// 创建消息
ey_t key = ftok("./",89);
if(key < 0)
{
perror("ftok error.");
return -1;
}
int msgID = msgget(key,IPC_CREAT | 0666);
if(msgID < 0)
{
perror("msgget error.");
return -1;
}
// 读取消息
struct msgbuf buf = {0};
int ret = msgrcv(msgID,&buf,sizeof(buf),0,0);
if(ret < 0)
{
perror("rcv error.");
msgctl(msgID,IPC_REMID,NULL);
return -1;
}
printf("%s\n", buf.data);
// 删除消息
msgctl(msgID,IPC_REMID,NULL);
return 0;
}
进程间通信总结
通信方式
(1)无名管道
(2)有名管道
(3)信号
(4)共享内存
(5)消息队列
(6)信号量
注意:以上都是同一台主机的进程间通信。
由数据交换的通信方式:无名管道 有名管道 共享内存 消息队列
信号:一种异步通信机制
信号量:同步机制
(1)无名信号量:多用于线程间
(2)有名信号量:进程间
不同主机间的通信方式:只有套接字