进程的IPC *** 作 之 信号量

进程的IPC *** 作 之 信号量,第1张

目录

一 信号量的定义

二 信号量的 *** 作

2.1 信号量的创建

2.2 信号量的PV *** 作

2.3 信号量的设置

三.测试案例


一 信号量的定义

信号量的出现,主要是为了解决进程之间对于临界资源的独占式访问的问题。信号量可以满足对于临界资源的访问只能有一个进程。

二 信号量的 *** 作

LINUX的信号量API都定义在sys/sem.h头文件中:

2.1 信号量的创建
int semget(key_t key, int num_sems, int sem_flags);

函数功能:创建或者获取由键值key标识的信号量集合。

函数参数:

key:主要是用于在全局唯一的表示一个信号量集合,相当于全局的文件名一样。

num_sems:主要是用于设置所创建信号量集合中信号量的个数;用于获取的时候可以不用设置。

sem_flags:主要讲解几个参数:

IPC_CREAT:如果该key值表示的信号量集不存在,则创建;如果存在,则获取;IPC_EXCL:只能和其他参数联合使用,如果和IPC_CREAT联合使用,则当key值表示的信号量集合存在,则返回错误:EEXIST。

当创建成功时,会在内核中创建一个变量和该信号量关联:

struct semid_ds{
    struct ipc_perm sem_perm;
    unsigned long int sem_nsems;
    time_t sem_otime;
    time_t sem_ctime;
};

其中,ipclass="superseo">c_perm是LINUX内核用于表示所有IPC变量(信号量,共享内存以及消息队列)以及其相关权限的结构体:

struct ipc_perm{
    key_t key;//用于全局标识唯一的IPC变量
    uid_t uid;//用户id
    gid_t gid;//用户组id
    uid_t cuid;//创建者id
    gid_t cgid;//创建者所在组id
    mode_t mode;//访问权限
};
2.2 信号量的PV *** 作

首先来讲一下和每个信号量相关的内核变量:

unsigned short semval;//该信号量的值
unsigned short semncnt;//等待该信号量不为0的进程数
unsigned short semzcnt;//等待该信号量为0的进程数
pid_t sempid;//上一次修改该信号量值的进程PID

PV *** 作的函数如下所示:

int semop(int sem_id, struct sembuf* sem_ops, size_t num_sem_ops);

函数功能:主要是对该信号量进行PV *** 作。

函数参数:

sem_id:semget()函数返回的信号量集标识符。sem_ops:指向一个sembuf类型的结构体数组:
struct sembuf{
unsigned short int sem_num;//该信号量在信号量集中的位置
short int sem_op;//相应的对于该信号量的加减 *** 作的值
short int sem_flg;
};

    3.num_sem_ops:要执行 *** 作的个数。

具体 *** 作流程:

a. 当sem_op大于0时,将会对该信号量集中对应信号量的值加sem_op;

b. 当sem_op小于0时,将会对该信号量集中对应信号量的值减去sem_op;

当信号量的当前值semval小于等于sem_op,则 *** 作成功;

当信号量的当前值semval大于sem_op,则 *** 作失败:如果sem_flg的值设为IPC_NOWAIT,则会返回错误EAGAIN,不会阻塞等待;如果sem_flg的值未设为IPC_NOWAIT,则会阻塞等待,阻塞等待结束条件是:semval值增加到大于等于sem_op;该信号量集被进程回收,会返回错误EIDRM;该semop()函数被中断,返回错误EINTR。

c. 当sem_op等于0时,则会等待该信号的semval值为0:

当信号量的当前值semval等于0,则 *** 作成功;

当信号量的当前值不为0,则 *** 作失败:如果sem_flg设为IPC_NOWAIT,则会返回错误EAGAIN,不会阻塞等待;如果sem_flg的值未设为IPC_NOWAIT,则会阻塞等待,阻塞等待结束条件是:semval值变为0;信号量集被进程回收,返回错误EIDRM;semop() *** 作被中断,返回错误EINTR。

2.3 信号量的设置
int semctl(int sem_id, int sem_num, int command, union semun);

函数功能:用于给sem_id标识的信号量集进行设置。

函数参数:

a.用户自己定义的参数,一般格式为:

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

b.command:一个指令,常用指令如下:

IPC_STAT:用于将该sem_id标识的信号量集的所有信号量结构体semid_ds值存入buf中;

IPC_SET:用于设置sem_id标识信号量集的所有信号量结构体semid_ds值为buf;

GETVAL:用于将sem_id标识的信号量集中的第sem_num个信号量的值semval存入val中;

SETVAL:与GETVAL相反;

GETALL:用于将sem_id标识的信号量集中的所有信号量的值semval存入array中;

SETALL:与GETALL相反。

三.测试案例

下面我将要测试信号量对于代码执行过程的影响,代码如下所示:

#include
#include
#include
#include
#include

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

void pv(int sem_id, int sem_op)
{
	struct sembuf sem_ops;
	sem_ops.sem_num = 0;
	sem_ops.sem_op = sem_op;
	sem_ops.sem_flg = SEM_UNDO;
	semop(sem_id, &sem_ops, 1);
}

int main()
{
	printf("开始:\n");
	int sem_id = semget(IPC_PRIVATE, 1, 0666);//创建信号量集(其中只有一个信号量)
	union semun sem_un;
	sem_un.val = 1;//信号量初始值设为1,这里只测试二元信号量
	semctl(sem_id, 0, SETVAL, sem_un);
	pid_t pid = fork();
	if(pid < 0)
		return -1;
	else if(pid == 0)
	{
		printf("这是子进程.\n");
		pv(sem_id, -1);//进入临界区
		printf("子进程休息.\n");
		sleep(5);
		printf("子进程休息完毕.\n");
		pv(sem_id, 1);//退出临界区
		exit(0);//终止子进程
	}
	else
	{
		printf("这是父进程.\n");
		pv(sem_id, -1);//进入临界区
		printf("父进程休息.\n");
		sleep(5);
		printf("父进程休息完毕.\n");
		pv(sem_id, 1);//退出临界区
	}
	waitpid(pid, NULL, 0);//回收子进程
	semctl(sem_id, 0, IPC_RMID, sem_un);//删除信号量
	return 0;
}

1.当加入pv *** 作时,代码执行结果如下所示:

 2.当注释pv *** 作时,代码执行结果如下所示:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/926220.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存