温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

POSIX有名信号量与无名信号量有哪些区别

发布时间:2021-10-13 13:41:36 来源:亿速云 阅读:235 作者:iii 栏目:编程语言

本篇内容主要讲解“POSIX有名信号量与无名信号量有哪些区别”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“POSIX有名信号量与无名信号量有哪些区别”吧!

区别

有名信号量和无名信号量的差异在于创建和销毁的形式上,但是其他工作一样。

无名信号量只能存在于内存中,要求使用信号量的进程必须能访问信号量所在的这一块内存,所以无名信号量只能应用在同一进程内的线程之间(共享进程的内存),或者不同进程中已经映射相同内存内容到它们的地址空间中的线程(即信号量所在内存被通信的进程共享)。意思是说无名信号量只能通过共享内存访问。

相反,有名信号量可以通过名字访问,因此可以被任何知道它们名字的进程中的线程使用。

单个进程中使用 POSIX 信号量时,无名信号量更简单。多个进程间使用 POSIX 信号量时,有名信号量更简单。

联系

无论是有名信号量还是无名信号量,都可以通过以下函数进行信号量值操作。

wait

weit 为信号量值减一操作,总共有三个函数,函数原型如下:

#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

Link with -pthread.这一句表示 gcc 编译时,要加 -pthread.返回值:若成功,返回 0 ;若出错,返回-1
复制代码

其中,第一个函数的作用是,若 sem 小于 0 ,则线程阻塞于信号量 sem ,直到 sem 大于 0 ;否则信号量值减1。

第二个函数作用与第一个相同,只是此函数不阻塞线程,如果 sem 小于 0,直接返回一个错误(错误设置为 EAGAIN )。

第三个函数作用也与第一个相同,第二个参数表示阻塞时间,如果 sem 小于 0 ,则会阻塞,参数指定阻塞时间长度。 abs_timeout 指向一个结构体,这个结构体由从 1970-01-01 00:00:00 +0000 (www.taobao-wd.com) 开始的秒数和纳秒数构成。结构体定义如下:

struct timespec {
               time_t tv_sec;      /* Seconds */
               long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
           };
复制代码

如果指定的阻塞时间到了,但是 sem 仍然小于 0 ,则会返回一个错误 (错误设置为 ETIMEDOUT )。

post

post 为信号量值加一操作,函数原型如下:

#include <semaphore.h>

int sem_post(sem_t *sem);

Link with -pthread.返回值:若成功,返回 0 ;若出错,返回-1
复制代码

应用实例

有名信号量

创建

有名信号量创建可以调用 sem_open 函数,函数说明如下:

#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);  
sem_t *sem_open(const char *name, int oflag,	
                       mode_t mode, unsigned int value);	

Link with -pthread.返回值:若成功,返回指向信号量的指针;若出错,返回SEM_FALLED
复制代码

其中第一种函数是当使用已有的有名信号量时调用该函数,flag 参数设为 0 。

如果要调用第二种函数,flag 参数应设为 O_CREAT ,如果有名信号量不存在,则会创建一个新的,如果存在,则会被使用并且不会再初始化。

当我们使用 O_CREAT 标志时,需要提供两个额外的参数:

mode 参数指定谁可以访问信号量,即权限组,mode 的取值和打开文件的权限位相同,比如 0666 表示 所有用户可读写 。因为只有读和写访问要紧,所以实现经常为读和写打开信号量。

value 指定信号量的初始值,取值范围为 0~SEM_VALUE_MAX 。

如果信号量存在,则调用第二个函数会忽略后面两个参数(即 mode 和 value )。

释放

当完成信号量操作以后,可以调用 sem_close 函数来释放任何信号量的资源。函数说明如下:

#include <semaphore.h>

int sem_close(sem_t *sem);

Link with -pthread.返回值:若成功,返回 0 ;若出错,返回-1
复制代码

如果进程没有调用该函数便退出了,内核会自动关闭任何打开的信号量。无论是调用该函数还是内核自动关闭,都不会改变释放之前的信号量值。

销毁

可以使用 sem_unlink 函数销毁一个有名信号量。函数说明如下:

#include <semaphore.h>

int sem_unlink(const char *name);

Link with -pthread.返回值:若成功,返回 0 ;若出错,返回-1
复制代码

sem_unlink 函数会删除信号量的名字。如果没有打开的信号量引用,则该信号量会被销毁,否则,销毁会推迟到最后一个打开的引用关闭时才进行。

例子

例如,管道通信中,如果父进程使用 fork()创建两个子进程1和2,子进程1,2按顺序向管道写一段文字,最后父进程从管道将子进程写入的内容读出来,要保证进程执行的先后顺序,可以用有名信号量来解决。

#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<semaphore.h>
#include<sys/sem.h>
#include <sys/stat.h>

#include<fcntl.h>

