共享內存 :內核空間預留出來的一塊內存,用於進程間通訊linux
共享內存是一種最高效的進程間通訊方式,由於進程能夠直接讀寫內存,不須要任何數據的複製,爲了在進程間交換信息,內核專門留出了一塊內存區,這段內存能夠由要訪問的進程將其映射到本身的私有地址空間,所以,進程就能夠直接讀寫這一內存而不須要進行數據的複製,從而大大提升了效率,固然,因爲多個進程共享一段內存,所以也須要依靠某種同步機制,如互斥鎖和信號量等對象
(1)int shmget(key_t key, size_t size, int shmflg);blog
功能:獲取共享內存段的ID進程
參數:
@key IPC_PRIVATE 或 ftok()
@size 申請的共享內存段大小 [4k的倍數]
@shmflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL 內存
返回值:
成功返回ID,失敗返回-1 ci
(2)void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享內存到用戶空間
參數:
@shmid 共享內存段ID
@shmaddr NULL:系統自動完成映射
@shmflg: SHM_RDONLY:只讀 0:讀寫
返回值:
成功返回映射後的地址,失敗返回(void *)-1資源
(3)int shmdt(const void *shmaddr);
功能:撤銷映射
參數:
@shmaddr 共享內存映射的地址 get
注意:當一個進程結束的時候,它映射共享內存,會自動撤銷映射cmd
(4)int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:根據命令控制共享內存
參數:
@shmid 共享內存段的ID
@cmd IPC_STAT[獲取屬性],IPC_SET[設置屬性],IPC_RMID[刪除IPC對象]
@buf 保存屬性
返回值:
成功返回0,失敗返回 -1 同步
----------------------------------------------------------------------------
注意:當咱們調用shmctl刪除共享內存的時候,並不會當即刪除。只有當共享內存映射
次數爲0,纔會刪除共享內存對象
-----------------------------------------------------------------------------
實例以下:
shm_r.c
#define SHM_SIZE 4096
typedef struct{
int read_flag;
int write_flag;
char mtxt[1];//標示數據存放地址
}data_t;
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void read_shm(void *addr)
{
data_t *pdata;
pdata = (data_t *)addr;
pdata->read_flag = 0;
pdata->write_flag = 1;
while(1){
if(pdata->read_flag){
printf("Read : %s!\n",pdata->mtxt);
pdata->write_flag = 1;
pdata->read_flag = 0;
}
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
read_shm(addr);
return 0;
}
shm_w.c
#define SHM_SIZE 4096
typedef struct{
int read_flag;
int write_flag;
char mtxt[1];//標示數據存放地址
}data_t;
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void write_shm(void *addr)
{
data_t *pdata;
pdata = (data_t *)addr;
pdata->read_flag = 0;
pdata->write_flag = 1;
while(1){
if(pdata->write_flag){
fgets(pdata->mtxt,SHM_SIZE - 8,stdin);//讀寫標誌共佔8個字節
pdata->mtxt[strlen(pdata->mtxt) - 1] = '\0';
pdata->write_flag = 0;
pdata->read_flag = 1;
}
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
write_shm(addr);
return 0;
}
運行並執行shm_w.c
運行並執行shm_r.c
思路仍是比較簡單
上面的代碼能夠改進嗎?
這段代碼涉及到信號量的知識,能夠查看:linux進程間通訊--信號量
先引進:
sem.c
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 init_sem_value(int sem_id,int sem_num,int value)
{
union semun sem_val;
sem_val.val = value;
if(semctl(sem_id,sem_num,SETVAL,sem_val) < 0)
{
perror("Fail to semctl");
exit(EXIT_FAILURE);
}
return ;
}
int init_sems(const char *filename,int nsem)
{
key_t key;
int sem_id;
key = ftok(filename,'b');
if(key < 0){
perror("Fail to ftok");
exit(EXIT_FAILURE);
}
sem_id = semget(key,nsem,IPC_CREAT|IPC_EXCL| 0666);
if(sem_id < 0)
{
//已經存在
if(errno == EEXIST)
{
//從新得到ID
sem_id = semget(key,nsem,IPC_CREAT | 0666);
}else{
//其餘錯誤
perror("Fail to semget");
exit(EXIT_FAILURE);
}
//不存在,建立好後,初始化
}else{
init_sem_value(sem_id,0,0);//0號信號燈初始化爲0
init_sem_value(sem_id,1,1);//1號信號燈初始化爲1
}
return sem_id;
}
void P(int sem_id,int sem_num)
{
struct sembuf sem;
sem.sem_num = sem_num;
sem.sem_op = -1;
sem.sem_flg = 0;
if(semop(sem_id,&sem,1) < 0)
{
perror("Fail to semop for P\n");
exit(EXIT_FAILURE);
}
return ;
}
void V(int sem_id,int sem_num)
{
struct sembuf sem;
sem.sem_num = sem_num;
sem.sem_op = 1;
sem.sem_flg = 0;
if(semop(sem_id,&sem,1) < 0)
{
perror("Fail to semop for V\n");
exit(EXIT_FAILURE);
}
return ;
}
shm_r.c修改以下:
#define SHM_SIZE 4096
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void read_shm(void *addr,int semid)
{
char *pdata;
pdata = (char *)addr;
while(1){
//P操做讀資源
P(semid,0);//申請讀資源
printf("Read : %s!\n",pdata);
//V操做寫資源
V(semid,1);//釋放寫資源
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
int semid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
//信號燈集
semid = init_sems(argv[1],2);
read_shm(addr,semid);
return 0;
}
shm_w.c修改以下:
#define SHM_SIZE 4096
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void write_shm(void *addr,int semid)
{
char *data = (char *)addr;
while(1){
P(semid,1);
fgets(data,SHM_SIZE,stdin);
data[strlen(data) - 1] = '\0';
V(semid,0);
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
int semid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
semid = init_sems(argv[1],2);
write_shm(addr,semid);
return 0;
}
運行結果如上