zoukankan      html  css  js  c++  java
  • system V信号量和Posix信号量

    一、函数上的区别

            信号量有两种实现:传统的System V信号量和新的POSIX信号量。它们所提供的函数很容易被区分:对于所有System V信号量函数,在它们的名字里面没有下划线。例如,应该是semget()而不是sem_get()。然而,所有的的POSIX信号量函数都有一个下划线。下面列出了它们提供的所有函数清单:

    Systm V POSIX
    semctl() sem_getvalue()
    semget() sem_post()
    semop() sem_timedwait()
    sem_trywait()
    sem_wait()
    sem_destroy()
    sem_init()
    sem_close()
    sem_open()
    sem_unlink()


    二、使用上的区别

    1、XSI system V的信号量是信号量集,可以包括多个信号灯(有个数组),每个操作可以同时操作多个信号灯
         posix是单个信号灯,POSIX有名信号灯支持进程间通信,无名信号灯放在共享内存中时可以用于进程间通信。
    2、POSIX信号量在有些平台并没有被实现,比如:SUSE8,而SYSTEM V大多数LINUX/UNIX都已经实现。两者都可以用于进程和线程间通信。但一般来说,system v信号量用于 进程间同步、有名信号灯既可用于线程间的同步,又可以用于进程间的同步、posix无名用于同一个进程的不同线程间,如果无名信号量要用于进程间同步,信号量要放在共享内存中。
    3、POSIX有两种类型的信号量,有名信号量和无名信号量。有名信号量像system v信号量一样由一个名字标识。
    4、POSIX通过sem_open单一的调用就完成了信号量的创建、初始化和权限的设置,而system v要两步。也就是说posix 信号是多线程,多进程安全的,而system v不是,可能会出现问题。
    5、system V信号量通过一个int类型的值来标识自己(类似于调用open()返回的fd),而sem_open函数返回sem_t类型(长整形)作为posix信号量的标识值。
    6、对于System V信号量你可以控制每次自增或是自减的信号量计数,而在Posix里面,信号量计数每次只能自增或是自减1。
    7、Posix无名信号量提供一种非常驻的信号量机制。
    8、相关进程: 如果进程是从一已经存在的进程创建,并最终操作这个创建进程的资源,那么这些进程被称为相关的。


    三、注意事项

    1、Posix有名信号灯的值是随内核持续的。也就是说,一个进程创建了一个信号灯,这个进程结束后,这个信号灯还存在,并且信号灯的值也不会改变。当持有某个信号灯锁的进程没有释放它就终止时,内核并不给该信号灯解锁

    2、posix有名信号灯是通过内核持续的,一个进程创建一个信号灯,另外的进程可以通过该信号灯的外部名(创建信号灯使用的文件名)来访问它。posix基于内存的无名信号灯的持续性却是不定的,如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它也会消失。如果某个基于内存的信号灯是在不同进程间同步的,该信号灯必须存放在共享内存区中,这要只要该共享内存区存在,该信号灯就存在。

    四、总结

    1、System V的信号量一般用于进程同步, 且是内核持续的, api为:semget、semctl、semop
    2、Posix的有名信号量一般用于进程同步, 有名信号量是内核持续的. 有名信号量的api为:sem_open、sem_close、sem_unlink
    3、Posix的无名信号量一般用于线程同步, 无名信号量是进程持续的, 无名信号量的api为:sem_init、sem_destroy

    五、代码示例


    <span style="background-color: rgb(255, 255, 255);">/*</span><span style="background-color: rgb(255, 255, 252);">
     * MultiProcessLock.cpp
     *
     *  Created on: 2013-8-28
     *  Detail: System V 信号量的使用
     */
    
    #include <sys/sem.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <unistd.h>
    #include <errno.h>
    
    
    // val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针。
    union semun
    {
    	int val;                   /*value for SETVAL*/
    	struct semid_ds *buf;      /*buffer for IPC_STAT & IPC_SET*/
    	unsigned short int *array; /*array for GETALL & SETALL*/
    	struct seminfo *__buf;     /*buffer for IPC_INFO*/
    };
    
    
    /* 等待一个二元信号量:阻塞直到信号量的值为正,然后将其减1  */
    int binary_semaphore_wait(int semid)
    {
    	struct sembuf sem_b;
    
    	sem_b.sem_num = 0;
    	/* 减一。 */
    	sem_b.sem_op = -1;
    	/* 允许撤销操作 */
    	sem_b.sem_flg = SEM_UNDO;
    	return semop (semid, &sem_b, 1);
    }
    
    /* 对一个二元信号量执行投递操作:将其值加一。 这个操作会立即返回 */
    int binary_semaphore_post (int semid)
    {
    	struct sembuf sem_b;
    	/* 使用(且仅使用)第一个信号量 */
    	sem_b.sem_num = 0;
    	/* 加一 */
    	sem_b.sem_op = 1;
    	/* 允许撤销操作 */
    	sem_b.sem_flg = SEM_UNDO;
    	return semop (semid, &sem_b, 1);
    }
    
    int main()
    {
    	int iSemId;
    	int nsems  = 1;
    
    	/*
    	 * 通过 IPC_CREAT | IPC_EXCL 可以判断这个key对应的信号量是不是已经存在了,这可以用在单进程运行多次的情况下,只初始化一次信号量的情况下
    	 */
    	int semflg = 0666 | IPC_CREAT | IPC_EXCL; // 如果key对应的((semflg &IPC_CREAT) &&(semflg &IPC_EXCL))非0, 那么semget返回EEXIST
    	key_t key = (key_t)0x20130828;
    
    	/*创建一个新的信号量集*/
    	iSemId = semget(key, nsems, semflg);
    	if (iSemId < 0 && errno != EEXIST)
    	{
    		perror( "semget ") ;
    		return -1;
    	}
    
    
    	if(iSemId >= 0) // 这个信号量是第一次创建,则初始化
    	{
    		printf("create: semid is %d
    ", iSemId);
    		/* 初始化信号量 */
    		semun sem_union;
    		sem_union.val = 1;
    		if(semctl(iSemId, 0, SETVAL, sem_union) == -1)
    		{
    			perror("semctl");
    			return -1;
    		}
    	}
    	else  // 这个信号量已经有了,则获得这个信号量值
    	{
    		iSemId = semget(key, nsems, 0666);
    		printf("exit: semid is %d
    ", iSemId);
    	}
    
    	/* 进程同步执行 */
    	for(int i = 0; i < 10; ++i)
    	{
    		if(binary_semaphore_wait(iSemId))
    		{
    			perror("semop: ");
    			exit(-1);
    		}
    		printf("%d ", getpid());
    		sleep(1);
    		printf("%d
    ", getpid());
    		if(binary_semaphore_post(iSemId))
    		{
    			exit(-1);
    		}
    
    	}
    
    	return 0;
    }</span>

      

    /* posixSemProgessLock.cpp 
     * 
     *  Created on: 2013-8-29 
     * Detail: 使用Posix信号量的进程同步 
     */  
      
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <semaphore.h>  
    #include <fcntl.h>  
      
    #define SEM_NAME "0x20130829"  
      
    int main()  
    {  
        /* 初始化一个有名二元信号灯  */  
        sem_t *sem;  
        sem = sem_open(SEM_NAME, O_CREAT, 0666, 10);  
        if(sem == SEM_FAILED)  
        {  
            perror("sem init failed:");  
            return -1;  
        }  
      
        int val;  
        sem_wait(sem);  
        sem_getvalue(sem,&val);  
        printf("1: getvalue: value=%d, pid=%d
    ",val, getpid());  
        sleep(1);  
        printf("2: getvalue: value=%d, pid=%d
    ",val, getpid());  
        sem_post(sem);  
        sleep(1);  
        sem_getvalue(sem,&val);  
        printf("3: getvalue: value=%d, pid=%d
    ",val, getpid());  
        sem_unlink(SEM_NAME);  
        return 0;  
    }  

    PV原子操作主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量sem。 当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行,下面是一个线程同步的例子:

    /*thread_sem.c*/  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <pthread.h>  
    #include <semaphore.h>  
    #include <unistd.h>  
       
    #define THREAD_NUMBER        3                 /* 线程数 */  
    #define REPEAT_NUMBER        3                 /* 每个线程中的小任务数 */  
    #define DELAY_TIME_LEVELS   10.0               /*小任务之间的最大时间间隔*/  
    sem_t sem[THREAD_NUMBER];  
       
    void *thrd_func(void *arg)  
    {  
        int thrd_num = (int)arg;  
        int delay_time = 0;  
        int count = 0;  
        /* 进行P操作 */  
        sem_wait(&sem[thrd_num]);  
        printf("Thread %d is starting
    ", thrd_num);  
      
        for (count = 0; count < REPEAT_NUMBER; count++)  
        {  
            delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;  
            sleep(delay_time);  
            printf("	Thread %d: job %d delay = %d
    ", thrd_num, count, delay_time);  
        }  
      
        printf("Thread %d finished
    ", thrd_num);  
        pthread_exit(NULL);  
    }  
       
    int main(void)  
    {  
        pthread_t thread[THREAD_NUMBER];  
        int no = 0, res;  
        void * thrd_ret;  
      
        srand(time(NULL));  
        for (no = 0; no < THREAD_NUMBER; no++)  
        {  
            sem_init(&sem[no], 0, 0);  
            int val;    
            sem_getvalue(&sem[no],&val);   
            printf("val is %d
    ", val);             
            res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);  
            if (res != 0)  
            {  
                printf("Create thread %d failed
    ", no);  
                exit(res);  
            }  
        }  
      
        printf("Create treads success
     Waiting for threads to finish...
    ");  
        /* 对最后创建的线程的信号量进行V操作 */  
        sem_post(&sem[THREAD_NUMBER - 1]);  
        for (no = THREAD_NUMBER - 1; no >= 0; no--)  
        {  
            res = pthread_join(thread[no], &thrd_ret);  
            if (!res)  
            {  
                printf("Thread %d joined
    ", no);  
            }  
            else  
            {  
                printf("Thread %d join failed
    ", no);  
            }  
            /* 进行V操作 */  
            sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]);  
        }  
      
        for (no = 0; no < THREAD_NUMBER; no++)  
        {  
            /* 删除信号量 */  
            sem_destroy(&sem[no]);  
        }  
        return 0;         
    }  
  • 相关阅读:
    Nginx的proxy_cache缓存
    linux服务器优化
    LVS+keepalived负载均衡实战
    bash history(history命令)
    APACHE默认模块功能说明
    MySQL配置文件例子翻译
    Microsoft JET Database Engine (0x80004005) 未指定的错误的完美解决[转贴]
    entity framework 新增 修改 删除 查询
    Flash Builder 找不到所需的 Adobe Flash Player 调试器版本
    sql server 2008 远程连接
  • 原文地址:https://www.cnblogs.com/Zoran-/p/5819256.html
Copyright © 2011-2022 走看看