1、理论介绍
1.1、进程间通信的概念
两个进程间实现数据的互相传输的通信,进程间通信简称IPC。
1.2、进程间通信的方式
1、管道,管道又可以分为命名管道(FIFO)和无名管道
2、消息队列
3、共享内存
4、信号
5、信号量
1.3、各个进程间通信的特点
(1) 无名管道
1、无名管道的通信是半双工的,数据只能够单方向流动,具有固定的读端和写端。
2、无名管道的通信仅仅局限于父子进程和兄弟进程之间。
3、无名管道可以理解为是一种特殊的文件,对它的操作可以使用普通的read、write操作,但不是普通的文件,不属于文件系统,仅仅存在于内存中。
(2) 命名管道
1、FIFO可以在无关的进程之间实现通信。
2、FIFO有路径名与之相对应,以一种特殊的文件形式存在于文件系统中。
3、FIFO也是半双工通信。
(3) 消息队列
1、消息队列是消息的链表,存放于Linux的内核中,每一个消息队列由一个标识符来标识。
2、消息队列独立于发送和接收进程,进程终止时,消息队列以及其内容不会删除。
3、消息队列可以随机查询消息,不一定要按照先进先出的方式读取,也可以按照消息类型来读取。
(4) 共享内存
1、共享内存是两个或者多个的进程共享一个给定的存储区。
2、因为多个进程可以同时对共享内存进行操作,所以需要进行同步。
3、共享内存通常会配合信号量来使用,信号量用来同步共享内存的访问。
(5) 信号
信号对于Linux而言,实际上是一种软中断,主要是用于实现一些异步通信的方式,当然,也可以通过信号来实现进程间的通信。
(6) 信号量
信号量主要用于实现进程间的互斥与同步,用于对临界资源的保护,当一个进程在访问临界资源时,另一个进程则不可以访问临界资源。必须要等待到当前进程访问完临界资源并且释放锁后,另一个进程才可以访问临界资源。
2、案例介绍
2.1、管道
(1) 无名管道
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd[2] = {0};
char read_buf[128] = {0};
char* write_data = "hello world\n";
pid_t pid = 0;
if (pipe(fd) == -1)
{
printf("create pipe fail\n");
}
pid = fork();
if (pid > 0)
{
sleep(3);
close(fd[0]);
write(fd[1], write_data, strlen(write_data));
printf("i am father process\n");
wait(NULL);
}
else if (pid == 0)
{
printf("i am children process\n");
close(fd[1]);
read(fd[0], read_buf, sizeof(read_buf));
printf("i read data : %s\n",read_buf);
exit(0);
}
return 0;
}
(2) 命名管道
当open一个FIFO时,是否设置非阻塞标志的区别(O_NONBLOCK)。
若是没有指定O_NONBLOCK,只读open则会阻塞到某个进程为写而打开此FIFO,只写open则会阻塞到某个进程为读而打开此FIFO。
fifo_read.c
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = 0;
char* file_path = "./file1";
char read_buf[100] = {0};
if ((mkfifo(file_path, 0666) == -1) && errno == EEXIST)
{
printf("this file is exist\n");
}
fd = open(file_path, O_RDONLY);
printf("open file success\n");
while(1)
{
read(fd, read_buf, sizeof(read_buf));
printf("read_buf : %s\n", read_buf);
}
close(fd);
return 0;
}
fifo_write.c
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd = 0;
char* write_data = "i am write data\n";
char* file_path = "./file1";
fd = open(file_path, O_WRONLY);
while(1)
{
write(fd, write_data, strlen(write_data));
sleep(1);
}
close(fd);
return 0;
}
2.2、消息队列
msg_client.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
typedef struct msgbuf
{
long mtype;
char mtext[128];
}msgbuf_t;
int main()
{
key_t key = 0;
int msg_id = 0;
msgbuf_t msg_write_buf = {111, "i am zoudonghong"};
msgbuf_t msg_recv_buf = {0};
key = ftok(".", 11);
msg_id = msgget(key, IPC_CREAT | 0777);
if (msg_id == -1)
{
printf("get msg fail\n");
}
msgsnd(msg_id, &msg_write_buf, strlen(msg_write_buf.mtext), 0);
msgrcv(msg_id, &msg_recv_buf, sizeof(msg_recv_buf.mtext),222,0);
msgctl(msg_id, IPC_RMID, NULL);
printf("i have recv:%s, type = %ld\n",msg_recv_buf.mtext, msg_recv_buf.mtype);
return 0;
}
msg_server.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
typedef struct msgbuf
{
long mtype;
char mtext[128];
}msgbuf_t;
int main()
{
key_t key = 0;
int msg_id = 0;
msgbuf_t msg_read_buf = {0};
msgbuf_t msg_ack_buf = {222, "i am fozu,thank you"};
key = ftok(".", 11);
msg_id = msgget(key, IPC_CREAT | 0777);
if (msg_id == -1)
{
printf("get msg fail\n");
}
msgrcv(msg_id, &msg_read_buf, sizeof(msg_read_buf.mtext), 111, 0);
printf("i recv buf : %s\n", msg_read_buf.mtext);
msgsnd(msg_id, &msg_ack_buf, strlen(msg_ack_buf.mtext), 0);
msgctl(msg_id, IPC_RMID, NULL);
return 0;
}
2.3、共享内存
shm_read.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
int main()
{
key_t key = 0;
char* shm_addr = NULL;
int shmid = 0;
key = ftok(".", 1);
shmid = shmget(key, 4 * 1024, 0);
if (shmid == -1)
{
printf("get shm fail\n");
exit(-1);
}
shm_addr = shmat(shmid, NULL, 0);
printf("i read buf : %s\n", shm_addr);
shmdt(shm_addr);
return 0;
}
shm_write.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
int main()
{
key_t key = 0;
char* shm_addr = NULL;
int shmid = 0;
key = ftok(".", 1);
shmid = shmget(key, 4 * 1024, IPC_CREAT | 0666);
if (shmid == -1)
{
printf("get shm fail\n");
exit(-1);
}
shm_addr = shmat(shmid, NULL, 0);
strcpy(shm_addr, "i am zoudonghong");
sleep(10);
printf("shm get over\n");
shmdt(shm_addr);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
2.4、信号
入门版的信号:
signal_recv.c
#include <stdio.h>
#include <signal.h>
typedef void (*sighandler_t)(int);
static void ldsMySignalHandler(int signal_num)
{
printf("i get signal num = %d\n", signal_num);
}
int main()
{
signal(SIGINT, ldsMySignalHandler);
signal(SIGKILL, ldsMySignalHandler);
signal(SIGUSR1, ldsMySignalHandler);
while(1);
return 0;
}
signal_send.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char** argv)
{
if (argc != 3)
{
printf("you input arg num error\n");
exit(-1);
}
pid_t pid = 0;
int sig = 0;
pid = atoi(argv[1]);
sig = atoi(argv[2]);
printf("pid = %d, sig = %d\n", pid, sig);
kill(pid, sig);
return 0;
}
高级版本:可以实现数据的交互
high_signal_send.c
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char** argv)
{
pid_t pid = 0;
int sig = 0;
union sigval value;
printf("my pid = %d\n", getpid());
if (argc != 3)
{
printf("you input arg error\n");
exit(-1);
}
pid = atoi(argv[1]);
sig = atoi(argv[2]);
value.sival_int = 2000;
sigqueue(pid, sig, value);
return 0;
}
high_signal_recv.c
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void ldsRecvSignalHandler(int sig_num, siginfo_t *info, void* context)
{
printf("sig_num = %d\n", sig_num);
if (context != NULL)
{
printf("i receive pid = %d, data = %d\n", info->si_pid,info->si_int);
printf("data = %d\n", info->si_value.sival_int);
}
}
int main()
{
struct sigaction act;
printf("my pid = %d\n", getpid());
act.sa_sigaction = ldsRecvSignalHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &act, NULL);
while(1);
return 0;
}
2.5、信号量
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
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) */
};
//信号量的P操作
static void ldsPGetKey(int sem_id)
{
struct sembuf sem_val;
sem_val.sem_num = 0;
sem_val.sem_op = -1;
sem_val.sem_flg = SEM_UNDO;
semop(sem_id, &sem_val, 1);
}
//信号量的V操作
static void ldsVPutBackKey(sem_id)
{
struct sembuf sem_val;
sem_val.sem_num = 0;
sem_val.sem_op = 1;
sem_val.sem_flg = SEM_UNDO;
semop(sem_id, &sem_val, 1);
}
int main()
{
key_t key = 0;
pid_t pid = 0;
int sem_id = 0;
union semun sem_item = {0};
key = ftok(".", 1);
sem_id = semget(key, 1, IPC_CREAT | 0666); //创建信号量集合
sem_item.val = 0; //初始信号量值设置为0
semctl(sem_id, 0, SETVAL, sem_item); //给信号量集合赋予初值
pid = fork();
if (pid > 0)
{
ldsPGetKey(sem_id);
printf("i am father process\n");
ldsVPutBackKey(sem_id);
}
else if(pid == 0)
{
ldsVPutBackKey(sem_id);
printf("i am child process\n");
}
else
{
printf("create process fail\n");
}
return 0;
}