进程间通信
- 管道
- 消息队列
- 信号量
- 共享内存
管道
无名管道 Pipe
- 同主机进程间数据交互机制:
- 无名管道(PIPE):只有创建该管道的程序,才能够找到该管道(亲缘进程通信)
- 单向传输
- 控制进程只有pipe的一端
- pipe的阻塞操作
*fd[0] 读 fd[1]写
/*匿名管道*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main ()
{
int fd[2];
int ret;
pid_t pid;
if ( pipe(fd) == -1)
{
perror("pipe error");
return -1;
}
if ( ( pid = fork() ) == -1)
{
perror("fork error");
return -1;
}
if (pid > 0) //父进程
{
close(fd[0]); //关闭读端
char * str = "hello";
write(fd[1],str,strlen(str)+1);
close(fd[1]);
wait(NULL);
}
else if (pid == 0) //子进程
{
close(fd[1]);//关闭写端
char str[64] = {0};
ret = read(fd[0],str,sizeof(str));
write(STDOUT_FILENO,str,ret);
close(fd[0]);
}
return 0;
}
有名管道 fifo
- 依赖于文件系统,像普通文件一样具有磁盘路径,文件权限和其他属性,所有程序都可以通过path找到有名管道
- fifo是一个文件,存储信息在内存,当两个进程都消失,数据消息,文件只有接口的作用
- mkfifo
int mkfifo(const char* pathname, mode_t mode); - 单项通信
- 只能够进行少量数据传输
- 只能一对一进行传输
- 两个进程,中途退出了一个进程,未退出的一端如果是写操作的话,返回sigpipe信号
- 未退出的一端如果是阻塞读操作的话,该读操作不会继续阻塞,直接返回0
fifo_w.c
/*命名管道*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main ()
{
int key,ret;
int fd;
//判断文件是否存在
ret = access("fifo.tmp",F_OK);
if (ret == -1) //文件不存在,则创建
{
if (mkfifo("fifo.tmp",0664) == -1)
{
perror("mkfifo error");
exit(1);
}
else
{
printf("mkfifo sucess!
");
}
}
fd = open("fifo.tmp",O_WRONLY);
while(1){
char *pt = "hello world !";
write (fd , pt ,strlen(pt)+1);
sleep(1);
}
close(fd);
return 0;
}
fifo_r.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd ;
int ret ;
if ( access("fifo.tmp",F_OK) == -1)
{
printf("no exit file
");
exit(1);
}
fd = open("fifo.tmp",O_RDONLY);
if(fd == -1)
{
perror ("open error");
return 1;
}
while(1)
{
char buf[64] = {0};
read(fd,buf,sizeof(buf));
printf("%s
", buf);
}
close(fd);
return 0;
}
消息队列
-
创建消息对象:
int msgget(key_t key, int msgflg); -
设置对象:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
cmd:
IPC_RMID 删除
IPC_SET 设置ipc_perm参数
IPC_STAT 获取ipc_perm参数
IPC_INFO 获取ipc信息 和ipcs -
使用对象:
- 发送
int msgsnd(int msqid,const void *msg_ptr,size_t sz,int msgflg); - 接收
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- 发送
-
消息的数据结构,数据前有个消息类型
-
struct msgbuf
{
long mtype; /* message type, must be > 0 /
char mtext[1]; / message data */
};
msq_w.c
/*消息队列*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/msg.h>
#define MAX_TEXT 1024
//消息结构体
struct my_msg{
long int my_msg_type;//消息类型
char buf[MAX_TEXT];//消息数据
};
int main ()
{
int msqid;
struct my_msg data;
msqid = msgget((key_t)12345,0664|IPC_CREAT) ;//创建消息队列
if (msqid == -1)
{
perror("msgget error");
exit(-1);
}
int i = 0;
for (i = 0;i < 5;++i) //发五条消息
{
printf("please input: ");
fgets(data.buf,BUFSIZ,stdin);
data.my_msg_type = i+1;
//发送消息
if ( msgsnd(msqid,(void *)&data,sizeof(data.buf),0) == -1)
{
perror("msgsnd error");
exit(-1);
}
}
return 0;
}
msq_r.c
/*消息队列*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/msg.h>
#define MAX_TEXT 1024
struct my_msg
{
long int my_msg_type;
char buf[MAX_TEXT];
};
int main ()
{
int msqid;
struct my_msg data;
msqid = msgget( (key_t)12345,0664|IPC_CREAT);//获取消息队列
if (msqid == -1)
{
perror("msgget error");
exit(-1);
}
int i = 0;
for (i = 0;i < 5;++i) //收五条消息
{
//发送消息
if ( msgrcv(msqid,(void *)&data,BUFSIZ,i+1,0) == -1)
{
perror("msgrcv error");
exit(-1);
}
//打印
printf("rcv:%ld
", data.my_msg_type);
printf("rcv:%s
", data.buf);
}
//删除消息队列
if (msgctl(msqid,IPC_RMID,NULL) == -1)
{
perror("msgctl error");
exit(-1);
}
return 0;
}
信号量
- 实现进程间同步
- 标识系统可用资源的个数
对象:
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */信号量个数
struct sem * sem_base
};
struct sem{
int semval 信号量的值
int sempid 最近一个操作的进程号
}
对象操作
-
创建对象:
int semget(key_t k,int n,int semflag);//key 个数 权限 -
设置对象:
int semctl(int sid,int semnum,int cmd,union semun a);
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) */
};
int semop(int s,struct sembuf *sb,size _t n);//操作信号量
struct sembuf
{
unsigned short int sem_num; //信号量的下标
short int sem_op; // 操作码 正数 表示增加信号的值
short int sem_flg;
};
sem_flg 操作标识 有两个取值IPC_NOWAIT 和 SEM_UNDO
IPC_NOWAIT 如果操作信号集合中任意一个失败,立即返回,并且不会对其他的信号量做操作,所以的信号量都不被操作
SEM_UNDO :进程退出后,该进程对sem进行的操作将被撤销
sem_w.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
//自定义联合体
union semu{
int val ;
struct semid_ds * buf;
unsigned short * array;
struct seminfo * _buf;
};
static int semid;
void init()
{
union semu sem_u;
key_t key;
key = ftok(".",0);
short arr[2];
arr[0] = 0; //生产出的产品数
arr[1] = 100; //仓库剩余容量
semid = semget(key,2,0664 | IPC_CREAT);
if (semid == -1)
{
perror("semget error");
exit(-1);
}
sem_u.array = arr;
if (semctl(semid,0,SETALL,sem_u) == -1 )
{
perror("semctl error");
exit(-1);
}
}
// p 操作 -1
int op_p(int num)
{
struct sembuf sem_p;
sem_p.sem_num = num;
sem_p.sem_op = -1;
sem_p.sem_flg = 0;
if (semop(semid,(struct sembuf *)&sem_p,1) == -1)
return 0;
return 1;
}
// v 操作 +1
int op_v(int num)
{
struct sembuf sem_v;
sem_v.sem_num = num;
sem_v.sem_op = 1;
sem_v.sem_flg = 0;
if (semop(semid,(struct sembuf *)&sem_v,1) == -1)
return 0;
return 1;
}
int main ()
{
init();
while(1)
{
printf("before
");
printf("productor number is %d
",semctl(semid,0,GETVAL));
printf("space number is %d
",semctl(semid,1,GETVAL));
op_p(1);//写一个,那么仓库容量减1 (如果容量不足将阻塞等待)
printf("生产了一件商品
");
op_v(0);//产品数目增加1 (即通知消费者可以消耗商品)
printf("before
");
printf("productor number is %d
",semctl(semid,0,GETVAL));
printf("space number is %d
",semctl(semid,1,GETVAL));
sleep(4);
}
//删除信号量
semctl(semid,IPC_RMID,0);
return 0;
}
sem_r.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
static int semid;
void init()
{
key_t key;
key = ftok(".",0);
semid = semget(key,2,0664 | IPC_CREAT);
if (semid == -1)
{
perror("semget error");
exit(-1);
}
}
// p 操作 -1
int op_p(int num)
{
struct sembuf sem_p;
sem_p.sem_num = num;
sem_p.sem_op = -1;
sem_p.sem_flg = 0;
if (semop(semid,(struct sembuf *)&sem_p,1) == -1)
return 0;
return 1;
}
// v 操作 +1
int op_v(int num)
{
struct sembuf sem_v;
sem_v.sem_num = num;
sem_v.sem_op = 1;
sem_v.sem_flg = 0;
if (semop(semid,(struct sembuf *)&sem_v,1) == -1)
return 0;
return 1;
}
int main ()
{
init();
while(1)
{ printf("before
");
printf("productor number is %d
",semctl(semid,0,GETVAL));
printf("space number is %d
",semctl(semid,1,GETVAL));
//编号 0 是商品数目 1 是库存
op_p(0);//消费一个,那么商品数目减一(如果商品数不足将阻塞等待)
printf("消费了一件商品
");
op_v(1);//库存容量增加1
printf("after
");
printf("productor number is %d
",semctl(semid,0,GETVAL));
printf("space number is %d
",semctl(semid,1,GETVAL));
sleep(3);
}
return 0;
}
共享内存 (内存映射的方式)
- 数据量大
- 传输最快
- nmap
共享内存对象
struct shmid_ds
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
创建共享内存
int shmget (key_t key, size_t size, int shmflg) ;
设置共享内存
int shmctl (int shmid, int cmd, struct shmid_ds *buf) ;
特殊 cmd:
SHM_INFO
SHM_STAT
SHM_LOCK
SHM_UNLOCK
使用对象
void *shmat (int shmid, const void *shmaddr, int shmflg);//将进程与共享内存绑定
int shmdt (const void * shmaddr); //解除绑定
// shmaddr 填NULL ,由内核帮助你来分配
- smflg:
SHM_RDONLY //只读
SHM_REMAP //重新映射
SHM_EXEC //可读可写
0 //可读可写
注意
1.父子进程的共享内存约定
fork函数 子进程会继承共享内存
exec执行一个新的程序 自动卸载
共享内存用完了记得shmdt();
信号量 和 共享内存的连用,信号通知,然后从共享内存中读
shm_w.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#define SEGSIZE 680
/*对于共享内存,获得内存地址后,更具你要传递的数据的类型
该指针进行类型转换即可,这样很方便完成数据的传递,非常不错的IPC方式*/
typedef struct data
{
char name[64];
int age ;
}Sdat;
int main(int argc, char const *argv[])
{
key_t key;
int shm_id;
Sdat * smap = NULL;
key = ftok(".",2);
//创建共享内存
shm_id = shmget(key,SEGSIZE,IPC_CREAT | 0664);
if (shm_id == -1)
{
perror("shmget error");
return 1;
}
//将进程与共享内存绑定,映射
smap =(struct data*)shmat(shm_id,NULL,0);
char buf[64] = "john";
int i = 0;
for (i = 0;i < 10 ; ++i) //往共享内存中写东西
{
strcpy((smap+i)->name,buf);
(smap+i)->age = i*10;
}
if (shmdt(smap) == -1)
{
perror("shmdt error");
return -1;
}
return 0;
}
shm_r.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#define SEGSIZE 680 //刚好10个结构体大小
/*对于共享内存,获得内存地址后,更具你要传递的数据的类型
该指针进行类型转换即可,这样很方便完成数据的传递,非常不错的IPC方式*/
typedef struct data
{
char name[64];
int age ;
}Sdat;
int main(int argc, char const *argv[])
{
key_t key;
int shm_id;
Sdat * smap = NULL;
key = ftok(".",2);
//创建共享内存
shm_id = shmget(key,SEGSIZE,IPC_CREAT | 0664);
if (shm_id == -1)
{
perror("shmget error");
return 1;
}
//将进程与共享内存绑定,映射
smap =(struct data*)shmat(shm_id,NULL,0);
int i = 0;
for (i = 0;i < 10 ; ++i) //从共享内存中读东西
{
printf("%s
",(smap+i)->name );
printf("%d
",(smap+i)->age );
}
if (shmdt(smap) == -1)
{
perror("shmdt error");
return -1;
}
if ( -1== shmctl(shm_id,IPC_RMID,0) )
{
perror("shmctl error");
return -1;
}
return 0;
}
信号和共享内存的混合使用
sem_shm_w.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int running = 1;
key_t key;
int sem_id;
int shm_id;
void * nmap;
struct sembuf sem_u;
sem_u.sem_num = 0;
sem_u.sem_flg = SEM_UNDO;
key = ftok(".",3);
if (sem_id = semget(key,1,IPC_CREAT | 0664) ==-1)
{
perror("semget error");
return -1;
}
if (semctl(sem_id,0,SETVAL,0) == -1)
{
printf("sem init error");
return -1;
}
shm_id = shmget(key,(size_t)4096,IPC_CREAT | 0664);
if (shm_id == -1)
{
printf("shm init error");
return -1;
}
if ( (nmap = shmat(shm_id,NULL,0) )== NULL)
{
printf("shmat error");
return -1;
}
while(running)
{
if (semctl(sem_id,0,GETVAL)==0) //可以写数据
{
printf("please input something:");
scanf("%s",(char *)nmap);
sem_u.sem_op = 1;
if (semop(sem_id,&sem_u,1)==-1)
{
printf("semop error");
return -1;
}
}
if (strcmp(nmap,"end")==0)
running--;
}
shmdt(nmap);
return 0;
}
sem_shm_r.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int running = 1;
key_t key;
int sem_id;
int shm_id;
void * nmap;
struct sembuf sem_u;
sem_u.sem_num = 0;
sem_u.sem_flg = SEM_UNDO;
key = ftok(".",3);
if (sem_id = semget(key,1,IPC_CREAT | 0664) ==-1)
{
perror("semget error");
return -1;
}
shm_id = shmget(key,(size_t)4096,IPC_CREAT | 0664);
if (shm_id == -1)
{
printf("shm init error");
return -1;
}
if ( (nmap = shmat(shm_id,NULL,0) )== NULL)
{
printf("shmat error");
return -1;
}
while(running)
{
if (semctl(sem_id,0,GETVAL)==1) //可以读数据
{
printf("recv:%s
",(char*)nmap);
sem_u.sem_op = -1;
if (semop(sem_id,&sem_u,1)==-1)
{
printf("semop error");
return -1;
}
}
if (strcmp(nmap,"end")==0)
running--;
}
shmdt(nmap);
if (shmctl(shm_id,IPC_RMID,0)==-1)
{
printf("shm_id error");
return -1;
}
if (semctl(sem_id,0,IPC_RMID,0)==-1)
{
printf("sem_id error");
return -1;
}
return 0;
}