linux IPC

您所在的位置:网站首页 linux查看信号量最大 linux IPC

linux IPC

2024-02-23 21:59| 来源: 网络整理| 查看: 265

目录

1、信号量

1.1、Posix信号量的选择

1.2、基于内存的信号量的持续性

2、信号量分类

3、特性:

4、Posix信号量

4.1、sem_open()函数

4.2、sem_close()函数

4.3、sem_unlink()函数

4.4、sem_wait()函数

4.5、sem_trywait()函数

4.6、sem_post()函数

4.7、sem_getvalue()函数

4.8、sem_init()函数

4.9、sem_destroy()函数

4.10、Posix信号量限制

5、System V信号量

5.1、System V特点

5.2、缺陷                

5.3、smeget()函数

5.4、semop()函数

5.5、semctl()函数

1、信号量

信号量是一种用于提供不同进程间或一个从给定进程的不同线程间同步手段的原语。

Posix有名信号量使用Posix IPC名字标识Posix基于内存的信号量存放在共享内存区System V信号量在内核中维护 1.1、Posix信号量的选择

1)单个进程的各个线程共享,可以使用基于内存的信号量。

2)彼此无亲缘关系的不同进程需使用信号量时,通常使用有名信号量。

1.2、基于内存的信号量的持续性

1)如果某个基于内存的信号量是由单个进程的各个线程共享的(sem_init的shared的参数为0),那么该信号量具有随进程的持续性,当该进程终止时它也消失。

2)如果某个基于内存的信号量是在不同进程间共享的(sem_init的shared的参数为1),那么该信号量必须存放在共享内存区中,因而只要该共享内存区任然存在,该信号量也就继续存在。

2、信号量分类 Posix二值信号量其值为0或1的信号量计数信号量其值在 0和某个限制值System V计数信号量集一个或多个信号量(构成一个集合),其中每个都是计数信号量。

注:当谈论System V信号量时,所指的是计数信号量集。当谈论Posix信号量时,所指的是单个计数信号量。

3、特性:

1)互斥锁必须总是由锁住它的线程解锁,信号量的挂出却不必由执行过它的等待操作的同一线程执行。

2)信号量有一个与之关联的状态(计数值),信号量的挂出操作总是被记住。向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失(即如果某个线程调用pthread_cond_signal,不过当时没有任何线程阻塞在pthread_cond_wait调用中,那么发往相应条件变量的信号将丢失)。

3)当持有某个信号量锁的进程没有释放该锁就终止时,内核没有自动挂出该信号量。这与记录锁不一样,当持有某个记录锁的进程没有释放它就终止时,内核会自动释放。

4、Posix信号量 4.1、sem_open()函数

创建一个新的有名信号量或打开一个已存在的有名信号量。

注:sem_open不需要类似于shared的参数或类似于PTHREAD_PROCESS_SHARED的属性,因为有名信号量总是可以在不同进程间共享。

#include #include #include sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);

参数 name:有名信号量名字。

参数 oflag:0、O_CREAT或O_CREAT|O_EXCL。

参数 mode:指定权限位(指定O_CREAT时才需要)。

参数 value:指定信号量的初值(指定O_CREAT时才需要)。

返回值:若成功则为指向信号量的指针,若出错则为SEM_FAILED。

4.2、sem_close()函数

关闭由sem_open()打开的有名信号量。

#include int sem_close(sem_t *sem);

参数 sem:要关闭的有名信号量。

返回值:若成功则为0,若出错则为-1.

注:关闭一个信号量并没有将它从系统中删除。即使当前没有进程打开着某个信号量,他的值仍然保持。

4.3、sem_unlink()函数

从系统中删除有名信号量。

#include int sem_unlink(const char *name);

参数 name:有名信号量名字

返回值:若成功则为0,若出错则为-1.

注:当引用计数还大于0时,name就能从文件系统中删除,然而其信号量的析构(不同于将它的名字从文件系统删除)却要等最后一个sem_close发生为止。

4.4、sem_wait()函数

等待信号量,如果该值大于0,那就将它减1并立即返回。如果该值等于0,调用线程就被投入睡眠,知道该值变为大于0,此时再将它减1,函数随后返回。

#include int sem_wait(sem_t *sem);

参数 sem:等待的信号量。

返回值:若成功则为0,若出错则为-1.

4.5、sem_trywait()函数

当所指定信号量为0时,不将调用线程投入睡眠。而是返回一个EAGAIN错误。

