system V信号量和Posix信号量
阅读原文时间:2023年07月10日阅读:5

一、函数上的区别

信号量有两种实现:传统的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

五、代码示例

/*
* MultiProcessLock.cpp
*
* Created on: 2013-8-28
* Detail: System V 信号量的使用
*/

#include
#include
#include
#include
#include
#include
#include

// 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\\n", 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\\n", iSemId);  
}

/\* 进程同步执行 \*/  
for(int i = 0; i < 10; ++i)  
{  
    if(binary\_semaphore\_wait(iSemId))  
    {  
        perror("semop: ");  
        exit(-1);  
    }  
    printf("%d ", getpid());  
    sleep(1);  
    printf("%d\\n", getpid());  
    if(binary\_semaphore\_post(iSemId))  
    {  
        exit(-1);  
    }

}

return 0;  

}

/* posixSemProgessLock.cpp
*
* Created on: 2013-8-29
* Detail: 使用Posix信号量的进程同步
*/

#include
#include
#include
#include
#include

#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\\n",val, getpid());  
sleep(1);  
printf("2: getvalue: value=%d, pid=%d\\n",val, getpid());  
sem\_post(sem);  
sleep(1);  
sem\_getvalue(sem,&val);  
printf("3: getvalue: value=%d, pid=%d\\n",val, getpid());  
sem\_unlink(SEM\_NAME);  
return 0;  

}

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

/*thread_sem.c*/
#include
#include
#include
#include
#include

#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\n", thrd_num);

for (count = 0; count < REPEAT\_NUMBER; count++)  
{  
    delay\_time = (int)(rand() \* DELAY\_TIME\_LEVELS/(RAND\_MAX)) + 1;  
    sleep(delay\_time);  
    printf("\\tThread %d: job %d delay = %d\\n", thrd\_num, count, delay\_time);  
}  

printf("Thread %d finished\\n", 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\\n", val);  
    res = pthread\_create(&thread\[no\], NULL, thrd\_func, (void\*)no);  
    if (res != 0)  
    {  
        printf("Create thread %d failed\\n", no);  
        exit(res);  
    }  
}  

printf("Create treads success\\n Waiting for threads to finish...\\n");  
/\* 对最后创建的线程的信号量进行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\\n", no);  
    }  
    else  
    {  
        printf("Thread %d join failed\\n", no);  
    }  
    /\* 进行V操作 \*/  
    sem\_post(&sem\[(no + THREAD\_NUMBER - 1) % THREAD\_NUMBER\]);  
}  

for (no = 0; no < THREAD\_NUMBER; no++)  
{  
    /\* 删除信号量 \*/  
    sem\_destroy(&sem\[no\]);  
}  
return 0;  

}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章