【Linux】信号量操作函数

二、 实验原理

信号量操作函数

1、semget() 函数

#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

函数作用:

semget() 函数是 Linux 中的一个系统调用,用于创建或获取一个信号量集的标识符。信号量集是一个包含若干个信号量的数据结构,每个信号量都是一个整型值,用于在进程之间进行同步和互斥。

参数意义:

参数含义如下:

  • key:一个整型值,用于标识信号量集。如果 key 为 0,则系统会自动生成一个唯一的 key 值。
  • nsems:表示信号量集中包含的信号量个数。
  • semflg:表示创建信号量集的权限和行为。可以使用以下值:
    IPC_CREAT:如果信号量集不存在,则创建一个新的信号量集;如果信号量集已存在,则获取它的标识符。
    IPC_EXCL:与 IPC_CREAT 一起使用时,如果信号量集已存在,则调用 semget() 函数失败,并返回错误。
    返回值:
  • 成功:返回信号量集的标识符。
  • 失败:返回 -1,并设置 errno 为相应的错误码。

例子:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int main(int argc, char** argv) 
{
	// 创建一个信号量集,并设置信号量集中信号量的值为1
	key_t key = 12345;
	int sem_id = semget(key, 1, 0666 | IPC_CREAT); // IPC_CREAT表示如果信号量集不存在,就创建它
	if (sem_id == -1) 
	{
	perror("semget error");
	exit(1);
	}
union semun 
{
	int val;
	struct semid_ds *buf;
	ushort *array;
} argument;
	argument.val = 1;
if (semctl(sem_id, 0, SETVAL, argument) == -1) 
{
	perror("semctl error");
	exit(1);
}
	return 0;
}

1、上面的代码中,首先使用semget 函数来创建一个信号量集,参数 key 指定一个唯一的键值,参数 nsems 指定信号量集中信号量的数量,这里设置为1,即只创建一个信号量。参数 semflg 指定权限和创建信号量集的选项,0666 表示允许所有用户读写信号量集,IPC_CREAT 表示如果信号量集不存在,就创建它。
2、然后使用 semctl 函数来设置信号量集中信号量的值,参数 semid 指定信号量集的标识符,参数 semnum 指定信号量的编号,这里设置为0,即设置信号量集中唯一的信号量的值,参数 cmd 指定执行的操作,这里设置为 SETVAL,表示设置信号量的值,最后一个参数 argument 是一个联合类型,用于传递信号量的值。
3、如果成功创建并设置信号量集,则 semget 函数返回信号量集的标识符,semctl 函数返回0,否则返回-1。

2、semop()函数

#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

函数作用:

semop()函数是用于对信号量集进行操作的函数。它的作用是执行一个或多个信号量操作,如P操作或V操作。通常,semop()函数被用于控制对共享资源的访问,以保证在多个进程之间的资源共享是互斥的。
semop() 函数通过修改信号量集中每个信号量的值来实现对信号量的 P 操作和 V 操作。
semop() 函数的返回值为 0 表示执行成功,-1 表示执行失败。

参数意义:

  • semid 指信号量集的标识符;
  • sops 指向一个信号操作结构体的指针;
  • nsops 为要执行的信号操作的数量。

struct sembuf 结构体定义如下

struct sembuf 
{ 
 unsigned short int sem_num; /* 信号量的序号从 0~nsems-1 */ 
 short int sem_op; /* 对信号量的操作,>0, 0, <0 */ 
 short int sem_flg; /* 操作标识:0, IPC_WAIT, SEM_UNDO */ 
};

其中,sem_num 为信号量在信号量集中的编号
sem_op 为要执行的信号操作(P操作或V 操作)的操作数
sem_flg 为执行信号操作的标志(如是否阻塞等)。

例子:

举个例子,假设有一个信号量的值为 1,我们想要对这个信号量做 P 操作,也就是将其减 1,那么我们可以使用 semop() 函数来实现这个操作:

int sem_id; // 信号量的标识符
struct sembuf sops; // 信号量操作结构体
sops.sem_num = 0; // 信号量的编号(对应信号量集中的第几个信号量)
sops.sem_op = -1; // 要执行的操作(-1 表示 P 操作,1 表示 V 操作)
sops.sem_flg = 0; // 操作标志
semop(sem_id, &sops, 1); // 执行信号量操作

以上代码执行完之后,这个信号量的值就变成了 0。

1、上述代码中,semop 函数用来执行一个或多个信号量操作。它的第一个参数是信号量的标识符,第二个参数是一个指向结构体 sembuf 的指针,表示要执行的操作,第三个参数是要执行的操作的数量。
2、在上述代码中,sem_num 字段表示要操作的信号量在信号量集中的编号,sem_op 字段表示要执行的操作,如果为 -1,则执行 P 操作,如果为 1,则执行 V 操作。sem_flg 字段表示操作标志,通常设置为 0。

例如,如果要执行两个 P 操作,则可以这样调用 semop 函数:

struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = -1;
sops[1].sem_flg = 0;
semop(sem_id, sops, 2);

