UNIX网络编程卷2进程间通信读书笔记—Posix信号量

一、Posix信号量

 

1.Posix信号量分为两种:

1.   有名信号量:使用Posix IPC名字标识(有名信号量总是既可用于线程间的同步,又可以用于进程间的同步)

2.   内存信号量:存放在共享内存区中(基于内存的信号量则必须在创建时指定成是否在进程间共享,且在所有进程的共享内存区,具有随进程的持续性)

Posix信号量不必在内核中维护(System V信号量由内核维护),由可能为路径名的名字来标识。

Posix信号量更常用于进程间同步,互斥锁常用于线程间同步)

 

2.基本操作:

1.   创建(create):指定初始值。

2.   等待(wait):如果值小于等于0则阻塞,否则将其减一,又称P操作。

3.   挂出(post):将信号量的值加1,加后如果值大于0,则唤醒一个阻塞在等待上的线程,又称V操作。

二值信号量可用于互斥,就像互斥锁一样。但互斥锁必须由锁住它的线程解锁信号量的挂出却不必由执行过等待的同一线程执行

信号量的wait和post与条件变量的wait和signal类似,区别是:因为永久的改变了信号量的值,信号量的操作总被记住(会影响到后续的操作);条件变量的signal如果没有线程在等待,该信号将丢失(对后续操作没有影响)。

互斥锁是为上锁而优化的,条件变量是为等待优化的,信号量既可以上锁也可以等待,因此开销更大。

3.Posix信号量操作

有名信号量:

内存信号量:

sem_open

sem_init:需要指定是否共享

                                                                 sem_wait:原子的“测试并减1”操作

                                                                 sem_trywait

                                                                 sem_post:同步技巧中唯一能在信号处理函数内安全调用的操作

                                                                 sem_getvalue

sem_close

sem_destroy

sem_unlink

 

即使当前没有进程打开信号量,它的值仍然保持,因此Posix有名信号量至少是随内核持续的。

在父进程中打开的任何信号量,fork后在子进程中仍打开。

 

 

 

关于posix有名信号量使用的几点注意

 

1、有名信号量使用sem_unlink从系统中删除。每个信号量有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号量从文件系统中删除。也就是要等待最后一个sem_close发生。

 

2、彼此无亲缘关系的进程间需使用信号量时,一般用有名信号量。如果不需要使用关联名字时,可改用内存信号量。

内存信号量需要放在共享内存区中,并由sem_init函数初始化为shared状态才能被不同进程使用,这种情况下它的持续性与共享内存区相同。sem_init总是初始化信号量的值,因此,对于一个给定的信号量,必须小心保证只调用sem_init一次。

 

3、我们可以用sem_wait来申请共享资源,sem_wait函数可以测试所指定信号量的值,如果该值大于0,那就将它减1并立即返回。我们就可以使用申请来的共享资源了。如果该值等于0,调用线程就被进入睡眠状态,直到该值变为大于0,这时再将它减1,函数随后返回。sem_wait操作必须是原子的。sem_wait和sem_trywait的差别是:当所指定信号量的值已经是0时,后者并不将调用线程投入睡眠。相反,它返回一个EAGAIN错误。

 当一个线程使用完某个信号量时,它应该调用sem_post来告诉系统申请的资源已经用完。本函数和sem_wait函数的功能正好相反,它把所指定的信号量的值加1,然后唤醒正在等待该信号量值变为正数的任意线程。

 

4、.Posix有名信号量的值是随内核持续的。也就是说,一个进程创建了一个信号量,这个进程结束后,这个信号量还存在,并且信号量的值也不会改变。

 

5、当持有某个信号量锁的进程没有释放它就终止时,内核并不给该信号量解锁。


 

posix有名信号量应用于多线程

#include

#include

#include

#include

#include

 

void *thread_function(void *arg); /*线程入口函数*/

void print(pid_t); /*共享资源函数*/

sem_t *sem; /*定义Posix有名信号量*/

int val; /*定义信号量当前值*/

 

int main(int argc,char *argv[])

{

int n=0;

 

if(argc!=2)

{

    printf(“please input a file name!\n”);

    exit(1);

}

sem=sem_open(argv[1],O_CREAT,0644,3); /*打开一个信号量*/

 

while(n++<5) /*循环创建5个子线程,使它们同步运行*/

{

    if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0)

        {

             perror(“Thread creation failed”);

             exit(1);

         }

}

pthread_join(a_thread,NULL);

sem_close(bin_sem);

sem_unlink(argv[1]);

}

 

void *thread_function(void *arg)

{

sem_wait(sem); /*申请信号量*/

    print(); /*调用共享代码段*/

    sleep(1);

    sem_post(sem); /*释放信号量*/

    printf(“I’m finished,my tid is %d\n”,pthread_self());

}

 

void print()

{

printf(“I get it,my tid is %d\n”,pthread_self());

sem_getvalue(sem,&val);

printf(“Now the value have %d\n”,val);

}

 

程序用循环的方法建立5个线程,然后让它们调用同一个线程处理函数thread_function,在函数里我们利用信号量来限制访问共享资源的线程数。共享资源我们用print函数来代表,在真正编程中它有可以是一个终端设备(如打印机)或是一段有实际意义的代码。

 

 


你可能感兴趣的:(Unix网络编程(卷二))