进程间通信——共享内存(shm)

1. System V IPC机制(msg/shm/sem)

System V引入了三种高级的进程间通信机制:
       
 消息队列(msg)
        共享内存(shm)
        信号量(sem)


                       消息队列                 共享内存                信号量
头文件         <sys/msg.h>          <sys/shm.h>        <sys/sem.h>
创建                 msgget                 shmget                 semget
操作                 msgctl                  shmctl                  semctl
...

System V IPC原理:
        
就是在内核中,建立一个数据结构,提供一些操作数据结构的函数接口

使用步骤:
(1) 先向内核申请一个IPC key("许可证")
        IPC对象通过“key”来引用和访问IPC对象,所有的system V的IPC对象在内核中都有一个唯一的标识ID,在用户空间我们称之为“key”
       
 key:唯一的标识一个system V的IPC对象(msg,shm,sem)
        不同的进程利用相同的“key”可以访问同一个IPC对象
     
  key:由用户提供的一个整数一个字符串生成
    

(2) 利用KEY创建或者打开IPC对象(msg,shm,sem)

        不同的进程利用相同的“key”可以访问同一个IPC对象
        msgget:创建或者打开system v 消息队列
        shmget:创建或者打开system v 共享内存
        semget:创建或者打开system v 消息队列
    

(3) 读 / 写 / 操作

        三种IPC对象的操作方法都不一样

 2. System V 共享内存

进程间通信常用的方法,多个进程共享一段内存“共享内存”
   
    进程的地址空间是独立的(共享的内存是存在于内核)
        “随内核的持续性”


共享内存其实就是通过映射的方式使得多个进程可以同时访问同一块内存,换而言之就是将一块内存同时映射到多个进程的进程地址空间中,借此这些进程就可以通过这块内存进行数据交流。这块内存就称之为共享内存


由于是多个进程共享一段内存,这段内存既是你的,又是我的,p1往这段内存中写入数据,实际上就是写入p2的内存中。比管道少一次copy
        共享内存的效率比管道高

 

实现方式:
    
    在内核中开辟(申请)一块共享内存,其他的进程通过“映射”方式获取这一段内存的引用(指针)
        进程P1可以映射并且操作这一段内存,同时P2也可以映射并且操作这一段内存

使用方式:

(1) 申请一个system V IPC的key:ftok

        所有的system V的IPC是相同的
        ftok

NAME
    ftok - convert a pathname and a project identifier to a System V IPC key
SYNOPSIS
    #include <sys/types.h>
    #include <sys/ipc.h>

ftok的英文可以理解为 file to key
ftok利用一个文件名和一个项目ID(整数)生成一个key
        
key_t ftok(const char *pathname, int proj_id);
       
    pathname:一个文件的路径名(必须存在)
       
    proj_id:一个指定的整数
            
        如果两个进程要通过system V通信,那么他们的ftok的两个参数必须相同,这样才
可以产生同样的key,从而打开同一个IPC对象
       
    返回值:
        成功返回一个ipc的key,类型是key_t
        失败返回-1,同时errno被设置

key_t key = ftok(PATHNAME, PROJID);
(2) 创建或者打开system V共享内存对象:shmget

        shmget(share memory)

NAME
    shmget - allocates a System V shared memory segment
SYNOPSIS
    #include <sys/ipc.h>
    #include <sys/shm.h>
      
shmget:用来在内核中创建或者打开一个system V的共享内存(IPC对象)
       
int shmget(key_t key, size_t size, int shmflg);
      
    @key:system V IPC对象的key,一般是ftok的返回值
      
    @size:以字节为单位指定共享内存区域的大小
        在实际操作时,当shmget是创建一个新的共享内存区域时,必须指定一个不为0的size
值,一般是4096的整数倍,如果shmget是打开一个已经存在的共享内存区域时,size == 0
一旦创建成功,size就不能改变啦

    @shmflg:标志位
        a. 创建共享内存
                1.如果底层已经存在
                    【1】第二个参数size是底层存在的那个共享内存的大小,则获取并返回
                    【2】第二个参数size不是底层存在的那个共享内存的大小,