再来个完整点的例子1:

以下是一个使用 semop() 函数的简单例子:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main() 
{
	// 创建一个信号量集,包含一个信号量,初值为 1
	int sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
	if (sem_id < 0) {
	perror("semget error");
	return -1;
	}
	
	// 对信号量做 P 操作,即获取锁
	struct sembuf sops;
	sops.sem_num = 0;
	sops.sem_op = -1; // 操作数为 -1,即为 P 操作
	sops.sem_flg = 0;
	if (semop(sem_id, &sops, 1) < 0) {
	perror("semop error");
	return -1;
	}
	
	// 在获得锁的情况下进行关键操作
	printf("Critical section\n");
	
	// 对信号量做 V 操作,即释放锁
	sops.sem_op = 1; // 操作数为 1,即为 V 操作
	if (semop(sem_id, &sops, 1) < 0) {
	perror("semop error");
	return -1;
	}
	
	// 删除信号量集
	if (semctl(sem_id, 0, IPC_RMID) < 0) {
	perror("semctl error");
	return -1;
	}
	
	return 0;
}

1、这段代码实现了一个简单的互斥锁,通过使用信号量来保证关键操作的互斥执行。
2、首先,使用 semget() 函数创建一个信号量集,并设置信号量集中的信号量的初值为 1。
3、然后,使用 semop() 函数执行 P 操作,即获取锁。获取锁成功之后,执行关键操作。
4、最后,使用 semop() 函数执行 V 操作,即释放锁。
5、最后,使用 semctl() 函数删除信号量集。

这段代码的运行过程如下:

1、创建信号量集,并设置信号量的初值为 1。
2、执行 P 操作,即获取锁。
3、执行关键操作。
4、执行 V 操作,即释放锁。
5、删除信号量集。

再来个完整点的例子2:

#include <sys/sem.h>
#include <stdio.h>
int main()
{
	key_t key;
	int semid;
	struct sembuf sops;
	// 获取信号量集的标识符
	key = ftok("/tmp/sem.temp", 1);
	semid = semget(key, 1, 0666 | IPC_CREAT);

	// 初始化信号量
	semctl(semid, 0, SETVAL, 1);
	
	// 执行 P 操作
	sops.sem_num = 0;
	sops.sem_op = -1;
	sops.sem_flg = SEM_UNDO;
	semop(semid, &sops, 1);
	
	// 执行 V 操作
	sops.sem_num = 0;
	sops.sem_op = 1;
	sops.sem_flg = SEM_UNDO;
	semop(semid, &sops, 1);
	
	return 0;
}
  • 这是一个使用信号量实现互斥锁的例子。信号量集的标识符获取使用了 ftok 函数,信号量初始化使用了 semctl 函数,信号量 P 和 V 操作使用了 semop 函数。
  • ftok 函数用于获取文件的 IPC 键值,参数 pathname 指定文件路径,参数 proj_id 指定一个字节的整数值。ftok 函数返回的结果作为 semget 函数的第一个参数。
  • semctl 函数用于设置信号量的值,或者获取信号量的信息。参数 semid 是信号量集的标识符,参数 semnum 指定信号量集中信号量的编号,参数 cmd 指定操作的类型,参数 arg 是 cmd 的参数。semctl 函数的返回值是操作的结果。
  • semop 函数用于执行信号量操作。参数 semid 是信号量集的标识符,参数 sops 是一个指向 struct sembuf 结构体的指针,指定了要执行的信号量操作。参数 nsops 指定 sops 数组的长度。semop 函数的返回值是操作的结果。
  • 在本例中,使用了 SEM_UNDO 标志,表示若进程因意外终止,则系统会自动执行 V 操作来释放锁。这样就可以避免进程意外终止而导致的死锁问题。
  • 此外,还可以使用 semop() 函数的 sops 数组版本来执行多个信号量操作。例如,若要同时获取多个锁,可以将多个信号量的 P 操作放在 sops 数组中,并一次性执行,以达到节省时间的目的。

3、semctl()函数

#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ... /* arg */);

函数作用

semctl() 函数是 Linux 中用于控制信号量集的函数。它支持多种操作,可以用于获取信号量集的属性、设置信号量集的属性、控制信号量的值等。

细致一些

semctl() 函数是用来控制信号量的函数,它接受三个参数:信号量集的标识符、信号量的编号、控制命令。
根据不同的控制命令,可以进行不同的操作,比如设置信号量的值、获取信号量的值、删除信号量集等。

参数意义(4个):

  • semid:信号量集的标识符。
  • semnum:要操作的信号量的编号。是信号量在信号量集中的编号
  • cmd 是要执行的命令,后面的参数是根据 cmd 的值而定的。
    如果 cmd 为 SETVAL 或 SETALL,则此参数被忽略
    cmd:指定要执行的操作。(操作命令
    可以是以下值之一:
    GETVAL:获取信号量的值。
    SETVAL:设置信号量的值,取自 arg 的 val 元素。
    GETPID:返回最后一个执行 semop 函数的进程的进程号。
    GETNCNT:获取当前等待信号量为正值的进程数。
    GETZCNT:获取当前等待信号量为 0 的进程数。
    GETALL:获取信号量集中所有信号量的值。
    SETALL:设置信号量集中所有信号量的值。
    IPC_STAT:获取信号量集的属性。
    IPC_SET:设置信号量集的属性。
    IPC_RMID:删除信号量集。
    常用的有:
    semctl() 函数有多种用途,常用的命令有以下几种:
    GETVAL:获取信号量的值。
    SETVAL:设置信号量的值。
    IPC_RMID:删除信号量集。
    IPC_STAT:获取信号量集的属性。
    IPC_SET:设置信号量集的属性。
  • arg:可变参数,依赖于 cmd 的值而不同。如果 cmd 为 SETVAL 或 SETALL,则 arg 指向一个 int 类型的值,表示要设置的信号量的值。

例子1:

下面是一个使用 semctl() 函数获取信号量值的示例:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int main()
{
	key_t key = ftok("/tmp", 'a');
	int sem_id = semget(key, 1, 0666 | IPC_CREAT);
	int val = semctl(sem_id, 0, GETVAL);
	printf("Semaphore value: %d\n", val);
	return 0;
}
小问题:

其中 int sem_id = semget(key, 1, 0666 | IPC_CREAT); 参数为什么要在这样设置?

semget()函数用于创建或获取信号量集。
它的三个参数分别为:

  • key:信号量集的标识符。
  • nsems:信号量集中信号量的数量。
  • semflg:控制信号量集的创建或获取方式。
    1、在这里,第一个参数key是一个键值,用于指定信号量集的标识符。第二个参数nsems指定信号量集中信号量的数量。第三个参数semflg用于控制信号量集的创建或获取方式。
    2、0666表示信号量集的读写权限
    3、IPC_CREAT标志表示如果信号量集不存在,则创建它。
    综上,int sem_id = semget(key, 1, 0666 | IPC_CREAT);
    这行代码用于创建一个新的信号量集,其中包含一个信号量,并返回该信号量集的标识符。

例子2:

nt sem_id; // 信号量集的标识符
int sem_num = 3; // 信号量的编号
int sem_val = 5; // 信号量的值

union semun 
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
} sem_arg; // 定义信号量控制命令的参数

sem_arg.val = sem_val; // 设置信号量的值
semctl(sem_id, sem_num, SETVAL, sem_arg); // 设置信号量的值

这样就可以使用 semctl() 函数来设置信号量的值了。

小问题:

上面的代码中,设置信号量的值 那行代码 是什么作用?

在上面的代码中,设置信号量的值的那行代码是使用 semctl() 函数设置信号量的值。semctl() 函数是用来控制信号量集的函数,它提供了许多操作选项,如设置信号量的值,获取信号量的值,删除信号量等。在这个代码中,使用的是 SETVAL 操作选项,表示将信号量的值设置为指定的数值。这个函数的第三个参数就是要设置的信号量的值。


每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Linux提供了几个信号函数来实现进程间同步和互斥操作。这些函数包括: 1. `semget()`:创建一个新的信号或获取一个已有的信号的标识符。 2. `semop()`:对信号进行操作,如设置、释放、获取等。 3. `semctl()`:对信号进行控制,如获取、设置、删除等。 下面是这些函数的详细说明: 1. `semget()` ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); ``` 参数说明: - `key`:信号的标识符,可以使用 `ftok()` 函数生成。 - `nsems`:需要的信号。 - `semflg`:信号的创建标志,可以是 `IPC_CREAT`、`IPC_EXCL` 或者二者的或运算。 返回值:如果成功,则返回信号的标识符,否则返回 -1。 2. `semop()` ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, unsigned nsops); ``` 参数说明: - `semid`:信号的标识符。 - `sops`:操作信号的结构体指针。 - `nsops`:操作信号。 返回值:如果成功,则返回 0,否则返回 -1。 `semop()` 操作信号的结构体包括: ```c struct sembuf { short sem_num; // 信号的编号 short sem_op; // 信号操作 short sem_flg; // 操作标志 }; ``` 其中,`sem_num` 表示需要操作信号的编号,从 0 开始;`sem_op` 表示操作类型,一般为正数(V 操作)或负数(P 操作);`sem_flg` 表示操作标志,一般为 0 即可。 3. `semctl()` ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...); ``` 参数说明: - `semid`:信号的标识符。 - `semnum`:信号的编号,如果是对所有信号进行操作,则为 0。 - `cmd`:需要执行的控制命令,可以是 `IPC_STAT`、`IPC_SET`、`IPC_RMID` 等。 - 可变参数:根据不同的命令而定,如 `IPC_SET` 则需要传入 `struct semid_ds *buf`。 返回值:根据不同的命令而定,如 `IPC_STAT` 则返回 0,否则返回 -1。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KevinGuo457

哈哈哈资助我买两包辣条叭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值