共享内存:
它是一种两个进程间传递信息的媒介。每一个进程都可以将共享内存连接到
自己的地址空间中。共享内存可以视为一种文件,对于共享内存的写操作将会被可以访问
同一段共享内存的任何进程所看见。同步工作要有具体程序员负责。
1.主要的接口(头文件#include<sys/shm.h>)
int shmget(key_t key,size_t size,int shmflg)
该函数成功返回共享内存的标识符,是一个非负整数,失败返回-1。
第一个参数key提供一个键值,类似名字的作用。有一个特殊的键值是IPC_PRIVATE,这个
键值表示创建一个私有只属于创建进程的共享内存。在linux中其实大多数情况并非真正
私有。
第二个参数是共享内存需要的字节数
第三个参数是包含9个比特的权限标志,IPC_CREAT是一个特殊的它通常写成IPC_CREAT|0600
相当于设置了该进程的属主程序具有读写权限。
void *shmat(int shm_id,const void *shm_addr,int shmflg)
该功能主要是与已经建立好的共享内存建立映射关系。
第一个参数是共享内存的标识符。
第二个参数通常是设置共享内存映射的位置,是一个指针通常为NULL让系统自动选择。
第三个参数两个可能的取值分别是SHM_RND,SHM_RDONLY。通常第一个不用,第一个主要是
控制共享内存的地址。第二个是以只读的方式打开。
int shmdt(void *shmat)
它的参数是shmat返回的指针,成功返回0,失败返回-1.
int shmctl(int shm_id,int command,struct shmid_ds *buf);
第一个是共享内存的标识符
第二个有三个参数,通常使用IPC_RMID
2.ipcs是查看共享内存的终端命令,我们可以在终端中通过它看见是否有共享内存。ipcrm删除共享内存的终端命令。
信号量:
信号量主要用于进程间同步。
为什么要同步?因为多个进程在某一时刻的代码段上进入了临界区去访问同一个临界资源。
什么是临界资源?临界资源就是例如像共享内存管道等这样的可以同时被多个进程访问的
内存物理空间。面对临界区和临界资源我们要使用做原子操作。
什么是原子操作?原子操作就是在同一时间内只能由一个进程来操作一个资源进行读写操作。
我们怎么达到原子操作?使用信号量。只有合理使用原子操作才能合理的解决竞争临界资源,
以及有序的处理好生产者消费者之间的关系。
1.ipcs可以查看信号量 ipcrm -s idsem 删除对应信号量。
2.主要的接口
它没有现成的库函数,因此我们要自己使用系统调用完成,信号量的创立删除pv等操作函数。
头文件#include<sys/sem.h>
int semget(key_t key,int num_sems,int sem_flags);
第一个参数是信号量的键,我们使用信号量主要通过标识符,这正是这个函数成功的返回值。
失败将返回-1。
第二个参数是信号量的个数,大多数情况下都设为1。
第三个参数是标志位,他与共享内存的类似IPC_CREAT|IPC_EXCL即为建立一个新的信号量。
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops)
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
}
第一个参数是semget的返回值,第二个参数是一个如上的结构体,结构体中的第一个一般
取0除非你需要一组信号量,第二个是可以设为+1-1及pv操作,第三个是标志位,SEM_UNDO
是程序退出时如果未释放信号量内核自己释放。
int semctl(int sem_id,int sem_num,int command)
第一个参数是semget的返回值,第二个参数一般为0除非用到成组信号量的时候它是信号量
的编号。第三个参数多为IPC_RMID为删除一个信号量。
实践程序:
我们通过共享内存进行内存间通信简单的实现。其中用信号量完成进程间的同步工作。
并通过程序简单的展示生产者消费者之间简单的逻辑关系。
信号量的基本逻辑结构如下
信号量:S1=0,S2=1
a程序: b程序:
P s2 P s1
临界区 临界区
V s1 V s2
源程序(a.c b.c c.c d.c)
a.c
#include"c.c"
int main()
{
int shmid=shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid!=-1);
printf("shmid=%d\n",shmid);
char *s=shmat(shmid,NULL,0);//rang xitong xuan ze gongxiangneicun
assert(s != (char *)-1);
int a[2]={1,0};
sem_init(2,a);
while(1)
{
sem_p(0);
char buff[128]={0};
fgets(buff,128,stdin);
strcpy(s,buff);
sem_v(1);
if(strncmp(buff,"end",3)==0)
{
break;
}
}
shmdt(s);
}
b.c
#include"c.c"
int main()
{
int shmid =shmget((key_t)1234,256,IPC_CREAT|0600);
assert(shmid != -1);
char *p=(char *)shmat(shmid,NULL,0);
assert(p!=(char *)-1));
int a[2]={0,1};
sem_init(2,a);
while(1)
{
sem_p(1);
if(strncmp(p,"end",3)==0)
{
sem_des();////
break;
}
printf("p=%s\n",p);
sleep(1);
sem_v(0);
}
shmdt(p);
}
c.c
union semun
{
int val;
};
void sem_init(int n,int a[]);
void sem_p(int index);
void sem_v(int index);
void sem_des();
d.c
#include"sem.h"
static int semid=-1;
void sem_init(int n,int a[])
{
semid=semget((key_t)1234,n,IPC_CREAT|IPC_EXCL|0600);
union semun b;
int i=0;
for(;i<n;i++)
{
b.val=a[i];
if(semctl(semid,i,SETVAL,b)==-1)
{
perror("error");
}
}
}
void sem_p(int index)
{
struct sembuf buf;
buf.sem_num=index;
buf.sem_op=-1;//p
buf.sem_flg=SEM_UNDO;
if(semop(semid,&buf,1)==-1)
{
perror("p error");
}
}
void sem_v(int index)
{
struct sembuf buf;
buf.sem_num=index;
buf.sem_op=1;//v
buf.sem_flg=SEM_UNDO;
if(semop(semid,&buf,1)==-1)
{
perror("v error");
}
}
void sem_des()
{
if(semctl(semid,0,IPC_RMID)==-1)
{
perror("semctl error");
}
}
程序c.c为程序d.c的头文件程序。我们可以先将d.c以管理员身份加入lib 或者usr/lib文件中进行编译。
或者使用绝对路径进行编译。a.c与b.c同时运行检验结果。