系统是以进程进行工作的的,进程的地址空间是独立的,进程之间有可能需要数据交互
进程间通信:
1.管道:有名管道 无名管道
2.信号量
3.消息队列
4.共享内存
管道
1.有名管道(半双工通讯)
单工通信
半双工通讯
全双工通讯
有名管道:在磁盘空间会有一个管道文件标识(inode节点),只要是有权限操作这个管道文件的进程,都可以利用其和其他进程进行数据交互
有名管道虽然在磁盘上有管道文件标识,但在使用管道文件来进行进程通信时,数据会被缓存到内存上,并不会占据磁盘的block区域
有名管道的使用
创建管道文件:
命令 mkfifo filename — 》创建一个管道文件
系统调用 int mkfifo(const char*pathname , mode_t mode);
打开管道文件 open 会阻塞,只有读打开或者只有写打开
写数据 write 有读存在,管道没空余空间,阻塞
读数据 read 有写端存在,但是管道中没有数据,read阻塞
关闭 close
示例:A进程向B进程发送“hello world”,B进程打印
2.无名管道(无管道文件)
数据也是缓存在内存上,也是半双工通讯
借助的是fork时,父子进程共享fork之前打开的文件描述符
限制:只能应用在有关系的进程之间:父子,兄弟
创建并打开无名管道:
int pipe(int fds[2]);需要两个整型变量
pipe方法调用成功,传递的参数:
fds[0]:指向管道的读端
fds[1]:指向管道的写端
使用:read write close
示例:
信号量
相关概念
- 临界资源:同一时刻,只能被一个进程访问的资源
- 临界区:程序中访问临界资源的代码区域
- 原子操作:不能被分割执行,一旦开始就必须结束
- PV操作:P操作进行-1,V操作进行+1操作
信号量时进行进程间同步执行控制的一种手段
直接制约关系:一个进程为另一个进程提供数据
间接制约关系:两个进程竞争临界资源
信号量:是一种特殊的计数器,当信号量大于零时,记录的是临界资源的个数,当信号量等于零时,对信号量执行P操作,则P操作阻塞,当一个进程释放使用的临界资源,是对信号量执行V操作
特殊信号量:二元信号量–》它的值只能是0或者1
信号量使用:
多个进程需要操作的是同一个信号量,信号量必须存储在这些进程能够共享的位置–》系统内核–内核对象:操作系统内核标识:1.ID整型值。2.为使多个进程能够汇合到同一个IPC对象上,有一个外部名:键值(key)
-
创建或者获取
-
初始化–》只有创建时,才需要
-
P操作–》进程进入临界区前
-
V操作–》进程结束临界区后
-
销毁信号量
封装成
- 创建并初始化或者获取一个信号量
- 执行P操作的方法
- 执行V操作的方法
- 销毁信号量的方法
例题:进程a和进程b模拟访问打印机,进程a输出第一个字符‘a’表示开始使用打印机,输出第二个字符‘a’表示结束使用,b进程操作与a进程相同(打印机仅有一台且同一时刻只能被一个进程使用,所以输出结果应该成对出现,例如aabb)
封装方法的头文件
#pragma once
typedef union semun
{
int val;
}SemUn;
int GetSem(int key, int initval);
int DelSem(int semid);
int SemP(int semid);
int SemV(int semid);
方法实现
#include "sem.h"
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
int GetSem(int key,int initval)
{
int semid = semget((key_t)key , 0, 0664);
if(semid == -1)
{
semid = semget((key_t)key, 1, 0664 | IPC_CREAT);
if(semid == -1)
{
return -1;
}
SemUn data;
data.val = initval;
int res = semctl(semid, 0, SETVAL, data);
if(res == -1)
{
DelSem(semid);
return -1;
}
}
return semid;
}
int SemP(int semid)
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
int res = semop(semid, &buf, 1);
if(res == -1)
{
return -1;
}
return 0;
}
int SemV(int semid)
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
int res = semop(semid, &buf, 1);
if(res == -1)
{
return -1;
}
return 0;
}
int DelSem(int semid)
{
int res = semctl(semid, 0, IPC_RMID);
if(res == -1)
{
return -1;
}
return 0;
}
进程a程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <fcntl.h>
#include <signal.h>
#include "sem.h"
int main()
{
srand((unsigned int)time(NULL) * time(NULL));
int semid = GetSem(1234, 1);
assert(semid != -1);
int count = 0;
while(1)
{
SemP(semid);
printf("a");
fflush(stdout);
int n = rand() % 4;
sleep(n);
printf("a");
SemV(semid);
fflush(stdout);
count ++;
if(count == 10)
break;
n = rand() % 3;
sleep(n);
}
exit(0);
}
进程b程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <fcntl.h>
#include <signal.h>
#include "sem.h"
int main()
{
srand((unsigned int)time(NULL) * time(NULL));
int semid = GetSem(1234, 1);
assert(semid != -1);
int count = 0;
while(1)
{
SemP(semid);
printf("b");
fflush(stdout);
int n = rand() % 4 + 1;
sleep(n);
printf("b");
SemV(semid);
fflush(stdout);
count ++;
if(count == 10)
break;
n = rand() % 3 + 1;
sleep(n);
}
exit(0);
}
最终执行结果
ipcs -s 查看当前系统上的信号量集
ipsrm -s semid 删除系统上的信号量集
消息队列
消息:消息是按照条进行计算的,消息是类型+数据的组合
队列:先进先出的数据结构
读取消息的进程可以根据类型(选择性)来读取消息(蓝色块类型,橙色块数据)
在同种类型上,读取消息按照先进先出的原则读取
进程a
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
typedef struct msgbuf
{
long mtype;
char mtext[128];
}msgbuf;
int main()
{
int msgid = msgget((key_t)1234, IPC_CREAT | 0664);
assert(msgid != -1);
msgbuf buf;
memset(&buf, 0 ,sizeof(buf));
buf.mtype = 100;
strcpy(buf.mtext, "hello world");
msgsnd(msgid, &buf, strlen(buf.mtext), 0);
exit(0);
}
进程b
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
typedef struct msgbuf
{
long mtype;
char mtext[128];
}msgbuf;
int main()
{
int msgid = msgget((key_t)1234, IPC_CREAT | 0664);
assert(msgid != -1);
msgbuf buf;
memset(&buf, 0 ,sizeof(buf));
msgrcv(msgid, &buf, 127, 100, 0);
printf("mtype = %d, mtext = %s\n",buf.mtype,buf.mtext);
exit(0);
}
执行结果:
共享内存
(最快的IPC)
管道通过文件系统机制,共享内存不是
1.先申请一块物理内存空间,有共享内存的内核对象来保存
2.各个进程分别将自己的一块虚拟地址空间通过共享内存的内核对象映射到物理内存空间上
3.可以直接通过映射的首地址来访问这块空间
返回值是地址空间的首地址
示例:
进程a从键盘获取数据并拷贝到共享内存中,进程b从共享内存中获取并打印数据。要求进程a输入一次,进程b输出一次,a不输入,b不输出。
sem.h
#pragma once
typedef union semun
{
int val;
}SemUn;
int GetSem(int key, int initval[],int n);
int DelSem(int semid);
int SemP(int semid,int index);
int SemV(int semid,int index);
sem.c
#include "sem.h"
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
int GetSem(int key,int initval[], int n)
{
int semid = semget((key_t)key , 0, 0664);
if(semid == -1)
{
semid = semget((key_t)key, n, 0664 | IPC_CREAT);
if(semid == -1)
{
return -1;
}
int i = 0;
for(; i < n;++i)
{
SemUn data;
data.val = initval[i];
int res = semctl(semid, i, SETVAL, data);
if(res == -1)
{
DelSem(semid);
return -1;
}
}
}
return semid;
}
int SemP(int semid, int index)
{
struct sembuf buf;
buf.sem_num = index;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
int res = semop(semid, &buf, 1);
if(res == -1)
{
return -1;
}
return 0;
}
int SemV(int semid, int index)
{
struct sembuf buf;
buf.sem_num = index;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
int res = semop(semid, &buf, 1);
if(res == -1)
{
return -1;
}
return 0;
}
int DelSem(int semid)
{
int res = semctl(semid, 0, IPC_RMID);
if(res == -1)
{
return -1;
}
return 0;
}
shma.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include "sem.h"
int main()
{
int initval[] = {1, 0};
int semid = GetSem(1234, initval, 2);
assert(semid != -1);
int shmid = shmget(1234, 128, IPC_CREAT | 0664);
assert(shmid != -1);
char *ptr = (char *)shmat(shmid, NULL, 0);
assert(ptr != (char *)-1);
while(1)
{
SemP(semid, 0);
printf("input:");
fgets(ptr, 127, stdin);
SemV(semid,1);
if(strncmp(ptr, "end", 3) == 0)
break;
}
shmdt(ptr);
shmctl(shmid, IPC_RMID, NULL);
exit(0);
}
shmb.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include "sem.h"
int main()
{
int initval[] = {1, 0};
int semid = GetSem(1234, initval, 2);
assert(semid != -1);
int shmid = shmget(1234, 128, IPC_CREAT | 0664);
assert(shmid != -1);
char *ptr = (char *)shmat(shmid, NULL, 0);
assert(ptr != (char *)-1);
while(1)
{
SemP(semid, 1);
if(strncmp(ptr, "end", 3) == 0)
break;
printf("B: %s",ptr);
memset(ptr, 0 ,128);
SemV(semid,0);
}
shmdt(ptr);
shmctl(shmid, IPC_RMID, NULL);
DelSem(semid);
exit(0);
}
执行结果: