Semaphores

Remember file locking? Well, semaphores can be thought of as really generic advisory locking mechanisms. You can use them to control access to files, shared memory, and, well, just about anything you want. The basic functionality of a semaphore is that you can either set it, check it, or wait until it clears then set it ("test-n-set"). No matter how complex the stuff that follows gets, remember those three operations.

This document will provide an overview of semaphore functionality, and will end with a program that uses semaphores to control access to a file. (This task, admittedly, could easily be handled with file locking, but it makes a good example since it's easier to wrap your head around than, say, shared memory.)

1. Grabbing some semaphores

With System V IPC, you don't grab single semaphores; you grab sets of semaphores. You can, of course, grab a semaphore set that only has one semaphore in it, but the point is you can have a whole slew of semaphores just by creating a single semaphore set.

How do you create the semaphore set? It's done with a call to semget(), which returns the semaphore id (hereafter referred to as the semid):

#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

What's the key? It's a unique identifier that is used by different processes to identify this semaphore set. (This key will be generated usingftok(), described in the Message Queues section.)

The next argument, nsems, is (you guessed it!) the number of semaphores in this semaphore set. The exact number is system dependent, but it's probably between 500 and 2000. If you're needing more (greedy wretch!), just get another semaphore set.

Finally, there's the semflg argument. This tells semget() what the permissions should be on the new semaphore set, whether you're creating a new set or just want to connect to an existing one, and other things that you can look up. For creating a new set, you can bit-wise or the access permissions with IPC_CREAT.

Here's an example call that generates the key with ftok() and creates a 10 semaphore set, with 666 (rw-rw-rw-) permissions:

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

key_t key;
int semid;

key = ftok("/home/beej/somefile", 'E');
semid = semget(key, 10, 0666 | IPC_CREAT);

Congrats! You've created a new semaphore set! After running the program you can check it out with the ipcs command. (Don't forget to remove it when you're done with it with ipcrm!)

Wait! Warning! ¡Advertencia! ¡No pongas las manos en la tolva! (That's the only Spanish I learned while working at Pizza Hut in 1990. It was printed on the dough roller.) Look here:

When you first create some semaphores, they're all uninitialized; it takes another call to mark them as free (namely to semop() or semctl()—see the following sections.) What does this mean? Well, it means that creation of a semaphore is not atomic (in other words, it's not a one-step process). If two processes are trying to create, initialize, and use a semaphore at the same time, a race condition might develop.

One way to get around this difficulty is by having a single init process that creates and initializes the semaphore long before the main processes begin to run. The main processes just access it, but never create nor destroy it.

Stevens refers to this problem as the semaphore's "fatal flaw". He solves it by creating the semaphore set with the IPC_EXCL flag. If process 1 creates it first, process 2 will return an error on the call (with errno set to EEXIST.) At that point, process 2 will have to wait until the semaphore is initialized by process 1. How can it tell? Turns out, it can repeatedly call semctl() with the IPC_STAT flag, and look at the sem_otime member of the returned  struct semid_ds  structure. If that's non-zero, it means process 1 has performed an operation on the semaphore with semop(), presumably to initialize it.

For an example of this, see the demonstration program semdemo.c, below, in which I generally reimplement Stevens' code.

In the meantime, let's hop to the next section and take a look at how to initialize our freshly-minted semaphores.

2. Controlling your semaphores with semctl()

Once you have created your semaphore sets, you have to initialize them to a positive value to show that the resource is available to use. The function semctl() allows you to do atomic value changes to individual semaphores or complete sets of semaphores.

int semctl(int semid, int semnum,
           int cmd, ... );

semid is the semaphore set id that you get from your call to semget(), earlier. semnum is the ID of the semaphore that you wish to manipulate the value of. cmd is what you wish to do with the semaphore in question. The last "argument", "arg", if required, needs to be a  union semun , which will be defined by you in your code to be one of these:

union semun {
    int val;               
    struct semid_ds *buf;  
    ushort *array;         
};

The various fields in the  union semun  are used depending on the value of the cmd parameter to setctl() (a partial list follows—see your local man page for more):

cmd Effect
SETVALSet the value of the specified semaphore to the value in the val member of the passed-in  union semun .
GETVALReturn the value of the given semaphore.
SETALLSet the values of all the semaphores in the set to the values in the array pointed to by the array member of the passed-in  union semun . The semnum parameter to semctl() isn't used.
GETALLGets the values of all the semaphores in the set and stores them in the array pointed to by the array member of the passed-in union semun . The semnum parameter to semctl() isn't used.
IPC_RMIDRemove the specified semaphore set from the system. The semnum parameter is ignored.
IPC_STATLoad status information about the semaphore set into the  struct semid_ds  structure pointed to by the buf member of the union semun .

For the curious, here are the contents of the  struct semid_ds  that is used in the  union semun :

struct semid_ds {
    struct ipc_perm sem_perm;  
    time_t          sem_ctime; 
    unsigned short  sem_nsems; 
};

We'll use that sem_otime member later on when we write our initsem() in the sample code, below.

3. semop(): Atomic power!

All operations that set, get, or test-n-set a semaphore use the semop() system call. This system call is general purpose, and its functionality is dictated by a structure that is passed to it,  struct sembuf :


struct sembuf {
    ushort sem_num;
    short sem_op;
    short sem_flg;
};

Of course, sem_num is the number of the semaphore in the set that you want to manipulate. Then, sem_op is what you want to do with that semaphore. This takes on different meanings, depending on whether sem_op is positive, negative, or zero, as shown in the following table:

sem_opWhat happens
NegativeAllocate resources. Block the calling process until the value of the semaphore is greater than or equal to the absolute value of sem_op. (That is, wait until enough resources have been freed by other processes for this one to allocate.) Then add (effectively subtract, since it's negative) the value of sem_op to the semaphore's value.
PositiveRelease resources. The value of sem_op is added to the semaphore's value.
ZeroThis process will wait until the semaphore in question reaches 0.

So, basically, what you do is load up a  struct sembuf  with whatever values you want, then call semop(), like this:

int semop(int semid, struct sembuf *sops,
          unsigned int nsops);

The semid argument is the number obtained from the call to semget(). Next is sops, which is a pointer to the  struct sembuf  that you filled with your semaphore commands. If you want, though, you can make an array of  struct sembuf s in order to do a whole bunch of semaphore operations at the same time. The way semop()knows that you're doing this is the nsop argument, which tells how many  struct sembuf s you're sending it. If you only have one, well, put 1 as this argument.

One field in the  struct sembuf  that I haven't mentioned is the sem_flg field which allows the program to specify flags the further modify the effects of the semop() call.

One of these flags is IPC_NOWAIT which, as the name suggests, causes the call to semop() to return with error EAGAIN if it encounters a situation where it would normally block. This is good for situations where you might want to "poll" to see if you can allocate a resource.

Another very useful flag is the SEM_UNDO flag. This causes semop() to record, in a way, the change made to the semaphore. When the program exits, the kernel will automatically undo all changes that were marked with the SEM_UNDO flag. Of course, your program should do its best to deallocate any resources it marks using the semaphore, but sometimes this isn't possible when your program gets a SIGKILL or some other awful crash happens.

4. Destroying a semaphore

There are two ways to get rid of a semaphore: one is to use the Unix command ipcrm. The other is through a call to semctl() with the cmd set to IPC_RMID.

Basically, you want to call semctl() and set semid to the semaphore ID you want to axe. The cmd should be set to IPC_RMID, which tells semctl() to remove this semaphore set. The parameter semnum has no meaning in the IPC_RMID context and can just be set to zero.

Here's an example call to torch a semaphore set:

int semid; 
.
.
semid = semget(...);
.
.
semctl(semid, 0, IPC_RMID);

Easy peasy.

5. Sample programs

There are two of them. The first, semdemo.c, creates the semaphore if necessary, and performs some pretend file locking on it in a demo very much like that in the File Locking document. The second program, semrm.c is used to destroy the semaphore (again, ipcrm could be used to accomplish this.)

The idea is to run run semdemo.c in a few windows and see how all the processes interact. When you're done, use semrm.c to remove the semaphore. You could also try removing the semaphore while running semdemo.c just to see what kinds of errors are generated.

Here's semdemo.c, including a function named initsem() that gets around the semaphore race conditions, Stevens-style:

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

#define MAX_RETRIES 10

union semun {
    int val;
    struct semid_ds *buf;
    ushort *array;
};


int initsem(key_t key, int nsems)  
{
    int i;
    union semun arg;
    struct semid_ds buf;
    struct sembuf sb;
    int semid;

    semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | 0666);

    if (semid >= 0) { 
        sb.sem_op = 1; sb.sem_flg = 0;
        arg.val = 1;

        printf("press return\n"); getchar();

        for(sb.sem_num = 0; sb.sem_num < nsems; sb.sem_num++) { 
            
            
            if (semop(semid, &sb, 1) == -1) {
                int e = errno;
                semctl(semid, 0, IPC_RMID); 
                errno = e;
                return -1; 
            }
        }

    } else if (errno == EEXIST) { 
        int ready = 0;

        semid = semget(key, nsems, 0); 
        if (semid < 0) return semid; 

        
        arg.buf = &buf;
        for(i = 0; i < MAX_RETRIES && !ready; i++) {
            semctl(semid, nsems-1, IPC_STAT, arg);
            if (arg.buf->sem_otime != 0) {
                ready = 1;
            } else {
                sleep(1);
            }
        }
        if (!ready) {
            errno = ETIME;
            return -1;
        }
    } else {
        return semid; 
    }

    return semid;
}

int main(void)
{
    key_t key;
    int semid;
    struct sembuf sb;
    
    sb.sem_num = 0;
    sb.sem_op = -1;  
    sb.sem_flg = SEM_UNDO;

    if ((key = ftok("semdemo.c", 'J')) == -1) {
        perror("ftok");
        exit(1);
    }

    
    if ((semid = initsem(key, 1)) == -1) {
        perror("initsem");
        exit(1);
    }

    printf("Press return to lock: ");
    getchar();
    printf("Trying to lock...\n");

    if (semop(semid, &sb, 1) == -1) {
        perror("semop");
        exit(1);
    }

    printf("Locked.\n");
    printf("Press return to unlock: ");
    getchar();

    sb.sem_op = 1; 
    if (semop(semid, &sb, 1) == -1) {
        perror("semop");
        exit(1);
    }

    printf("Unlocked\n");

    return 0;
}

Here's semrm.c for removing the semaphore when you're done:

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

int main(void)
{
    key_t key;
    int semid;
    union semun arg;

    if ((key = ftok("semdemo.c", 'J')) == -1) {
        perror("ftok");
        exit(1);
    }

    
    if ((semid = semget(key, 1, 0)) == -1) {
        perror("semget");
        exit(1);
    }

    
    if (semctl(semid, 0, IPC_RMID, arg) == -1) {
        perror("semctl");
        exit(1);
    }

    return 0;
}

Isn't that fun! I'm sure you'll give up Quake just to play with this semaphore stuff all day long!

6. Summary

I might have understated the usefulness of semaphores. I assure you, they're very very very useful in a concurrency situation. They're often faster than regular file locks, too. Also, you can use them on other things that aren't files, such as Shared Memory Segments! In fact, it is sometimes hard to live without them, quite frankly.

Whenever you have multiple processes running through a critical section of code, man, you need semaphores. You have zillions of them—you might as well use 'em

CH341A编程器是一款广泛应用的通用编程设备,尤其在电子工程和嵌入式系统开发领域中,它被用来烧录各种类型的微控制器、存储器和其他IC芯片。这款编程器的最新版本为1.3,它的一个显著特点是增加了对25Q256等32M芯片的支持。 25Q256是一种串行EEPROM(电可擦可编程只读存储器)芯片,通常用于存储程序代码、配置数据或其他非易失性信息。32M在这里指的是存储容量,即该芯片可以存储32兆位(Mbit)的数据,换算成字节数就是4MB。这种大容量的存储器在许多嵌入式系统中都有应用,例如汽车电子、工业控制、消费电子设备等。 CH341A编程器的1.3版更新,意味着它可以与更多的芯片型号兼容,特别是针对32M容量的芯片进行了优化,提高了编程效率和稳定性。26系列芯片通常指的是Microchip公司的25系列SPI(串行外围接口)EEPROM产品线,这些芯片广泛应用于各种需要小体积、低功耗和非易失性存储的应用场景。 全功能版的CH341A编程器不仅支持25Q256,还支持其他大容量芯片,这意味着它具有广泛的兼容性,能够满足不同项目的需求。这包括但不限于微控制器、EPROM、EEPROM、闪存、逻辑门电路等多种类型芯片的编程。 使用CH341A编程器进行编程操作时,首先需要将设备通过USB连接到计算机,然后安装相应的驱动程序和编程软件。在本例中,压缩包中的"CH341A_1.30"很可能是编程软件的安装程序。安装后,用户可以通过软件界面选择需要编程的芯片类型,加载待烧录的固件或数据,然后执行编程操作。编程过程中需要注意的是,确保正确设置芯片的电压、时钟频率等参数,以防止损坏芯片。 CH341A编程器1.3版是面向电子爱好者和专业工程师的一款实用工具,其强大的兼容性和易用性使其在众多编程器中脱颖而出。对于需要处理25Q256等32M芯片的项目,或者26系列芯片的编程工作,CH341A编程器是理想的选择。通过持续的软件更新和升级,它保持了与现代电子技术同步,确保用户能方便地对各种芯片进行编程和调试。
内存分区情况的分析是嵌入式系统开发中的一个重要环节,特别是在资源有限的MCU(微控制器)环境中。标题提到的工具是一款专为分析Linux环境下的`gcc-map`文件设计的工具,这类文件在编译过程结束后生成,包含了程序在目标设备内存中的布局信息。这个工具可以帮助开发者理解程序在RAM、ROM以及FLASH等存储区域的占用情况,从而进行优化。 `gcc-map`文件通常包含以下关键信息: 1. **符号表**:列出所有定义的全局和静态变量、函数以及其他符号,包括它们的地址和大小。 2. **节区分配**:显示每个代码和数据节区在内存中的位置,比如.text(代码)、.data(已初始化数据)、.bss(未初始化数据)等。 3. **内存汇总**:总览所有节区的大小,有助于评估程序的整体内存需求。 4. **重定位信息**:显示了代码和数据如何在目标地址空间中定位。 该分析工具可能提供以下功能: 1. **可视化展示**:将内存分配以图形化方式呈现,便于直观理解。 2. **详细报告**:生成详细的分析报告,列出每个符号的大小和位置。 3. **比较功能**:对比不同编译版本或配置的`map`文件,查看内存使用的变化。 4. **统计分析**:计算各种内存区域的使用率,帮助识别潜在的优化点。 5. **自定义过滤**:允许用户根据需要筛选和关注特定的符号或节区。 虽然在MCU环境中,Keil IDE自带的工具可能更方便,因为它们通常针对特定的MCU型号进行了优化,提供更加细致的硬件相关分析。然而,对于通用的Linux系统或跨平台项目,这款基于`gcc-map`的分析工具提供了更广泛的适用性。 在实际使用过程中,开发者可以利用这款工具来: - **优化内存使用**:通过分析哪些函数或数据占用过多的内存,进行代码重构或调整链接器脚本以减小体积。 - **排查内存泄漏**:结合其他工具,比如动态内存检测工具,查找可能导致内存泄漏的部分。 - **性能调优**:了解代码执行时的内存分布,有助于提高运行效率。 - **满足资源限制**:在嵌入式系统中,确保程序能在有限的内存空间内运行。 总结来说,`gcc-amap`这样的工具对于深入理解程序的内存布局和资源消耗至关重要,它能帮助开发者做出更明智的决策,优化代码以适应不同的硬件环境。在处理`map`文件时,开发者不仅能获取到程序的内存占用情况,还能进一步挖掘出可能的优化空间,从而提升系统的整体性能和效率。
本次基于签名识别的合同管理系统的设计与实现其主要的使用角色是两个,一个是系统的管理员,一个签名识别使用的用户前台,用户主要是进行和同上传让母后进行签名的识别操作,管理员则是主要对系统用户的管理以及合同管理签名的实现等等: (1)登陆注册功能:用户在想要使用这个系统时,应该有一个注册的界面,让用户可以注册,然后还需要一个登陆的页面模块,让用户登陆后可以进行合同的上传和签名识别的操作。 (2)用户资料修改:用户在进入系统后应该可以对自己的资料进行一定的更正,因为注册时可能会填写了错误的信息,此时就需要登陆后进行二次的修改信息。 (3)签名对比功能:用户在进入系统后应该可以对自己上传的一些合同的签名信息进行对比,通过对神经网络的识别以及签名的信息识别,从而可以去识别这些合同上面的签名信息是不是正确的,更好的进行管理工作。 (4)用户管理功能:系统用户在通过登录页面登陆进入系统之后,应该可以对系统中的用户进行一个管理,这其中便包括了对用户的账号信息管理,可以添加和修改账号信息。 (5)合同管理功能:系统用户在通过登录页面登陆进入系统之后,可以对新疆阿克苏温宿县克孜勒政府土地合同管理,这个功能是主要就是对这个地区的合同进行管理操作,可以查看合同的一些信息以及合同的内容等等操作。 (6)合同和签名对比功能,系统用户在通过登录页面登陆进入系统之后,可以对新疆阿克苏温宿县克孜勒政府土地合同管理并且进行签名的识别,可以对自己的签名情况对比进行查看,也具有一个统计的功能,可以让用户查看识别的结果和情况。 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值