文章目录
- 二、 实验原理
- 1、semget() 函数
-
- 2、semop()函数
- 函数作用:
- 参数意义:
- struct sembuf 结构体定义如下
- 例子:
- 再来个完整点的例子1:
- 再来个完整点的例子2:
- 3、semctl()函数
-
- 每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。
二、 实验原理
信号量操作函数
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)
{
key_t key = 12345;
int sem_id = semget(key, 1, 0666 | 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;
short int sem_op;
short int sem_flg;
};
其中,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;
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()
{
int sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
if (sem_id < 0) {
perror("semget error");
return -1;
}
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = 0;
if (semop(sem_id, &sops, 1) < 0) {
perror("semop error");
return -1;
}
printf("Critical section\n");
sops.sem_op = 1;
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);
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
semop(semid, &sops, 1);
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, ... );
函数作用
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 操作选项,表示将信号量的值设置为指定的数值。这个函数的第三个参数就是要设置的信号量的值。
每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)