进程间的通信(IPC)方式:
无名管道(PIPE)
有名管道(FIFO)
信号(signal)
共享内存
消息队列
信号量
套接字
(1)无名管道(PIPE)
特点:
最原始的进程间的通信方式;
它只能在具有亲缘关系的进程间通信,例如:父子进程,兄弟进程;
无名管道顾名思义就是没有名字, 但是它是存在的;
可以存在linux和windows之间的共享中,因为不会生成管道文件;
半双工工作方式:读写端分开;
写入操作不具有原子性,因此只能用于一对一的简单通信情形;
不能使用lseek( )来定位。
函数原型:
#include <unistd.h>
int pipe(int fildes[2]);
参 数:fildes[2] --》 fildes[0] 读端 / fildes[1] 写端
返回值:成功= 0 / 失败 = -1
栗子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
int main()
{
pid_t myid;
int fd1[2];
int fd2[2];
int ret1,ret2;
char sbuf[100];
char rbuf[100];
char sbuf1[100];
char rbuf1[100];
ret1 = pipe(fd1); // 创建无名管道
ret2 = pipe(fd2); // 创建无名管道
if((ret1 == -1)||(ret2 == -1))
{
perror("create pipe failed!\n");
return -1;
}
printf("Createing fork a child program!\n");
myid = fork();
if(myid == 0) //子进程
{
while(1) //循环读写管道信息
{
printf("Child program: please input a message!\n");
fgets(sbuf,100,stdin);
write(fd1[1],sbuf,100);
read(fd2[0],rbuf1,100);
printf("Child program: message from father is:%s\n", rbuf1);
}
exit(0);
}
else if(myid > 0) //父进程
{
while(1) //循环读写管道信息
{
read(fd1[0],rbuf,100);
printf("Father progrom: message from child is:%s\n", rbuf);
printf("Father progrom: please input a message!\n");
fgets(sbuf1,100,stdin);
write(fd2[1],sbuf1,100);
}
}
printf("Over!\n");
wait(NULL);
return 0;
}
(2)有名管道(FIFO)
特点:
任意两个进程之间都可以使用;
不可以创建于linux和windows共享内存中;
有名管道不能够覆盖着创建,使用access()函数来判断是否存在;
存在于文件系统之中,提供写入原子性特征;
跟普通文件一样:使用统一的read( )/write( )来读写,但不能使用lseek( );
最先被写入FIFO的数据,最先被读出来;
不仅打开管道会有可能发生阻塞,在对管道进行读写操作时也有可能发生阻塞。
函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参 数:pathname: 有名管道的路径名 / mode:权限 0666
返回值:成功= 0 / 失败 = -1
栗子:
//只读进程
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#define FIFO "namefifo" //定义管道名
int main(void)
{
int my_fd,fd;
char r_buff[30];
bzero(r_buff,30);
if(access(FIFO,F_OK)==-1) //判断管道名是否存在
{
my_fd = mkfifo(FIFO,0664); //创建有名管道
if(my_fd == -1)
{
perror("failed!\n");
return -1;
}
}
fd = open(FIFO,O_RDONLY); //只读方式打开管道
if(fd==-1)
{
printf("open fifo file failed!\n");
exit(0);
}
while(1)
{
bzero(r_buff,30);
read(fd,r_buff,sizeof(r_buff)); //从管道中读取数据
printf("r_buff = %s\n",r_buff);
}
close(fd);
return 0;
}
//只写进程
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#define FIFO "namefifo" //定义管道名
int main(void)
{
int my_fd,fd;
char w_buff[30];
bzero(w_buff,30);
if(access(FIFO,F_OK)==-1) //判断管道名是否存在
{
my_fd = mkfifo(FIFO,0664); //创建有名管道
if(my_fd == -1)
{
perror("failed!\n");
return -1;
}
}
fd = open(FIFO,O_WRONLY); //只写方式打开管道
if(fd==-1)
{
printf("open fifo file failed!\n");
exit(0);
}
while(1)
{
bzero(w_buff,30);
fgets(w_buff,30,stdin);
write(fd,w_buff,strlen(w_buff)); //把数据写入管道
}
close(fd);
return 0;
}
(3)信号
特点:
非实时信号不排队,信号的响应会相互嵌套。
如果目标进程没有及时响应非实时信号,那么随后到达的该信号将会被丢弃。
每一个非实时信号都对应一个系统事件,当这个事件发生时,将产生这个信号。
如果进程的挂起信号中含有实时和非实时信号,那么进程优先响应实时信号并且会从大到小依此响应,而非实时信号没有固定的次序。
后面的31个信号(从SIGRTMIN[34] 到 SIGRTMAX[64])是Linux系统新增的实时信号,也被称为“可靠信号”,这些信号的特征是:
实时信号的响应次序按接收顺序排队,不嵌套。
即使相同的实时信号被同时发送多次,也不会被丢弃,而会依次挨个响应。
实时信号没有特殊的系统事件与之对应。
Linux中查看存在信号命令:kill -l 查看
函数原型:
A: 发送信号kill()
#include <signal.h>
int kill(pid_t pid, int sig);
pid :进程的id
sig :信号名字
命令发送 kill -SIGKILL 4131 == kill -信号名 pid
B: 信号的捕捉 signal()
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int); // SIGKILL
sig :需要捕捉的那个信号
void (*func)(int)函数指针,回调函数,捕捉到对应的信号调用该函数
第二个参数除了可以传递一个函数指针意外,还可以使用以下两个宏定义:
SIG_IGN :捕捉到的信号会被忽略
SIG_DFL :捕捉到的信号会采用系统默认的方式响应
C: 等待信号 pause()
#include <unistd.h>
int pause(void);
D: 信号的阻塞:
#include <signal.h>
int sigemptyset(sigset_t *set);//清空信号掩码
int sigfillset(sigset_t *set);//将所有的信号添加到信号掩码中
int sigaddset(sigset_t *set, int signum);//将特定的信号添加到信号掩码中
int sigdelset(sigset_t *set, int signum);/将特定的信号从掩码中删除
int sigismember(const sigset_t *set, int signum);//判断某个信号是不是在该掩码中
每个进程都有属于它自己的一个信号掩码,进程在运行的过程中会阻塞掉的信号就被称作信号掩码。
E: 配置信号掩码 sigprocmask()
#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
how:SIG_BLOCK 将set所包含的信号添加到原来的信号掩码中
SIG_SETMASK 用set去替换原来的信号掩码
SIG_UNBLOCK 将set中包含的信号从原来的掩码中删除
set: 新的信号掩码
oset:原本的信号掩码,原本进程中信号掩码包含了:SIGINT ,SIGCONT
F: 捕捉信号sigaction()
#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
sig :要捕捉的信号
act :要捕捉的信号对应的响应函数就定义在这个结构体
oact:原来的响应函数
struct sigaction
{
void(*) (int) sa_handler //信号的响应函数
sigset_t sa_mask //信号的掩码
int sa_flags // SA_SIGINFO
void(*) (int, siginfo_t * ,void ) //信号的响应函数
}
栗子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
// 定义信号的响应函数
void func1(int sig)
{
printf("now I catch signal is:%d\n",sig);
return ;
}
int main()
{
printf("Process PID = %d\n",getpid());
// 捕捉信号
signal(SIGUSR1,func1);
signal(SIGUSR2,SIG_IGN);
signal(SIGINT,SIG_IGN);
signal(SIGKILL,SIG_IGN); //该信号很特殊
// 等待外界信号的到来
pause();
return 0;
}