#include int sem_trywait(sem_t *sem);

参数 sem:等待的信号量。

返回值:若成功则为0,若出错则为-1.

注:如果被某个信号中断,sem_wait就可能过早的返回错误EINTR。

4.6、sem_post()函数

把所指定的信号量加1,然后唤醒正在等待该信号量变为正数的任意线程。

#include int sem_post(sem_t *sem);

参数 sem:等待的信号量。

返回值:若成功则为0,若出错则为-1.

注:在各种各样的同步技巧中(互斥锁、条件变量、读写锁、信号量)中、能够从信号处理程序中安全调用的唯一函数时sem_post。

4.7、sem_getvalue()函数 #include int sem_getvalue(sem_t *sem, int *sval);

参数 sem:等待的信号量。

参数 savl:保存所指定信号量的当前值。如果该信号量当前已上锁,那么返回或为0, 或为某个负数(其决定值就是等待该信号量解锁的线程数)

返回值:若成功则为0,若出错则为-1。

4.8、sem_init()函数

基于内存的信号量初始化。

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

参数 sem:要初始化的内存信号量。

参数 shared:如果为0,那么待初始化的信号量是在同一进程的各个线程间共享的,否则该信号量是在进程间共享的。

注:当shared为非0时,该信号量必须存放在某种类型的共享内存中,而即将使用它的所有进程都要能访问该共享内存区。

参数 value:信号量的初始值。

返回值:若出错则为-1。

4.9、sem_destroy()函数

摧毁基于内存的信号量。

#include int sem_destroy(sem_t *sem);

参数 sem:要摧毁的内存信号量。

返回值:若成功则为0,若出错则为-1.

4.10、Posix信号量限制 名称说明获取方法SEM_NSEMS_MAX 一个进程可同时打开着的最大信号量数sysconf(_SC_SEM_NSEMS_MAX)SEM_VALUE_MAX 一个信号量的最大值sysconf(_SC_SEM_VALUE_MAX) 5、System V信号量 5.1、System V特点 1创建一个System V信号量集需要技巧,因为创建该集合并随后初始化其各个值需要两个操作,从而可能导致竞争状态2System V信号量当指定应用到某个信号量集的一组信号量操作是,那么所有操作都执行,要么所有操作都不执行。3System V信号量提供“复旧”特性,该特性保证在进程终止时逆转某个信号量操作。4Posix信号量只允许-1(sem_wait)和+1(sem_post)这两个操作。System V信号量允许信号量的值增长或减少不光是1,而且允许等待信号量的值变为0.

对于系统的每个信号集,内核维护一个semid_ds结构体。

#include struct semid_ds { struct ipc_perm sem_perm; /* operation permission struct */ struct sem *sem_base; /* ptr to array of semaphores in set */ ushort sem_nsems; /* # of semaphores in set */ time_t sem_otime; /* time of last semop() */ time_t sem_ctime; /* time of creation or last IPC_SET */ }; struct sem{ unsigned short semval; /* 信号值的实际值(非负) */ pid_t sempid; /* 对其值执行最后一次操作的进程的进程ID*/ unsigned short semncnt; /* 等待其值增长的进程数计数 */ unsigned short semzcnt; /* 等待其值变为0的进程数计数 */ }; struct ipc_perm { uid_t uid; /* 属主用户ID */ gid_t gid; /* 属主组ID */ uid_t cuid; /* 创造者用户ID */ gid_t cgid; /* 创造者组ID */ unsigned short mode; /* 读写权限 */ unsignedshort seq; /* 槽位使用情况序列号 */ key_t key; /* IPC键 */ };

注:sem_base含有指向某个sem结构数组的指针。当前信号量集中的每个信号量对应其中一个数组元素。

5.2、缺陷                

在System V信号量的设计中,创建一个信号量集(semget)并将它初始化(semctl)需要两次系统调用。

一个不完备的解决方法是:在调用semget时指定IPC_CREAT|IPC_EXCL标志,这样只有一个进程(首先调用semget的那个进程)创建所需的信号量,该进程随后初始化该信号量,其他进程会收到来自semget的一个EEXIST错误,于是再次调用semget,不过这次调用既不指定IPC_CREAT标志也不指定IPC_EXCL标志。

上述问题不完备的点在于,首先调用semget的那个进程在调用semctl初始化信号量集前,内核有可能在这2个步骤之间把上下文切换到另一个进程。而新切换的进程可能使用该信号量,但是信号量的值尚未被初始化!!!

