Linux下关于互斥锁及同步的移植(一)
来源:互联网 发布:mac邮箱软件推荐 编辑:程序博客网 时间:2024/06/06 13:06
【原文:http://jazka.blog.51cto.com/809003/234732】
讲完了前面关于多线程的基础知识后,说一下我最近关于移植的一些体会。
将win32程序关于多线程的内容移植到Linux下面,不能简单的按照函数对应来移植。不过通过下面的对应关系,再加上你对这些模式的深入了解,相信会移植的很成功。
信号量
Windows 信号量是一些计数器变量,允许有限个线程/进程访问共享资源。Linux POSIX 信号量也是一些计数器变量,可以用来在 Linux 上实现 Windows 上的信号量功能。
- 信号量的类型: Windows 提供了有名(named)信号量和无名(unnamed)信号量。有名信号量可以在进程之间进行同步。在 Linux 上,在相同进程的不同线程之间,则只使用 POSIX 信号量。在进程之间,可以使用 System V 信号量。
- 等待函数中的超时: 当在一个等待函数中使用时,可以为 Windows 信号量对象指定超时值。在 Linux 中,并没有提供这种功能,只能通过应用程序逻辑处理超时的问题。
创建信号量
在 Windows 中,可以使用
CreateSemaphore()
创建或打开一个有名或无名的信号量。HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);
在这段代码中:
lpSemaphoreAttributes
是一个指向安全性属性的指针。如果这个指针为空,那么这个信号量就不能被继承。lInitialCount
是该信号量的初始值。lMaximumCount
是该信号量的最大值,该值必须大于 0。lpName
是信号量的名称。如果该值为 NULL,那么这个信号量就只能在相同进程的不同线程之间共享。否则,就可以在不同的进程之间进行共享。
这个函数创建信号量,并返回这个信号量的句柄。它还将初始值设置为调用中指定的值。这样就可以允许有限个线程来访问某个共享资源。
在 Linux 中,可以使用
sem_init()
来创建一个无名的 POSIX 信号量,这个调用可以在相同进程的线程之间使用。它还会对信号量计数器进行初始化:int sem_init(sem_t *sem, int pshared, unsigned int value)
。在这段代码中:value
(信号量计数器)是这个信号量的初始值。pshared
可以忽略,因为在目前的实现中,POSIX 信号量还不能在进程之间进行共享。
这里要注意的是,最大值基于 demaphore.h 头文件中定义的 SEM_VALUE_MAX。
在 Linux 中,
semget()
用于创建 System V 信号量,它可以在不同集成的线程之间使用。可以用它来实现与 Windows 中有名信号量相同的功能。这个函数返回一个信号量集标识符,它与一个参数的键值关联在一起。当创建一个新信号量集时,对于与 semid_ds
数据结构关联在一起的信号量,semget()
要负责将它们进行初始化,方法如下:sem_perm.cuid
和sem_perm.uid
被设置为调用进程的有效用户 ID。sem_perm.cgid
和sem_perm.gid
被设置为调用进程的有效组 ID。sem_perm.mode
的低 9 位被设置为semflg
的低 9 位。sem_nsems
被设置为nsems
的值。sem_otime
被设置为 0。sem_ctime
被设置为当前时间。
用来创建 System V 信号量使用的代码是:
int semget(key_t key, int nsems, int semflg)
。下面是对这段代码的一些解释:key
是一个惟一的标识符,不同的进程使用它来标识这个信号量集。我们可以使用ftok()
生成一个惟一的键值。IPC_PRIVATE
是一个特殊的key_t
值;当使用IPC_PRIVATE
作为key
时,这个系统调用就会只使用semflg
的低 9 位,但却忽略其他内容,从而新创建一个信号量集(在成功时)。nsems
是这个信号量集中信号量的数量。semflg
是这个新信号量集的权限。要新创建一个信号量集,您可以将使用IPC_CREAT
来设置位操作或访问权限。如果具有该 key 值的信号量集已经存在,那么IPC_CREAT
/IPC_EXCL
标记就会失败。
注意,在 System V 信号量中,
key
被用来惟一标识信号量;在 Windows 中,信号量是使用一个名称来标识的。为了对信号量集数据结构进行初始化,可以使用
IPC_SET
命令来调用 semctl()
系统调用。将 arg.buf 所指向的 semid_ds 数据结构的某些成员的值写入信号量集数据结构中,同时更新这个结构的 sem_ctime member 的值。用户提供的这个 arg.buf 所指向的 semid_ds 结构如下所示:sem_perm.uid
sem_perm.gid
sem_perm.mode
(只有最低 9 位有效)
调用进程的有效用户 ID 应该是超级用户,或者至少应该与这个信号量集的创建者或所有者匹配:
int semctl(int semid, int semnum, int cmd = IPC_SET, ...)
。在这段代码中:semid
是信号量集的标识符。semnum
是信号量子集偏移量(从 0 到nsems
-1,其中 n 是这个信号量集中子集的个数)。这个命令会被忽略。cmd
是命令;它使用IPC_SET
来设置信号量的值。args
是这个信号量集数据结构中要通过IPC_SET
来更新的值(在这个例子中会有解释)。
最大计数器的值是根据在头文件中定义的
SEMVMX
来决定的。打开信号量
在 Windows 中,我们使用
OpenSemaphore()
来打开某个指定信号量。只有在两个进程之间共享信号量时,才需要使用信号量。在成功打开信号量之后,这个函数就会返回这个信号量的句柄,这样就可以在后续的调用中使用它了。HANDLE OpenSemaphore( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName)
在这段代码中:
dwDesiredAccess
是针对该信号量对象所请求的访问权。bInheritHandle
是用来控制这个信号量句柄是否可继承的标记。如果该值为 TRUE,那么这个句柄可以被继承。lpName
是这个信号量的名称。
在 Linux 中,可以调用相同的
semget()
来打开某个信号量,不过此时 semflg
的值为 0:int semget(key,nsems,0)
。在这段代码中:key
应该指向想要打开的信号量集的 key 值。- 为了打开一个已经存在的信号量,可以将
nsems
和标记设置为 0。semflg
值是在返回信号量集标识符之前对访问权限进行验证时设置的。
获取信号量
在 Windows 中,等待函数提供了获取同步对象的机制。可以使用的等待函数有多种类型;在这一节中,我们只考虑
WaitForSingleObject()
(其他类型将会分别进行讨论)。这个函数使用一个信号量对象的句柄作为参数,并会一直等待下去,直到其状态变为有信号状态或超时为止。DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
在这段代码中:
hHandle
是指向互斥句柄的指针。dwMilliseconds
是超时时间,以毫秒为单位。如果该值是INFINITE
,那么它阻塞调用线程/进程的时间就是不确定的。
在 Linux 中,
sem_wait()
用来获取对信号量的访问。这个函数会挂起调用线程,直到这个信号量有一个非空计数为止。然后,它可以原子地减少这个信号量计数器的值:int sem_wait(sem_t * sem)
。在 POSIX 信号量中并没有超时操作。这可以通过在一个循环中执行一个非阻塞的
sem_trywait()
实现,该函数会对超时值进行计算:int sem_trywait(sem_t * sem)
。在使用 System V 信号量时,如果通过使用
IPC_SET
命令的 semctl()
调用设置初始的值,那么必须要使用 semop()
来获取信号量。semop()
执行操作集中指定的操作,并阻塞调用线程/进程,直到信号量值为 0 或更大为止:int semop(int semid, struct sembuf *sops, unsigned nsops)
。函数
semop()
原子地执行在 sops
中所包含的操作 —— 也就是说,只有在这些操作可以同时成功执行时,这些操作才会被同时执行。sops
所指向的数组中的每个 nsops
元素都使用 struct sembuf
指定了一个要对信号量执行的操作,这个结构包括以下成员:unsigned short sem_num;
(信号量个数)short sem_op;
(信号量操作)short sem_flg;
(操作标记)
要获取信号量,可以通过将
sem_op
设置为 -1 来调用 semop()
;在使用完信号量之后,可以通过将 sem_op
设置为 1 来调用 semop()
释放信号量。通过将 sem_op
设置为 -1 来调用semop()
,信号量计数器将会减小 1,如果该值小于 0(信号量的值是不能小于 0 的),那么这个信号量就不能再减小,而是会让调用线程/进程阻塞,直到其状态变为有信号状态为止。sem_flg
中可以识别的标记是 IPC_NOWAIT
和 SEM_UNDO
。如果某一个操作被设置了SEM_UNDO
标记,那么在进程结束时,该操作将被取消。如果 sem_op
被设置为 0,那么semop()
就会等待 semval
变成 0。这是一个“等待为 0” 的操作,可以用它来获取信号量。记住,超时操作在 System V 信号量中并不适用。这可以在一个循环中使用非阻塞的
semop()
(通过将 sem_flg
设置为 IPC_NOWAIT
)实现,这会计算超时的值。释放信号量
在 Windows 中,
ReleaseSemaphore()
用来释放信号量。BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);
在这段代码中:
hSemaphore
是一个指向信号量句柄的指针。lReleaseCount
是信号量计数器,可以通过指定的数量来增加计数。lpPreviousCount
是指向上一个信号量计数器返回时的变量的指针。如果并没有请求上一个信号量计数器的值,那么这个参数可以是 NULL。
这个函数会将信号量计数器的值增加在
lReleaseCount
中指定的值上,然后将这个信号量的状态设置为有信号状态。在 Linux 中,我们使用
sem_post()
来释放信号量。这会唤醒对这个信号量进行阻塞的所有线程。信号量的计数器同时被增加 1。要为这个信号量的计数器添加指定的值(就像是 Windows 上一样),可以使用一个互斥变量多次调用以下函数:int sem_post(sem_t * sem)
。对于 System V 信号量来说,只能使用
semop()
来释放信号量:int semop(int semid, struct sembuf *sops, unsigned nsops)
。函数
semop()
原子地执行 sops
中包含的一组操作(只在所有操作都可以同时成功执行时,才会将所有的操作同时一次执行完)。sops
所指向的数组中的每个 nsops
元素都使用一个 struct sembuf
结构指定了一个要对这个信号量执行的操作,该结构包含以下元素:unsigned short sem_num;
(信号量个数)short sem_op;
(信号量操作)short sem_flg;
(操作标记)
要释放信号量,可以通过将
sem_op
设置为 1 来调用 semop()
。通过将 semop()
设置为 1 来调用 semop()
,这个信号量的计数器会增加 1,同时用信号通知这个信号量。关闭/销毁信号量
在 Windows 中,我们使用
CloseHandle()
来关闭或销毁信号量对象。BOOL CloseHandle( HANDLE hObject);
hObject
是指向这个同步对象句柄的指针。在 Linux 中,
sem_destroy()
负责销毁信号量对象,并释放它所持有的资源: int sem_destroy(sem_t *sem)
。对于 System V 信号量来说,只能使用 semctl()
函数的IPC_RMID
命令来关闭信号量集:int semctl(int semid, int semnum, int cmd = IPC_RMID, ...)
。这个命令将立即删除信号量集及其数据结构,并唤醒所有正在等待的进程(如果发生错误,则返回,并将
errno
设置为 EIDRM
)。调用进程的有效用户 ID 必须是超级用户,或者可以与该信号量集的创建者或所有者匹配的用户。参数 semnum
会被忽略。 0 0
- Linux下关于互斥锁及同步的移植(一)
- Linux下关于互斥锁及同步的移植(二)
- Linux平台下关于GCC编译及使用的方法
- linux下关于socket程序的调试(gdb)
- linux下关于网络配置(永久性的)
- Linux下关于内存的查看
- Linux下关于网络设置的文件
- Linux下关于热插拔硬盘的指令
- Linux下关于热插拔硬盘的指令
- Linux下关于NFS的配置步骤
- Linux下关于环境变量的一些心得
- Linux下关于环境变量的一些心得
- linux下关于tomcat的几个问题
- linux下关于tomcat的几个问题
- linux c 下关于semaphore的使用
- linux下关于CPU使用率的代码
- Linux下关于环境变量的一些心得
- Linux 下关于网络的几个命令
- QWidget、QMainWindow、QDialog和QFrame的区别
- swift 学习 记录
- Solr各组件之间的关系图
- 解决JavaWeb项目跨域
- 立博宣言
- Linux下关于互斥锁及同步的移植(一)
- Yii 框架 URL路径简化
- 我主沉浮,路在前方
- SharePoint 修改密码Web Part部署方案
- java maven quartz exampe 实战攻略2
- js,java超时判断
- cocos2dx-jsb 跨语言调用
- Linux下关于互斥锁及同步的移植(二)
- 日期工具类