一、进程间通信概述
1.目的:数据传输、资源共享、通知事件、进程控制。
2.Linux使用的进程间通信方式包括:
(1)管道(pipe)和有名管道(FIFO)
(2)信号(signal)
(3)消息队列
(4)共享内存
(5)信号量
(6)套接字(socket)
二、管道通信
1.特点:管道是单向的、先进先出的,一个进程在管道的尾部写入数据,另一个进程从管道的头部读出数据。数据被一个进程读出后,将被从管道中删除。
2.(1)无名管道:用于父进程和子进程间的通信
无名管道创建:int pipe(int filedis[2]);
当一个管道建立时,它会创建两个文件描述符:filedis[0] 用于读管道,filedis[1] 用于写管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
void ReadData(int fd)
{
int ret;
char buf[32] = {0};
while(1)
{
ret = read(fd, buf, sizeof(buf));
if(-1 == ret)
{
perror("read");
exit(1);
}
if(!strcmp(buf, "bye"))
{
break;
}
printf("read from pipe: %s\n", buf);
memset(buf, 0, sizeof(buf));
}
}
void WriteData(int fd)
{
int ret;
char buf[32] = {0};
while(1)
{
scanf("%s", buf);
ret = write(fd, buf, strlen(buf));
if(-1 == ret)
{
perror("write");
exit(1);
}
if(!strcmp(buf, "bye"))
{
break;
}
memset(buf, 0, sizeof(buf));
}
close(fd);
}
int main()
{
int ret, fd[2] = {0};
pid_t = pid;
ret = pipe(fd); //创建一个无名管道
if(-1 == ret)
{
perror("pipe");
exit(1);
}
pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == pid)
{
close(fd[1]); //关闭写端口
ReadData(fd[0]); //fd[0]读数据
}
else
{
close(fd[0]); //关闭读端口
int status;
WriteData(fd[1]); //fd[1]读数据
wait(&status);
}
return 0;
}
注意事项:先创建pipe,再创建fork,因为是一个管道两个进程
(2)有名管道:用于运行于同一系统中的任意两个进程间的通信。
创建有名管道和读取数据:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int ret, fd;
char buf[32] = {0}; //从管道读取数据
ret = mkfifo("fifo.tmp", 666); //创建有名管道
if(-1 == ret)
{
perror("mkfifo");
exit(1);
}
fd = open("fifo.tmp", O_RDONLY);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
ret = read(fd, buf, sizeof(buf));
if(-1 == ret)
{
perror("read");
exit(1);
}
if(!strcmp(buf, "bye"))
{
break;
}
printf("%s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}
在有名管道中写入数据:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int ret, fd;
char buf[32] = {0}; //管道写数据
fd = open("fifo.tmp", O_WRONLY);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
scanf("%s", buf);
ret = write(fd, buf, strlen(buf));
if(-1 == ret)
{
perror("write");
exit(1);
}
if(!strcmp(buf, "bye"))
{
break;
}
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}
三、信号通信
1. 信号(signal)机制是Unix系统中最为古老的进程间通信机制
几种常见的信号:
(1)SIGHUP: 从终端上发出的结束信号
(2)SIGINT: 来自键盘的中断信号(Ctrl-C)
(3)SIGKILL:该信号结束接收信号的进程
(4)SIGTERM:kill 命令发出的信号
(5)SIGCHLD:标识子进程停止或结束的信号
(6)SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号
2.信号处理:(1)忽略(两种信号SIGKILL\SIGSTOP不能被忽略,它们是向超级用户提供了一种终止或停止进程的方法的信号)
(2)执行用户希望的动作(通知内核在某种信号发生时,调用一个用户函数,执行用户希望的处理)
(3)执行系统默认动作(对大多数信号的系统默认动作是终止该进程)
3.alarm函数:使用alarm函数可以设置一个时间值(闹钟时间),当所设置的时间到了时,产生SIGALRM信号.如果不捕捉此信号,则默认动作是终止该进程
unsigned int alarm(unsigned int seconds)
Seconds: 经过了指定的seconds秒后会产生信号SIGALRM。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void print(int num)
{
alarm(1);
printf("helloworld\n");
}
int main()
{
alarm(1); //闹钟函数,向操作系统发送SIGALRM信号
signal(SIGALRM, print);//操作系统收到SIGALRM信号,执行print函数
while(1);
return 0;
}
四、消息队列
例:
1.创建一个消息队列,一个发送数据,一个接收数据
send:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#define MSGKEY 1234
struct msgbuf
{
long mtype;
char mtext[64];
};
int main()
{
struct msgbuf mbuf;
int ret;
int msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL);
if(-1 == msgid)
{
perror("msgget");
exit(1);
}
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 1; //消息类型
scanf("%s", mbuf.mtext);
ret = msgsnd(msgid, &mbuf, sizeof(mbuf. mtext), 0);
if(-1 == ret)
{
perror("msgsnd");
exit(1);
}
if(!strcmp(mbuf.mtext, "bye"))
{
break;
}
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
recieve:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#define MSGKEY 1234
struct msgbuf
{
long mtype;
char mtext[64];
};
int main()
{
struct msgbuf mbuf;
int ret;
int msgid = msgget(MSGKEY, 0);
if(-1 == msgid)
{
perror("msgget");
exit(1);
}
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
ret = msgrcv(msgid, &mbuf, sizeof(mbuf. mtext), 1, 0);
if(-1 == ret)
{
perror("msgrcv");
exit(1);
}
if(!strcmp(mbuf.mtext, "bye"))
{
break;
}
printf("%s\n", mbuf.mtext);
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
2.创建一个消息队列,可以互相发送和接收数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#define MSGKEY 1234
struct msgbuf
{
long mtype;
char mtext[64];
};
int main()
{
int ret;
struct msgbuf mbuf;
int msgid = msgget(MSGKEY, 0);
if(-1 == msgid)
{
perror("megget");
exit(1);
}
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == pid) //子进程发送
{
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 2; //消息类型
scanf("%s", mbuf.mtext);
ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
if(-1 == ret)
{
perror("msgsnd");
exit(1);
}
if(!strcmp(mbuf.mtext, "bye"))
{
mbuf.mtype = 1;
msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
break;
}
}
}
else
{
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
ret = msgrcv(msgid, &mbuf, sizeof(mbuf. mtext), 1, 0);
if(-1 == ret)
{
perror("msgrcv");
exit(1);
}
if(!strcmp(mbuf.mtext, "bye"))
{
kill(pid, 2);
break;
}
printf("\t\t\t%s\n", mbuf.mtext);
}
}
sleep(1);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#define MSGKEY 1234
struct msgbuf
{
long mtype;
char mtext[64];
};
int main()
{
int ret;
struct msgbuf mbuf;
int msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL);
if(-1 == msgid)
{
perror("megget");
exit(1);
}
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == pid) //子进程发送
{
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 1; //消息类型
scanf("%s", mbuf.mtext);
ret = msgsnd(msgid, &mbuf, sizeof(mbuf. mtext), 0);
if(-1 == ret)
{
perror("msgsnd");
exit(1);
}
if(!strcmp(mbuf.mtext, "bye"))
{
mbuf.mtype = 2;
msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
break;
}
}
}
else
{
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
ret = msgrcv(msgid, &mbuf, sizeof(mbuf. mtext), 2, 0);
if(-1 == ret)
{
perror("msgrcv");
exit(1);
}
if(!strcmp(mbuf.mtext, "bye"))
{
kill(pid, 2);
break;
}
printf("\t\t\t%s\n", mbuf.mtext);
}
}
sleep(1);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
五、 共享内存
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#define SHMKEY 1234
#define SHMSIZE 4096
int main()
{
int shmid, count = 0;
void *shmaddr;
shmid = shmget(SHMKEY, SHMSIZE, IPC_CREAT | IPC_EXCL);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0); //映射到虚拟地址空间
if(NULL == shmaddr)
{
perror("shmat");
exit(1);
}
*(int *)shmaddr = count; //数据写到共享内存
while(1)
{
count = *(int *)shmaddr; //读取数据
usleep(100000);
if(count >= 100)
{
break;
}
printf("Process A : count = %d\n", count);
count++;
*(int *)shmaddr = count; //写回共享内存
}
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#define SHMKEY 1234
#define SHMSIZE 4096
int main()
{
int shmid, count;
void *shmaddr;
shmid = shmget(SHMKEY, SHMSIZE, 0);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0); //映射到虚拟地址空间
if(NULL == shmaddr)
{
perror("shmat");
exit(1);
}
while(1)
{
count = *(int *)shmaddr; //读取数据
usleep(100000);
if(count >= 100)
{
break;
}
printf("Process B : count = %d\n", count);
count++;
*(int *)shmaddr = count; //写回共享内存
}
return 0;
}
六、信号量
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sem.h>
#define SHMKEY 1234
#define SHMSIZE 4096 //以页为单位分配共享内存
#define SEMKEY 1234
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux specific) */
};
void sem_p(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = -1; //p操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if(-1 == ret)
{
perror("semop");
return;
}
}
void sem_v(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = -1; //v操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if(-1 == ret)
{
perror("semop");
return;
}
}
int main()
{
int shmid, semid, ret;
void *shmaddr;
int count = 0;
shmid = shmget(SHMKEY, SHMSIZE, IPC_CREAT | IPC_EXCL);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
semid = semget(SEMKEY, 1, IPC_CREAT | IPC_EXCL);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
union semun unsem;
unsem.val = 1; //初始化成二值信号量
ret = semctl(semid, 0, SETVAL, unsem);//初始化信号量
if(-1 == ret)
{
perror("semctl");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0); //映射到虚拟地址空间
if(NULL == shmaddr)
{
perror("shmat");
exit(1);
}
*(int *)shmaddr = count; //数据写到共享内存
while(1)
{
sem_p(semid); //p操作 拔钥匙
count = *(int *)shmaddr; //读取数据
usleep(1000);
if(count >= 100)
{
break;
}
printf("Process A : count = %d\n", count);
count++;
*(int *)shmaddr = count; //写回共享内存
sem_v(semid); //v操作 加一操作 插钥匙
}
shmdt(shmaddr); //解除映射
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sem.h>
#define SHMKEY 1234
#define SHMSIZE 4096 //以页为单位分配共享内存
#define SEMKEY 1234
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux specific) */
};
void sem_p(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = -1; //p操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if(-1 == ret)
{
perror("semop");
return;
}
}
void sem_v(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = -1; //v操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if(-1 == ret)
{
perror("semop");
return;
}
}
int main()
{
int shmid, semid, ret;
void *shmaddr;
int count = 0;
shmid = shmget(SHMKEY, SHMSIZE, 0);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
semid = semget(SEMKEY, 1, 0);
if(-1 == shmid)
{
perror("shmget");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0); //映射到虚拟地址空间
if(NULL == shmaddr)
{
perror("shmat");
exit(1);
}
while(1)
{
sem_p(semid); //p操作 拔钥匙
count = *(int *)shmaddr; //读取数据
usleep(1000);
if(count >= 100)
{
break;
}
printf("Process B : count = %d\n", count);
count++;
*(int *)shmaddr = count; //写回共享内存
sem_v(semid); //v操作 加一操作 插钥匙
}
shmdt(shmaddr); //解除映射
return 0;
}