绕过上述竞争状态的方法:当semget创建一个新的信号量集时,其semid_ds结构的sem_otime成员保证被置为0,该成员只是在semop调用成功时才被设置为当前值。因此别的进程再次成功的调用semget后,必须以IPC_STAT命令调用semctl。然后等待sem_otime变为非0值,到时就可断定该信号量已被初始化,而且对它进行初始化的那个进程已成功调用semop。这意味着创建该信号量的那个进程必须初始化它的值,而且必须在任何其他进程可以使用该信号量前调用semop.

注:Posix有名信号量通过让单个函数(sem_open)创建并初始化信号量来避免上述问题。而且即使指定O_CREATE标志,信号量也只是在尚未存在的前提下才被初始化。

5.3、smeget()函数

创建一个信号量集或访问一个已存在的信号量集。

注:一旦创建完差一个信号量集,就不能改变其中的信号量数。

#include int semget(key_t key,int nsems,int oflag);

参数 key:IPC键值。

参数 nsems:指定集合中信号量数。如果不创建一个新信号量集,而只是访问一个已存在的集合,那就可以把参数指定为0。

参数 oflag:SEM_R或SEM_A的组合(R代表读-read,A代表改-alter)。可以与IPC_CREAT或IPC_CREAT|IPC_EXCL按位或。

数字值(八进制)信号量说明0400SEM_R由用户(属主)读0200SEM_W由用户(属主)写0040SEM_R>>3由(属)组成员读0020SEM_W>>3由(属)组成员写0004SEM_R>>6由其他用户读0002SEM_W>>6由其他用户写

返回值:若成功则为非负信号量标识符,若出错则为-1。

当实际操作为创建一个新的信号量集时,相应的semid_ds结构的以下成员将被初始化。

1)sem_perm结构的uid和cuid成员被置位调用进程的有效用户ID,gid和cgid成员被置为调用进程的有效ID。

2) oflag参数中的读写权限位存入sem_perm.mode。

3) sem_otime被置为0,sem_ctime则被置为当前时间。

4) sem_nsems被置为nsems参数的值。

5)与该集合中每个信号量关联的各个sem结构并不初始化。由SET_VAL或SETALL命令调用semctl时初始化

5.4、semop()函数

对信号量进行操作。

注:semadj是指定信号量针对调用进程的调整值。只有在对于本操作的sembuf结构的sem_flg成员中指定SEM_UNDO标志后,semadj才会更新。这是一个概念性的变量,它由内核为在其某个信号量操作中指定了SEM_UNDO标志的各个进程维护,不必存在名为semadj的变量。

#include int semop(int semid,struct sembuf* opsptr,size_t nops);

参数 semid:信号量标识符

参数 opsptr:struct sembuf结构体指针。

struct sembuf { short sem_num; /* 特定信号量:0,1,...nsem-1(nsem为semget创建信号量集时的第二个参数) */ short sem_op; /* 信号量操作: 0 */ short sem_flg; /* 操作标志: 0,IPC_NOWAIT,SEM_UNDO */ }

注:由内核保证传递给semop函数的操作数组(opsptr)被原子的执行。这意味着要么所有操作都被执行,要么一个操作也不执行。

参数 nops:要操作的sembuf 结构体中元素的数量。

返回值:若成功则为0,若出错则为-1。

调用进程终止时,semadj加到相应信号量的semval之上。要是调用进程对信号量的全部操作都指定SEM_UNDO标志,那么该进程终止时,该信号量的值就会变得根本没有运行过该进程一样,这就是复旧(undo)的本意。

sem_op操作说明>0

其值加到semval上。这对应释放由某个信号量控制的资源。

如果指定了SEM_UNDO标志,那就从相应信号量的semadj值减掉sem_op的值。

=0

表示调用者希望等待到semval变为0。如果已经是0,则立刻返回。

如果semval不为0,相应信号量的semzcnt值就加1,调用线程则被阻塞到semval变为0(这时,相应的信号量的semzcnt再减1)。

如果指定了IPC_NOWAIT标志,调用线程就不会被投入睡眠,而是将返回EAGAIN错误。

注:如果某个被捕获的信号中断了引起睡眠的semop函数,那么函数将返回EINTR错误;如果相应的信号量被删除了,那么函数返回EIDRM错误。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3