则创建失败:shmget system V failed: Invalid argument

                2.如果不存在,则创建共享内存然后再返回

            IPC_CREAT | 权限位(同open函数)
            如:
            IPC_CREAT | 0664(S_IRUSR...)

        b. 打开 0
        
    返回值:
        成功返回共享内存区域的id(标识打开的共享内存对象),这个id就标识唯一的共享内存
        失败返回-1,同时errno被设置
===============================================================================
创建共享内存,如果底层已经存在,则获取并返回;如果不存在,则创建共享内存然后再返回
int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);

打开
int shm_id = shmget(key, 0, 0);
(3) 映射:shmat / 解映射:shmdt

        映射:把内核或者设备或者文件中的一段内存映射到进程地址空间,映射成功后,进程就可以直接利用指针去操作这一段内存(操作映射后的指针,就相当于操作这一段内存)

NAME
    shmat, shmdt - System V shared memory operations
SYNOPSIS
    #include <sys/types.h>
    #include <sys/shm.h>

shmat:用来映射一段systemV共享内存到进程的地址空间中去

void* shmat(int shmid, const void *shmaddr, int shmflg);

    shmid:要映射的共享内存的id,shmget的返回值,表示你要映射哪一个共享内存
           区域到调用者进程地址空间
        
    shmaddr:要把共享内存区域映射到进程地址空间的哪一个地方
             一般人不知道,设置为NULL,表示让OS自行分配

    shmflg:映射标志,常用的有以下几个
            a. SHM_RDONLY 只读,映射成功后,进程对这一段内存只有读的权限
            b. 0 读写
        
    返回值:
        成功返回映射后的首地址
        失败返回NULL,同时errno被设置

char *p = (char*)shmat(shm_id, NULL, 0);
shmdt:用来解映射一段systemV共享内存区域    
        
int shmdt(const void *shmaddr);    
        
    shmaddr:要解映射的地址,是shmat的返回值

    返回值:
        成功返回0
        失败返回-1,同时errno被设置

shmdt(p);
    
(4) 其他操作:shmctl

        shmctl:用来进行除了映射和解映射之外的控制操作,如:获取属性,删除共享内存...

NAME
    shmctl - System V shared memory control
SYNOPSIS
    #include <sys/ipc.h>
    #include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
        
    shmid:要进行控制操作的共享内存的ID,shmget的返回值
        
    cmd:命令号,表示要进行什么操作,常用的有以下几个
             IPC_RMID 删除指定的共享内存区域
                 当CMD == IPC_RMID时,buf为NULL

             IPC_STAT 获取共享内存的属性
                 当CMD == IPC_STAT时,buf为一段可用的内存空间,保存获取的属性

             IPC_SET 设置共享内存的属性
                 当CMD == IPC_SET时,buf指向你要设置的属性的结构体
        
     返回值:
         成功返回0
         失败返回-1,同时errno被设置

// 删除共享内存 
shmctl(shm_id, IPC_RMID, NULL);

注意:删除共享内存仅仅是标记要销毁,当最后一个进程分离它之后,才删除
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID    250
#define  SIZE      4096

