Win2Linux Semaphore映射

 

尽管Linux系统内核对象在实现和结构上与Windows有很大的不同。但Linux系统调用提供了和Windows的API相似的对内核对象操作的接口。在进程控制,线程管理,调度优先级,同步以及IPC的实现都存在着映射关系。以下一一列进程控制方面的映射。

在Windows中进程和线程的同步是由同步对象(Events,Semaphores,Mutexs,Critical sections)和Wait函数(WaitForSingleObject WaitForMultiObject )实现的,其中进程间的同步依赖于命名对象,而线程间的同步依赖于非命名对象。

在Linux中,实现线程同步的是pthread_mutex,pthread_rwlock,pthread_cond。实现进程同步的是System V的Semaphore。而且对不同的同步内核对象,都分别有自己阻塞、等待调用。

Semaphore

Semaphore可以称之为限制特定数目进程或者线程同时访问一个资源的计数器。Linux中所使用的System V的Semaphore(简称wsem)和Windows的Semphare(简称vsem)有几点不同:

  1. wsem使用的是单一信号量对象;而vsem使用的是信号量集(多个信号量的集合)。
  2. wsem进程间通过信号量名共享同一信号量;vsem通过key(键)共享同一信号量集。
  3. wsem创建信号量对象时初始化;而vsem的创建和初始化需要两个系统调用完成。

==================== 创建Semaphore ====================

  +--------------------------------------------------------------------------
| // lpInitialCount:初始信号量值即:并发进程(线程)限制数。
| // lMaximumCount:信号量最大值(大于零)
| // lpName:信号量名
| // NULL:未命名信号,用于线程间同步
| // 非NULL:命名信号,用于进程间同步
| HANDLE CreateSempaphore(
| LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
| LONG lInitialCount,
| LONG lMaximumCount,
| LPCSTR lpName
| );
+-------------------------------------------------------------------------+

vsem中,为了保证同步进程(线程)共享同一个信号量集。创建信号量集的key必须是所有同步进程(线程)所认可的。为了保证这一实现可以

  1. 在所有进程(线程)的公用头文件中定义此键值
  2. 通过将进程(线程)们认可的路径名和项目ID(0~255)用ftok变换成键值。
  3. 使用IPC_PRIVATE键,传递信号量集ID,此方式只能用于线程间同步或者父传递子的进程。
 +--------------------------------------------------------------------------+
| // #include <sys/ipc.h>
| key_t ftok( const char *path, int id);
+--------------------------------------------------------------------------+
| // #include <sys/sem.h>
| // flag指定了IPC_CREAT则创建新信号量集。
| // flag指定IPC_EXCL防止使用key的信号量集已经存在。若存在则出错返回。
| // key为IPC_PRIVATE则创建新信号量集。
| // 若非以上条件,且使用key的信号量集已经存在,则打开信号量集。
| // 创建信号量集时nsem指定信号量集中信号量个数。
| // 打开信号量集时nsem为0。
| // 创建或者打开信号量集,返回信号量集ID,否则返回错误
| int semget(key_t key, int nsem, int flag);
+---------------------------------------------------------------------------

vsem信号量集的创建和初始是分开的。信号量集的初始化可以通过semctl:

 +--------------------------------------------------------------------------+
| // #include <sys/sem.h>
| // semctl包含了多种信号量操作,由cmd指定。cmd可以是:
| // IPC_STAT:获取semid_ds结构,并保存于arg.buf指向的结构。
| // IPC_SET:将信号量内核结构中sem_perm.uid、sem_perm.gid、
| // sem_perm.mode设置为arg.buf->sem_perm中对应的值
| // IPC_RMID:root或者相符有效用户从系统中删除该信号量
| // GETVAL:返回第semnum个信号量结构中的semval值
| // SETVAL:将第semnum个信号量结构中的semval值设为arg.val
| // GETPID:返回第semnum个信号量结构中的sempid值
| // GETNCNT:返回第semnum个信号量结构中的semncnt值
| // GETZCNT:返回第semnum个信号量结构中的semzcnt值
| // GETALL:获取信号量集中所有信号量的semval值,放入arg.array中
| // SETALL:按arg.array的值依次设置信号量集中所能信号量的semval值
| // 此操作可用于初始化。
| int semctl(int semid, int semnum, int cmd,
| ... /*union semun arg */);
+--------------------------------------------------------------------------+
| union semun
| {
| int val; // 用于设置特定信号量的值
| struct semid_ds *buf; // 用于获取vsem的内核结构
| unsigned short *array; // 用于获取或者设置所有信号量的值
| };
+--------------------------------------------------------------------------+
| // 内核为每个vsem信号量集保存的结构。
| struct semid_ds
| {
| struct ipc_perm sem_perm; // 信号量集权限结构,原型如下
| unsigned short sem_nsems; // 信号集中的信号量数目
| time_t sem_otime; // 最后调用semop的时间
| time_t sem_ctime; // 最后改变此结构的时间
| ...
| };
+---------------------------------------------------------------------------

结构ipc_perm是System V IPC为每个IPC结构设置的规定权限和所有者的结构,大致如下:

 +--------------------------------------------------------------------------+