int main(){
    int pid1,pid2;
    sem_t *resource1; 
    sem_t *resource2; 
    int Cpid1,Cpid2=-1;
    int fd[2];//0为读出段,1为写入端
    char outpipe1[100],inpipe[200],outpipe2[100];
    pipe(fd);//建立一个无名管道

    pid1 = fork();
    if(pid1<0){
        printf("error in the first fork!");
    }else if(pid1==0){//子进程1
        resource1=sem_open("name_sem1",O_CREAT,0666,0);
        Cpid1 = getpid();
        close(fd[0]);//关掉读出端
        lockf(fd[1],1,0);//上锁,则锁定从当前偏移量到文件结尾的区域
        sprintf(outpipe1,"Child process 1 is sending a message!");
        write(fd[1],outpipe1,strlen(outpipe2));
        lockf(fd[1],0,0);//解锁
        sem_post(resource1);
        sem_close(resource1);
        exit(0);
   }else{
        
        pid2 = fork();
        if(pid2<0){
            printf("error in the second fork!\n");
        }else if(pid2==0){  
                resource1=sem_open("name_sem1",O_CREAT,0666,0);
                resource2=sem_open("name_sem2",O_CREAT,0666,0);
                Cpid2 = getpid();
                sem_wait(resource1);
close(fd[0]);
                lockf(fd[1],1,0);
                sprintf(outpipe2,"Child process 2 is sending a message!");

                write(fd[1],outpipe2,strlen(outpipe2));
                lockf(fd[1],0,0);//解锁
                sem_post(resource2);
                sem_close(resource1);
                sem_close(resource2);
                exit(0);
        }
        if(pid1 > 0 && pid2 >0){
                resource2=sem_open("name_sem2",O_CREAT,0666,0);
                sem_wait(resource2);
                waitpid(pid1,NULL,0);
                waitpid(pid2,NULL,0);
                close(fd[1]);//关掉写端
                read(fd[0],inpipe,200);
                printf("%s\n",inpipe);
                sem_close(resource2);
                
                exit(0);
        }
        sem_unlink("name_sem1");
        sem_unlink("name_sem2");
    }
    return 0;
}

复制代码

无名信号量

创建

无名信号量可以通过 sem_init 函数创建,函数说明如下:

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

Link with -pthread.返回值:若成功,返回 0 ;若出错,返回-1
复制代码

pshared 参数指示该信号量是被一个进程的多个线程共享还是被多个进程共享。

如果 www.sinorichmake.com 的值为 0 ,那么信号量将被单进程中的多线程共享,并且应该位于某个地址,该地址对所有线程均可见(例如,全局变量或变量在堆上动态分配)。

如果 pshared 非零,那么信号量将在进程之间共享,并且信号量应该位于共享内存区域。

销毁

如果无名信号量使用完成,可以调用 sem_destory 函数销毁该信号量。函数说明如下:

#include <semaphore.h>

int sem_destroy(sem_t *sem);

Link with -pthread.返回值:若成功,返回 0 ;若出错,返回-1
复制代码

注意:

  • 销毁其他进程或线程当前被阻塞的信号量会产生未定义的行为。

  • 使用已销毁的信号量会产生未定义的结果,除非使用 sem_init 重新初始化信号量。

  • 一个无名信号量应该在它所在的内存被释放前用 sem_destroy 销毁。如果不这样做,可能会导致某些实现出现资源泄漏。

例子

使用无名信号量实现有名信号量中的例子:

#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<semaphore.h>
#include<sys/sem.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include<fcntl.h>

int main(){
    int pid1,pid2;
    int Cpid1,Cpid2=-1;
    int fd[2];//0为读出段,1为写入端
    char outpipe1[100],inpipe[200],outpipe2[100];
    void *shm = NULL;
    sem_t *shared;
    int shmid = shmget((key_t)(1234), sizeof(sem_t *), 0666 | IPC_CREAT);//创建一个共享内存,返回一个标识符
    if(shmid == -1){
        perror("shmat :");
        exit(0);
    }
    shm = shmat(shmid, 0, 0);//返回指向共享内存第一个字节的指针
    shared = (sem_t *)shm;
    sem_init(shared, 1, 0);//初始化共享内存信号量值为0
    pipe(fd);//建立一个无名管道

    pid1 = fork();
    if(pid1<0){
        printf("error in the first fork!");
    }else if(pid1==0){//子进程1

        Cpid1 = getpid();
        close(fd[0]);//关掉读出端
        lockf(fd[1],1,0);//上锁,则锁定从当前偏移量到文件结尾的区域
        sprintf(outpipe1,"Child process 1 is sending a message!");
        write(fd[1],outpipe1,strlen(outpipe1));
        lockf(fd[1],0,0);//解锁
        sem_post(shared);

        exit(0);
   }else{

        pid2 = fork();
        if(pid2<0){
            printf("error in the second fork!\n");
        }else if(pid2==0){
                sem_wait(shared);
                Cpid2 = getpid();
close(fd[0]);
                lockf(fd[1],1,0);
                sprintf(outpipe2,"Child process 2 is sending a message!");

                write(fd[1],outpipe2,strlen(outpipe2));
                lockf(fd[1],0,0);//解锁

                exit(0);
        }
        if(pid1 > 0 && pid2 >0){

                waitpid(pid2,NULL,0);//同步,保证子进程先写父进程再读
                close(fd[1]);//关掉写端
                read(fd[0],inpipe,200);
                printf("%s\n",inpipe);

                exit(0);
        }

    }
    return 0;
}

到此,相信大家对“POSIX有名信号量与无名信号量有哪些区别”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI