linux进程间通信--信号量(POSIX 版本)

linux进程间通信–信号量(POSIX 版本)

System V信号量是通过标识符而不是大多数UNIX I/O 和IPC所采用的文件描述符来引用的。由于这些潜在的缺点,因此POSIX标准又重新定义了信号量API。
POSIX标准信号量semaphore不仅可用于同一进程下的各个线程同步,也可以用于不同进程间同步。主要用于协调进程线程对共享资源的访问。

libc库实现了POSIX和System V两种接口标准的信号量API。这里主要讨论POSIX接口的信号量API.

信号量的原理与使用:信号量只能进行两种操作,如下:

  1. P(sv):如果sv的值大于零,它就减一。如果为零,就挂起该进程的执行.P(sv)的函数如sem_wait()。
  2. V(sv):如果有进程因等待sv而挂起,就让他恢复运行,如果没有,就加1.V(sv)操作的函数如sem_post().

POSIX接口标准中的信号量可分为命名信号量未命名信号量:

  1. 未命名信号量:即信号量没有名称。这种信号量由sem_init()创建。由sem_destroy()销毁。在多个线程中用起来很方便。
  2. 命名信号量:即信号量有名称。这种信号量由sem_open()创建。可以通过名字访问,因此可以被任何已知它们名字的进程使用。命名信号量创建后可以在文件系统/dev/shm下查看到。

与信号量有关的宏:

  1. SEM_NSEMS_MAX:是一个进程能够拥有的POSIX 信号量的最大数目.标准要求至少支持256个。
  2. SEM_VALUE_MAX:一个POSIX 信号量值能够取的最大值.标准要求至少支持32767个。

信号量相关的函数接口如下:

  1. int sem_init(sem_t *sem, int pshared, unsigned value)

    1. 初始化一个未命名的信号量sem.
    2. sem:要被初始化的信号量
    3. pshared:
      1. if != 0:信号量在进程之间共享。这时要求sem实体内存必须各个进程都能访问到。即位于共享内存区。mmap()映射。
      2. if == 0:信号量在本进程下的多个线程之间使用。
    4. value:信号量的初始值。
    5. return: if true:0,if false:-1.
  2. int sem_destroy(sem_t *sem)

    1. 销毁未命名的信号量sem.
    2. sem:要销毁的信号量
    3. return: if true:0,if false:-1.
  3. sem_t *sem_open(const char *name, int oflag, ...)

    1. 将命名信号量和进程之间建立连接。
    2. name:信号量名
    3. oflag:
      1. oflag = 0:如果信号量存在,则连接到该信号量.
      2. oflag = O_CREAT:如果信号量存在,则连接到该信号量,否则创建该信号量。
      3. oflag = O_CREAT | O_EXCL:只有当信号不存在时创建,否则报错。
    4. 可选参数:当oflag & O_CREAT == true时使用。
      1. mode:表示谁可以访问信号量。即访问权限。
      2. value:初始化的信号量值。最大为SEM_VALUE_MAX。
    5. return:if true:信号量指针。if false:SEM_FAILED.
  4. int sem_close(sem_t *sem)

    1. 关闭命名信号量(由sem_open()创建)。不要用于未命名的信号量(sem_init()创建)。如果没有通过调用sem_unlink()来删除该信号量,那么sem_close()对已命名的信号量没有影响。但是,当已命名信号量被完全解除链接时,该信号量将在最后一个任务关闭时消失。
    2. sem:信号量指针
    3. return: if true:0,if false:-1.
  5. int sem_unlink(const char *name)

    1. 删除由输入名称参数命名的信号量。如果在调用sem_unlink()时,有一个或多个进程打开了命名的信号量,那么该信号量的销毁将被推迟,直到通过调用sem_close()销毁所有引用为止。
    2. name:信号量名称。
    3. return: if true:0,if false:-1.
  6. int sem_wait(sem_t *sem)

    1. 等待获取信号量。一般用于请求访问共享资源。
    2. return:
      1. if true:0,
      2. if false:-1.
        1. EINVAL:输入参数无效。
        2. EINTR:信号中断了信号量的等待而非获取到信号量。
  7. int sem_timedwait(sem_t *sem, conststructtimespec *abstime)

    1. 功能类似sem_wait(),带有超时时间。
    2. abstime:超时等待时间。
    3. return: if true:0,
      1. if false:-1.
        1. EINVAL:输入参数无效。
        2. ETIMEDOUT:超时
        3. EDEADLK:检测到死锁情况。
        4. EINTR:信号中断了信号量的等待而非获取到信号量。
  8. int sem_trywait(sem_t *sem)

    1. 尝试去获取信号量。如果未获取到不阻塞。
    2. return: if true:0,
      1. if false:-1.
        1. EINVAL:输入参数无效。
        2. EAGAIN:表示没有获得信号量。
  9. int sem_post(sem_t *sem)

    1. 信号量释放。一般用于当共享资源使用完成后。
    2. return: if true:0,if false:-1.
  10. int sem_getvalue(sem_t *sem, int *sval)

    1. 获取信号量的值。如果sem被锁定,sem_getvalue()返回的值将为零或负数,其绝对值表示等待该信号量的进程数量
    2. return: if true:0,if false:-1.

上面的函数中sem_init(),sem_destroy()用于未命名信号量的创建与销毁。sem_open(),sem_close(),sem_unlink()用于命名信号量的创建与删除。其他函数用于操作信号量(共用)。

sem_close()函数与sem_unlink()函数的区别:sem_close()函数是关闭一个打开的信号量。关闭后的命名信号量仍然存在。别的进程依然可以访问这个信号量。而sem_unlink()是删除命名信号量,删除后的信号量就不存在了。

使用了POSIX信号量函数,链接时要用到-lpthread库.

使用例程如下:

#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <errno.h>

void process_read(void)
{
    sem_t *sem_handled = sem_open("/sem",O_CREAT,0666,1);
    if(sem_handled == SEM_FAILED){
        printf("(%d)sem_open error\n",getpid());
        exit(0);
    }

    for(int i = 0; i < 10; i++){
        while(1){
            int res = sem_wait(sem_handled);
            if(res == 0){
                break;
            }
            if(res == EINTR){
                continue;
            }
            else {
                printf("(%d)sem_wait error\n",getpid());
                exit(0);
            }
        }
        
        printf("(%d)正在持有资源=================================\n",getpid());

        int res = sem_post(sem_handled);
        if(res == -1){
            printf("(%d)sem_post error\n",getpid());
        }
        usleep(2000);
    }

}

void process_write(void)
{
    sem_t *sem_handled = sem_open("/sem",O_CREAT,0666,1);
    if(sem_handled == SEM_FAILED){
        printf("(%d)sem_open error\n",getpid());
        exit(0);
    }

    for(int i = 0; i < 10; i++){
        while(1){
            int res = sem_wait(sem_handled);
            if(res == 0){
                break;
            }
            if(res == EINTR){
                continue;
            }
            else {
                printf("(%d)sem_wait error\n",getpid());
                exit(0);
            }
        }
        
        printf("(%d)正在持有资源..........................\n",getpid());

        int res = sem_post(sem_handled);
        if(res == -1){
            printf("(%d)sem_post error\n",getpid());
        }

        usleep(1500);
    }

    int res = sem_unlink("/sem");
    if(res == -1){
        printf("(%d)sem_unlink error\n",getpid());
    }

}

void main(void)
{
    pid_t pid = fork();
    
    if(pid == 0){//子进程
        printf("child process runing(%d)\n",getpid());
        process_read();
    }
    else{//父进程
        printf("parent process runing(%d)\n",getpid());
        process_write();
    }
}

关于技术交流

此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。二维码

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

theboynoName

感谢鼓励

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

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

打赏作者

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

抵扣说明:

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

余额充值