当前位置: 首页 > 科技观察

ParallelShell脚本验证Linux的互斥信号量

时间:2023-03-14 15:31:18 科技观察

1Linux下互斥信号量的使用1)Linux下互斥信号量的作用互斥信号量主要用于保证访问共享资源时操作的原子性,即也就是整个动作是不允许被打断的。2)如何学习LinuxMan命令下的文件操作函数学习函数的使用方法,写一小段代码,使用函数。下面将带大家学习互斥信号量相关的函数,然后用代码把这些函数串起来,用并行脚本来验证。2Linux下互斥信号量相关函数1)ftok函数ftok函数用于构造键值。①函数原型。key_tftok(c??har*fname,intid)②头文件。include包括③参数。fname:内核中文件名的数字表示。id:项目编号。键值由fname和项目id号组合生成。④返回值。成功:返回结果键值。失败:-1。2)semget函数semget函数用于创建一个开放的信号量。①函数原型。intsemget(key_tkey,intnsems,intsemflg)获取信号量集的标识符。当key指定的信号量不存在,而semflg中包含IPC_CREAT时,此时会创建一个信号量集。②头文件。includeincludeinclude③参数。键:键值。semflay:flag,可以去IPC_CREAT,如果key值对应的信号量不存在,也可以创建信号量。nsems:创建的信号量集中包含的信号量个数。④返回值。成功:返回信号量集的标识。失败:-1。3)semctl函数semctl函数对一个信号量集或一个集合中的单个信号量进行各种控制操作。①函数原型。intsemctl(intsemid,intsemnum,intcmd,.../*unionsemunarg*/)②头文件。include包括③参数。semid:要控制的信号量集的标识符。semnum:用于标识集合中的特定信号量。cmd:指定要执行的操作。信号量参数枚举如下:signal数量集结构如下:structsemid_ds{structipc_permsem_perm;//permissiontime_tsem_otime;//上次semop时间time_tsem_ctime;//最后修改时间unsignedlongsem_nsems;//信号量集中信号量个数};参数说明如下。<1正常控制操作。添加以下参数将忽略semnum参数。IPC_RMID:立即删除一个信号量集及其关联的semid_ds数据结构。IPC_STAT:将与此信号量集关联的semid_ds数据结构的副本放入arg.buf指向的缓冲区中。IPCSET:使用arg.buf指向的缓冲区中的值来更新与该信号量集关联的semid_ds数据结构中的选定字段。<2获取并初始化信号量值。以下操作可以获取或初始化集合中单个或所有信号量的值。获取信号量的值需要信号量的读权限,而初始化值需要写权限。GETVAL:semctl返回semid指定的信号量集中第semmum个信号量的值。此操作不带arg参数。SETVAL:用semid指定的信号量集中第semnum个信号量的值初始化arg.val。GETALL:获取semid指向的信号量集中所有信号量的值,放入arg.array指向的数组中。SETALL:用arg.array指向的数组中的值初始化semid指向的集合中的所有信号量。此操作忽略semnum参数。请注意,GETVAL和GETALL返回的信息在调用进程使用它们时可能已经过时。<3获取有关单个信号量的信息。以下操作返回有关semid引用的集合中第semnum个信号量的信息。所有这些操作都需要对信号量集的读取权限,并且不需要arg参数。GETPID:返回在信号量上执行semopO的最后一个进程的进程ID。该值称为sempid值。如果没有进程对信号量执行过semopO,则返回0。GETNCNT:返回当前等待信号量值增加的进程数。该值称为semncnt值。GETZCNT:返回当前等待这个信号量的值变为0的进程数;该值称为semzcnt值。与上述GETVAL和GETALL操作一样,GETPID、GETNCNT和GETZCNT操作返回的信息在调用进程使用它们时可能已经过时。④返回值。成功:semctl返回的值取决于cmd,如下。GETVAL:semval的值。GETPID:sempid的值。GETNCNT:semncnt的值。GETZCNT:semzcnt的值。其他参数:返回0,否则semctl返回-1,并设置errno表示错误。4)semop函数semop函数用于操作信号量集中的信号量。①函数原型。intsemop(intsemid,structsembuf*sops,unsignednsops)②头文件。includeincludeinclude③参数。semid:要操作的信号量集的标识。nsops:要操作多少个信号量。sops:对信号量进行什么样的操作,进行什么操作由structsembuf结构体中的数量决定。structsembuf{unsignedshortsem_num;//信号量个数shortsem_op;//要执行的操作shortsemf1g;//操作标志(IPC_NOMAIT和SEM_UNDO)}当sem_op>0时,将信号量的值加到sem_op的值上。结果是其他等待减少信号量值的进程可能会醒来并执行它们的操作。(需要写权限)当sem_op<0时,从信号量的值中减去sem_op的值。如果信号量的当前值大于或等于sem_op的绝对值,则操作立即结束。否则,semop将阻塞,直到信号量值增长到在执行操作后不会产生负值的程度。(需要写权限)当sem_op=0时,检查信号量值,看当前是否等于0。如果等于0,则操作立即结束,否则semop会阻塞,直到信号量值变为0。(需要阅读权限)④返回值。成功:0。失败:-1.3示例代码下面用一个小程序来使用上面介绍的功能。1)程序原理首先,程序通过并行脚本同时运行,在不加互斥信号量的情况下,中断(插入)不应该分开的程序。然后,加上一个互斥信号量,并行程序的每一个程序此时都不会被另一个程序中断(插入)。2)下面的一些头文件在不加信号量时是不需要的,加信号量时全部需要。为了省事,我就不去了。①unsem1.c。#include#include#include#include#includevoidmain(){printf("\nThisisunsem1start!\n");sleep(1);//打印一条消息后,会有printf("\nThisisunsem1end!\n");}②unsem2.c。#include#include#include#include#includevoidmain(){printf("\nThisisunsem2!\n");}③3个脚本文件。###脚本run.sh#!/bin/bash./run1.sh&./run2.sh###脚本文件——run1.sh#!/bin/bash./unsem1###脚本文件——run2.sh#!/bin/bash./unsem2是脚本run.sh运行run1.sh和run2.sh,并且可以并行运行程序。脚本run1.sh运行由unsem1.c编译和处理的??unsem1。脚本run2.sh运行由unsem2.c编译和处理的??unsem2。运行结果如下:因为是并行运行,所以两个程序不一定先运行。当unsem2先运行时,不影响unsem1,但当unsem1先运行时,会在unsem1的两次打印之间插入unsem2的打印。程序中使用sleep来给插入的机会。3)在添加信号量的情况下,下面的文件和上面的文件放在不同的文件夹下,所以相同的脚本名不影响。①sem1.c。#include#include#include#include#include#include#include#include#defineKEY1234unionsemun{intval;//信号量的值structsemid_ds*buf;unsignedshort*arrry;};voidmain(){key_tkey;intsemid;structsembufsop;intret;//创建键值//key=ftok("./",1);//当前目录可以创建多个键值,不使用该方法//创建信号量semid=semget((key_t)KEY,1,0666|IPC_CREAT);//使用key值创建一个信号量unionsemunsem_union;//定义分配给信号量的结构体并赋值sem_union.val=1;ret=semctl(semid,0,SETVAL,sem_union);//信号量的值设置为1//ret=semctl(semid,0,GETVAL);//获取信号量的值,如果要感觉semctl,可以不用这两条注释//printf("retvalueis%d\n",ret);//1获取信号量sop.sem_num=0;//操作第一个信号量,编号为0sop.sem_op=-1;//-1是获取信号量semop(semid,&sop,1);//因为定义了变量,参数是指针,所以取它的地址//2打印开始信息printf("\nThisissem1start!\n");//3sleep(1);//4打印结束消息printf("\nThisissem1end!\n");//5释放信号量sop.sem_num=0;//运行第一个信号量,数量为0sop.sem_op=1;//加1释放信号量semop(semid,&sop,1);//由于定义了变量,参数是指针,所以取其地址}②sem2.c#include#include#include#include#include#include#include#include#defineKEY1234voidmain(){key_tkey;intsemid;structsembufsop;intret;//打开与sem1相同的信号量semid=semget((key_t)KEY,1,0666|IPC_CREAT);//如果已经有这个信号量,则不创建,直接打开ret=semctl(semid,0,GETVAL);//获取信号量的值//printf("retvalueis%d\n",ret);//获取信号量sop.sem_num=0;//操作第一个信号量,编号为0sop.sem_op=-1;//-1是获取信号量semop(semid,&sop,1);//因为定义是变量,参数是指针,所以取其地址//打印消息的sem2printf("\nThisissem2!\n");//释放信号量sop.sem_num=0;//操作第一个信号量,编号为0sop.sem_op=1;//加1释放信号量semop(semid,&sop,1);//因为定义的是变量,参数是指针,所以取其地址}③3个脚本文件。###脚本run.sh#!/bin/bash./run1.sh&./run2.sh###脚本文件——run1.sh#!/bin/bash./sem1###脚本文件——run2.sh#!/bin/bash./sem2结果如下:可见无论sem1先运行还是sem2先运行,sem1的两次打印都不会中断。温馨提示:之前学过文件的操作,这里使用终端打印作为共享资源。也可以通过操作同一个文件来验证信号量的互斥性。去试试吧。本文转载自微信公众号“嵌杂军”,可通过以下二维码关注。转载本文请联系嵌入式杂军公众号。