| struct ipc_perm
| {
| uid_t uid; // 所有者用户ID
| gid_t gid; // 所有者组ID
| uid_t cuid; // 创建者用户ID
| gid_t cgid; // 创建者组ID
| mode_t mode; // IPC权限 666:用户,组,其他读写
| }
+---------------------------------------------------------------------------

vsem中每个信号量由一个无名结构表示,大致如下:

 +--------------------------------------------------------------------------+
| struct {
| unsigned short semval; // 信号量值
| pid_t sempid; // 最后操作的pid
| unsigned short semncnt;// 等待信号量值>n的进程数。
| unsigned short semzcnt;// 等待信号量值=0的进程数。
| ...
| };
+---------------------------------------------------------------------------

==================== 打开Semaphore ====================

 +--------------------------------------------------------------------------+
| // dwDesireAccess请求的信号量HANDLE权限,若SA没有相应权限,出错返回。
| // 通过Create函数返回的信号量HANDLE具有SA設定的所有访问权限。
| // bInheritHandle为TRUE,则子进程可以继承信号量HANDLE。否则不继承。
| // lpName:命名信号量的信号量名(区分大小写)
| // 只适用于命名信号量
| HANDLE OpenSemaphore(
| DWORD dwDesiredAccess,
| BOOL bInheritHandle,
| LPCSTR lpName
| ):
+---------------------------------------------------------------------------

在vsem中,打开已经存在的信号量集,是通过semget根据已知key打开的。具体使用如上对setget的注释。

==================== 请求Semaphore ====================

在Windows中wait方法可以用来请求信号量。若当前信号量值不能满足条件,则阻塞;当信号量值满足Wait的请求时,激活Wait所在进程(线程)并将信号量值减去Wait的請求值。Wait方法包括:

  1. 請求单一信号量:
  +-------------------------------------------------------------------------+
| // 对象:控制台输入,系统事件,作业,进程,线程,信号量,互斥体,計時。
| DWORD WaitForSingleObject(
| HANDLE hObj //对象句柄。
| DWORD dwMilliseconds // 等待时间间隔,INFINITES无限,0则不等待
| );
+--------------------------------------------------------------------------
  1. 請求多个信号量:
  +-------------------------------------------------------------------------+
| // 对象:控制台输入,系统事件,作业,进程,线程,信号量,互斥体,計時。
| DWORD WaitForMultipleObjects(
| DWORD nCount, // 对旬句柄数组的长度
| CONST HANDLE *lpHandles, // 对象句柄数组
| BOOL bWaitAll, // 获得即返回(FALSE)或获取所有返回(TRUE)。
| DWORD dwMilliseconds // 时间间隔设置
| );
+--------------------------------------------------------------------------

vsem中的semop执行信号量集中的信号量值操作。可以用它請求,释放信号量。也可以等信号量值为零。

  +-------------------------------------------------------------------------+
| // #include <sys/sem.h>
| // semoparray:将多个操作捆绑形成一个原子操作。
| // nops:操作数组的操作个数。
| int semop(int semid, struct sembuf semoparray[], size_t nops);
+--------------------------------------------------------------------------

每一个操作由下面的结构体定义:

  +-------------------------------------------------------------------------+
| struct sembuf
| {
| unsigned short sem_num; // 操作的信号量标识
| short sem_op; // 操作数(可以为正数,负数和0)
| short sem_flag;// 操作标签可以是IPC_NOWAIT和SEM_UNDO
| };
+--------------------------------------------------------------------------
  • 当sem_op为正数时
    • 请求sem_num信号量值sem_op。
    • 若不能满足且没有設定IPC_NOWAIT,则阻塞等待sem_num信号量的semncnt加1。
    • 若請求成功则sem_num信号量的值减去sem_op。
  • 当sem_op为负数时
    • 释放sem_num信号量值sem_op。
    • sem_num的值加上sem_op。
  • 当sem_op为零时
    • 则请求sem_num的信号量值为零。
    • 若不满足且没有設定IPC_NOWAIT,则阻塞等待,sem_num信号量的semzcnt加1。

==================== 释放Semaphore ====================

释放信号量的值会增加对应的信号量值,当信号量增加若满足其它进程(线程)的請求条件,则激活。

  +-------------------------------------------------------------------------+
| BOOL ReleaseSemaphore(
| HANDLE hSemaphore, // 信号量对象句柄
| LONG lReleaseCount, // 释放的信号量。即信号量值的增加值
| LPLONG lpPreviousCount // 传入指针以获取释放之间的信号量值
| );
+--------------------------------------------------------------------------

vsem中的semop既可以用来請求信号量,也可以用来释放信号量。其应用如上对semop的描述。

==================== 销毁Semaphore ====================

Windows的信号量和vsem的信号量集,属于在内核中创建的对象,所以必须手动销毁。否则不会消失,除非重启系统。

  +-------------------------------------------------------------------------+
| BOOL CloseHandle(
| HANDLE hObject
| );
+--------------------------------------------------------------------------

vsem中使用semctl销毁信号量集,其应用如上对semctl的描述。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值