linux进程间通信(六)----IPC篇----信号灯概念以及实现进程间通信

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
linux进程间通信(六)----IPC篇----信号灯概念以及实现进程间通信_第1张图片

一 why

linux进程间IPC通信方式有三种,分别是共享内存、消息队列,以及信号灯。它
们都存在于内核空间,之前我们已经探讨过共享内存以及消息队列的实现方式:
linux进程间通信(五)----IPC篇----共享内存实现进程间通信
linux进程间通信(六)----消息队列篇
今天这篇文章来谈谈信号灯基本概念,以及实现进程间通信的步骤。

二 what

(1)概念
第一个问题:信号灯和POSX规范中的信号量之间的区别是什么呢?
posix信号量针对于单个信号量 seminit semwait sempost,Posix有名信号量,无名信号量。什么意思呢?就是POSIX规范中的信号量只能作用于一个信号量(当然这个信号量对应的资源可能不止一个),每个信号对应一个P操作和V操作;而System V信号灯是一个集合,它是信号量的集合,它含有多个信号量,它可以对多个信号灯同时进行P/V操作。
第二个问题:为什么需要信号灯,只有POSIX信号量不是足够了吗?
只有POSIX信号量是不够的,考虑这样一个场景,

  1. 线程A中和线程B都需要访问共享资源1和共享资源2,在线程A中会需要先申请共享资源1,然后再申请共享资源2;
  2. 但是在线程B中,会先申请贡献资源2,然后再申请共享资源1。
  3. 当线程A中开始申请共享资源1时,紧接着会申请共享资源2;而此时线程B中开始申请共享资源2时,紧接着会申请共享资源1;
  4. 线程B正在占用着共享资源2,线程A正在占着共享资源1,导致线程B申请不到共享资源1,它就不会释放共享资源2;线程A申请不到共享资源2,它就不会释放共享资源1;这样就造成了死锁。当然这种情况可以这样避免,每次申请完一个共享资源时,申请结束就释放,而不能同时申请多个共享资源,多个申请成功之后,在释放。当然也可以使用今天的信号灯来解决这个问题
    (2)创建或者打开函数
#includde 
#includde 
#includde 
1. 创建或者打开函数
    原型: int semget(key_t key, int nsems, int semflag)
    参数: key 和信号灯集关联的key值
           nsems 信号灯集包含的信号灯数目
           semflag 信号灯集的访问权限
    返回值: 成功,信号灯ID,出错 -1

(3)关闭

原型: int semctl(int semid, int semnum, int cmd, ...union semun arg)
          注意最后一个参数不是地址,可以有,可以没有
    参数: semid 信号灯集id
           semnum 要修改的信号灯集编号,删除操作时,这个值可以设置为任意值
           cmd GETVAL 获取信号灯的值
               SETVAL 设置信号灯的值
               IPC_RMID 删除信号灯
           union semun arg: 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) */
                            };
    返回值: 成功,消息队列ID,出错 -1

(4)P/V操作函数

P/V操作函数 semop
    原型: int semop(int semid, struct sembuf *opsptr, size_t nops)
    参数: semid 信号灯集id
           opsptr struct sembuf{
     
                      short sem_num;   //要操作信号灯的编号
                      short sem_op;    //0: 等待,直到信号灯的值变为0,1:资源释放,V操作,-1:分配资源,P操作
                      short sem_flg;   //0: IPC_NOWAIT, SEM_UNDO
                  }
           nops 要操作信号灯个数
    返回值: 成功,消息队列ID,出错 -1

三 how

实现下面一个应用程序
父子进程通过system V信号灯同步对共享内存的读写

父进程从键盘输入字符串到共享内存
子进程删除字符串中的空格并打印
父进程输入quit后删除共享内存和信号灯集,程序结束

源代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define N 64
#define READ 0
#define WRITE 1

union semun {
     
	int val;
        struct semid_ds *buf;
        unsigned short *array;
        struct seminfo *__buf;
};

void init_sem(int semid, int s[], int n)
{
     
	int i;
	union semun myun;

	for (i = 0; i < n; i++){
     
		myun.val = s[i];
		semctl(semid, i, SETVAL, myun);
	}
}

void pv(int semid, int num, int op)
{
     
	struct sembuf buf;

	buf.sem_num = num;
	buf.sem_op = op;
	buf.sem_flg = 0;
	semop(semid, &buf, 1);
}

int main()
{
     
	int shmid, semid, s[] = {
     0, 1};
	pid_t pid;
	key_t key;
	char *shmaddr;

	key = ftok(".", 's');
	if (key == -1){
     
		perror("ftok");
		exit(-1);
	}

	shmid = shmget(key, N, IPC_CREAT|0666);
	if (shmid < 0) {
     
		perror("shmid");
		exit(-1);
	}

	semid = semget(key, 2, IPC_CREAT|0666);
	if (semid < 0) {
     
		perror("semget");
		goto __ERROR1;
	}
	init_sem(semid, s, 2);

	shmaddr = shmat(shmid, NULL, 0);
	if (shmaddr == NULL) {
     
		perror("shmaddr");
		goto __ERROR2;
	}

	pid = fork();
	if(pid < 0) {
     
		perror("fork");
		goto __ERROR2;
	} else if (pid == 0) {
     
		char *p, *q;
		while(1) {
     
			pv(semid, READ, -1);
			p = q = shmaddr;
			while (*q) {
     
				if (*q != ' ') {
     
					*p++ = *q;
				}
				q++;
			}
			*p = '\0';
			printf("%s", shmaddr);
			pv(semid, WRITE, 1);
		}
	} else {
     
		while (1) {
     
			pv(semid, WRITE, -1);
			printf("input > ");
			fgets(shmaddr, N, stdin);
			if (strcmp(shmaddr, "quit\n") == 0) break;
			pv(semid, READ, 1);
		}
		kill(pid, SIGUSR1);
	}

__ERROR2:
	semctl(semid, 0, IPC_RMID);
__ERROR1:
	shmctl(shmid, IPC_RMID, NULL);
	return 0;
}

你可能感兴趣的:(linux系统,linux,system,V信号灯集,进程间通信)