【Linux】信号量操作函数

文章目录

  • 二、 实验原理
    • 1、semget() 函数
      • 函数作用:
      • 参数意义:
      • 例子:
    • 2、semop()函数
      • 函数作用:
      • 参数意义:
      • struct sembuf 结构体定义如下
      • 例子:
      • 再来个完整点的例子1:
      • 再来个完整点的例子2:
    • 3、semctl()函数
      • 函数作用
        • 细致一些
      • 参数意义(4个):
      • 例子1:
        • 小问题:
      • 例子2:
        • 小问题:
  • 每天进步一点点 笔记仅供自学,用来回看复习,不一定适合你,如有错误请指出。

二、 实验原理

信号量操作函数

1、semget() 函数

#include 
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 
#include 
#include 
#include 
#include int main(int argc, char** argv) 
{// 创建一个信号量集,并设置信号量集中信号量的值为1key_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 
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 
#include 
#include 
int main() 
{// 创建一个信号量集,包含一个信号量,初值为 1int 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 
#include 
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 
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 
#include 
#include 
#include 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 操作选项,表示将信号量的值设置为指定的数值。这个函数的第三个参数就是要设置的信号量的值。


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


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部