int main(void) {

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	char *p = (char*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作
	pid_t pid = fork();
	if (pid < 0) {
		perror("fork failed");
        return -1;
	} else if (pid > 0) {
		// 父进程
		wait(NULL);
		printf("%s\n", p);
	} else {
		// 子进程
		strcpy(p, "Hello my father");
	}

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

3. 练习

共享内存的使用:
        p1  不断往共享内存中写入数据
        p2  不断从共享内存区域中把数据读取出来

/*   
	p1 不断往共享内存中写入数据
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID    250
#define  SIZE      4096

int main(void) {

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	char *p = (char*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4.操作(通过指针p操作共享内存区域)
	while (1) {
		char buf[256] = {0};
		fgets(buf, 256, stdin); // 会将最后一个回车也写入到buf数组中去
		strcpy(p, buf);
	}

ERR:
	// 5.解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}
/*   
	p2 不断从共享内存区域中把数据读取出来
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID    250
#define  SIZE      4096

int main(void) {

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	char *p = (char*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
	while (1) {
        sleep(3);
		printf("%s\n", p);
	}

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

问题:
        在内核中创建一个共享内存区域(system V),把这个共享内存区域的前面4个字节当成一个int来看

int *p;
*p = 0;
    
p1进程
i = 100000;
while (i--) {
    (*p)++;
}
    
p2进程
i = 100000;
while (i--) {
    (*p)++;
}    

        两个进程,p1,p2同时访问共享内存区域(语法是没有问题的),按道理说,p1和p2执行完毕后,*p应该为200000

代码实现:

        (1) 父子进程同时++,父进程等待子进程的退出

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID    250
#define  SIZE      4096

int main(void) {

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}
	
	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
	*p = 0;
    int i = 100000;
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        goto ERR;
    } else if (pid > 0) {
        // 父进程
        while (i--) {
            (*p)++;
        }
        wait(NULL); // 等待子进程退出
        printf("%d\n", *p);
    } else if (pid == 0) {
        // 子进程
        while (i--) {
            (*p)++;
        }
    }

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

        (2) 有名管道:一个终端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "errno.h"

#define  SYSTEM_V_PATHNAMEV  "/home/china/SC"      // 不能写成 "~/SC"
#define  FIFO_PATHNAME       "/home/china/SC/fifo" // 不能写成  "~/SC/fifo"
#define  PROJID  250
#define  SIZE    4096

int main(void) {

    // 创建一个有名管道
    int r = mkfifo(FIFO_PATHNAME, 0664);
	if (-1 == r) {
		if (errno != EEXIST) {
			perror("mkfifo error");
			return -1;
		}
	}

	// 1.申请一个system V IPC的key
	key_t key = ftok(SYSTEM_V_PATHNAMEV, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

    *p = 0;

	// 4. 操作(通过指针p操作共享内存区域)
    int i = 100000;
	int fifofd;

	pid_t pid = fork();
	if (pid == -1) {
		perror("fork failed");
		goto ERR;
	} else if (pid > 0) {
		// 父进程
		fifofd = open(FIFO_PATHNAME, O_RDONLY); // 只读打开
		while (i--) {
        	(*p)++;
        	printf("%d\n", *p);
		}
		wait(NULL);
    } else if (pid == 0) {
		fifofd = open(FIFO_PATHNAME, O_WRONLY); // 只写打开
		while (i--) {
        	(*p)++;
        	printf("%d\n", *p);
		}
	}

	// 关闭有名管道
    close(fifofd);

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

          (3) 有名管道:两个终端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "errno.h"

#define  FIFO_PATHNAME      "/home/china/SC/fifo" // 不能写成  "~/SC/fifo"
#define  SYSTEM_V_PATHNAME  "/home/china/SC"      // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

int main(void) {

    // 创建一个有名管道
    int r = mkfifo(FIFO_PATHNAME, 0664);
	if (-1 == r) {
		if (errno != EEXIST) {
			perror("mkfifo error");
			return -1;
		}
	}

	// 1.申请一个system V IPC的key
	key_t key = ftok(SYSTEM_V_PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

    *p = 0;

	// 4. 操作(通过指针p操作共享内存区域)
    int i = 100000;
    int fifofd = open(FIFO_PATHNAME, O_RDONLY); // 只读打开
    while (i--) {
        (*p)++;
        printf("%d\n", *p);
    }

    // 关闭有名管道
    close(fifofd);

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "errno.h"

#define  FIFO_PATHNAME       "/home/china/SC/fifo" // 不能写成  "~/SC/fifo"
#define  SYSTEM_V_PATHNAME  "/home/china/SC"       // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

int main(void) {

    // 创建一个有名管道
    int r = mkfifo(FIFO_PATHNAME, 0664);
	if (-1 == r) {
		if (errno != EEXIST) {
			perror("mkfifo error");
			return -1;
		}
	}

	// 1.申请一个system V IPC的key
	key_t key = ftok(SYSTEM_V_PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
    int i = 100000;
    int fifofd = open(FIFO_PATHNAME, O_WRONLY); // 只写打开
    while (i--) {
        (*p)++;
        printf("%d\n", *p);
    }

    // 关闭有名管道
    close(fifofd);

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

        (4) p2进程发送一个信号给p1进程,从而达到两个进程同时++

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

void my_handler(int sig) {
	printf("sig = %d\n", sig);
	printf("haha,get a signal\n");
}

int main(void) {

	// 捕捉 SIGUSR1 信号
    signal(SIGUSR1, my_handler);

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt success\n");

	*p = 0;

	// 4. 操作(通过指针p操作共享内存区域)
	pause();
	int i = 100000;
	while (i--) {
		(*p)++;
		printf("*p = %d\n", *p);
	}

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

ps -ef    查看进程号

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

int main(void) {

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
	int i = 100000;
    kill(24387, SIGUSR1); // 通过 ps -ef 查看p1进程号
	while (i--) {
		(*p)++;
        printf("*p = %d\n", *p);
	}

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

如果同时访问,答案可能不是200000 ???
        why ???
        同时访问同一个共享资源,可能产生竞争

改进:

        (1) 等待(wait)子进程++完,父进程再++

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

int main(void) {

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
	*p = 0;
    int i = 100000;
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        goto ERR;
    } else if (pid > 0) {
        // 父进程
        wait(NULL); // 等待子进程退出
        while (i--) {
            (*p)++;
        }
        printf("%d\n", *p);
    } else if (pid == 0) {
        // 子进程
        while (i--) {
            (*p)++;
        }
    }

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

        (2) 有名管道

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "errno.h"

#define  FIFO_PATHNAME      "/home/china/SC/fifo" // 不能写成  "~/SC/fifo"
#define  SYSTEM_V_PATHNAME  "/home/china/SC"      // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

int main(void) {

    // 创建一个有名管道
    int r = mkfifo(FIFO_PATHNAME, 0664);
	if (-1 == r) {
		if (errno != EEXIST) {
			perror("mkfifo error");
			return -1;
		}
	}

	// 1.申请一个system V IPC的key
	key_t key = ftok(SYSTEM_V_PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
	*p = 0;
    int i = 100000;
    int fifofd;
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        goto ERR;
    } else if (pid > 0) {
        // 父进程
        while (i--) {
            (*p)++;
        }
        fifofd = open(FIFO_PATHNAME, O_WRONLY); // 只写打开
        wait(NULL); // 等待子进程的退出,防止子进程变成僵尸进程
        printf("%d\n", *p);
    } else if (pid == 0) {
        // 子进程
        fifofd = open(FIFO_PATHNAME, O_RDONLY); // 只读打开
        while (i--) {
            (*p)++;
        }
    }

    // 关闭有名管道
    close(fifofd);

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}

        (3) 发送一个信号

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>

#define  PATHNAME  "/home/china/SC" // 不能写成 "~/SC"
#define  PROJID  250
#define  SIZE    4096

void my_handler(int sig) {
	printf("sig = %d\n", sig);
	printf("haha,get a signal\n");
}

int main(void) {

    signal(SIGUSR1, my_handler);

	// 1.申请一个system V IPC的key
	key_t key = ftok(PATHNAME, PROJID);
	if (key == -1) {
		perror("ftok system V failed");
		return -1;
	}

	// 2.创建或者打开system V共享内存对象
	int shm_id = shmget(key, SIZE, IPC_CREAT | 0664);
	if (shm_id == -1) {
		perror("shmget system V failed");
        return -1;
	}

	// 3.映射
	int *p = (int*)shmat(shm_id, NULL, 0);
	if (p == NULL) {
		perror("shmat system V failed");
		goto ERR;
	}

	printf("shamt sucess\n");

	// 4. 操作(通过指针p操作共享内存区域)
	*p = 0;
    int i = 100000;
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        goto ERR;
    } else if (pid > 0) {
        // 父进程
        pause(); // 等待信号的到来
        while (i--) {
            (*p)++;
        }
        printf("%d\n", *p);
        wait(NULL); // 等待子进程的退出,防止子进程变成僵尸进程
    } else if (pid == 0) {
        // 子进程
        while (i--) {
            (*p)++;
        }
        kill(getppid(), SIGUSR1); // 发送一个信号给父进程
    }

ERR:
	// 5. 解映射
	shmdt(p);
	// 6. 删除共享内存
	shmctl(shm_id, IPC_RMID, NULL);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值