After talking about the basics of multithreading, let's talk about some of my experiences on porting. Porting win32 program multi-thread content to Linux cannot be simply transplanted according to function correspondence. However, through the following ing, coupled with your in-depth understanding of these models, I believe it will be very successful.SemaphoresWindows semaphores are counter variables that allow limited threads/processes to access shared resources. Linux POSIX semaphores are also counter variables that can be used to implement semaphores on Windows in Linux.
- Semaphore type:Windows provides the famous named) semaphores and the unknown unnamed semaphores. A famous semaphore can be synchronized between processes. In Linux, only POSIX semaphores are used between different threads of the same process. System V semaphores can be used between processes.
- Wait for the timeout in the function:When used in a waiting function, you can specify a timeout value for the Windows semaphore object. In Linux, this function is not provided, and timeout issues can only be handled through application logic.
Create semaphoresIn Windows, you can use
CreateSemaphore()
Create or open a famous or unknown semaphore.
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName); |
In this Code:
lpSemaphoreAttributes
Is a pointer to the security attribute. If the pointer is null, the semaphore cannot be inherited.
lInitialCount
Is the initial value of the semaphore.
lMaximumCount
Is the maximum value of the semaphore. The value must be greater than 0.
lpName
Is the name of the semaphore. If the value is NULL, the semaphore can only be shared among different threads of the same process. Otherwise, they can be shared between different processes.
This function creates a semaphore and returns the handle of the semaphore. It also sets the initial value to the value specified in the call. This allows a limited number of threads to access a shared resource. In Linux, you can use
sem_init()
Create an unknown POSIX semaphore, which can be used between threads of the same process. It also initializes the semaphore counter:
int sem_init(sem_t *sem, int pshared, unsigned int value)
. In this Code:
value
Is the initial value of the semaphore.
pshared
Ignore this because POSIX semaphores cannot be shared among processes in the current implementation.
The maximum value is based on the SEM_VALUE_MAX defined in the demaphore. h header file. In Linux,
semget()
Used to create a System V semaphore, which can be used between different integrated threads. It can be used to implement the same functions as the famous semaphores in Windows. This function returns a semaphore set identifier, which is associated with the key value of a parameter. When you create a new semaphore set
semid_ds
Semaphores associated with data structures,
semget()
Initialize them as follows:
sem_perm.cuid
Andsem_perm.uid
It is set as the valid user ID of the calling process.
sem_perm.cgid
Andsem_perm.gid
Is set as the valid group ID of the calling process.
sem_perm.mode
Is setsemflg
9 bits.
sem_nsems
Setnsems
.
sem_otime
Set to 0.
sem_ctime
Is set to the current time.
The code used to create a System V semaphore is:
int semget(key_t key, int nsems, int semflg)
. The following are some explanations of this Code:
key
Is a unique identifier. Different processes use it to identify this semaphore set. We can useftok()
Generate a unique key value.IPC_PRIVATE
Is a specialkey_t
Value.IPC_PRIVATE
Askey
This system will only usesemflg
But ignore other content, so that a new semaphore set is created successfully ).
nsems
Is the number of semaphores in this semaphores set.
semflg
Is the permission of the new semaphore set. To create a semaphore set, you can useIPC_CREAT
To set bitwise operations or access permissions. If a semaphore set with this key already existsIPC_CREAT
/IPC_EXCL
The tag will fail.
Note: In the System V semaphore,
key
Used to uniquely identify the semaphores. In Windows, semaphores are identified by a name. To initialize the data structure of the semaphore set, you can use
IPC_SET
Command to call
semctl()
System Call. Write the values of some members of the semid_ds Data Structure pointed to by arg. buf to the data structure of the semaphore set, and update the value of sem_ctime member of this structure. The user-provided arg. buf points to the semid_ds structure as follows:
sem_perm.uid
sem_perm.gid
sem_perm.mode
Valid at least 9 characters)
The valid user ID of the calling process should be a superuser, or at least match with the creator or owner of the semaphore set:
int semctl(int semid, int semnum, int cmd = IPC_SET, ...)
. In this Code:
semid
Is the identifier of the semaphore set.
semnum
Is the offset of the semaphore subset from 0nsems
-1, where n is the number of subsets in the semaphore set ). This command is ignored.
cmd
Is a command; it usesIPC_SET
To set the semaphore value.
args
In the data structure of this semaphore setIPC_SET
The value to be updated will be explained in this example ).
The maximum counter value is defined according to
SEMVMX
.
Enable semaphoresIn Windows, we use
OpenSemaphore()
To open a specified semaphore. Semaphores are used only when two processes share semaphores. After the semaphore is successfully opened, the function returns the handle of the semaphore so that it can be used in subsequent calls.
HANDLE OpenSemaphore( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName) |
In this Code:
dwDesiredAccess
Is the access permission requested by the semaphore object.
bInheritHandle
Is a flag used to control whether the semaphore handle can inherit. If the value is TRUE, the handle can be inherited.
lpName
Is the name of the semaphore.
In Linux, you can call the same
semget()
To open a semaphore. However
semflg
The value is 0:
int semget(key,nsems,0)
. In this Code:
key
It should point to the key value of the semaphore set to be opened.
- To open an existing semaphore, you can
nsems
And the flag is set to 0.semflg
The value is set when the access permission is verified before the semaphore set identifier is returned.
Obtain semaphoresIn Windows, the wait function provides a mechanism for obtaining synchronization objects. There are multiple types of available wait functions. In this section, we only consider
WaitForSingleObject()
Other types will be discussed separately ). This function uses the handle of a semaphore object as a parameter and waits until its status changes to a signal state or times out.
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
In this Code:
hHandle
Is a pointer to a mutex handle.
dwMilliseconds
Is the timeout time, in milliseconds. If the value isINFINITE
The time it blocks the calling thread/process is uncertain.
In Linux,
sem_wait()
Used to obtain access to semaphores. This function suspends the calling thread until the semaphore has a non-empty count. Then, it can reduce the value of this semaphore counter atomically:
int sem_wait(sem_t * sem)
. There is no timeout operation in POSIX semaphores. This can be done by executing a non-blocking
sem_trywait()
This function calculates the timeout value:
int sem_trywait(sem_t * sem)
. When the System V semaphore is used
IPC_SET
Command
semctl()
You must use
semop()
To obtain the semaphore.
semop()
Execute the specified operation in the operation set and block the calling thread/process until the signal value is 0 or greater:
int semop(int semid, struct sembuf *sops, unsigned nsops)
. Function
semop()
Run
sops
Operations included in -- that is, these operations will be executed at the same time only when these operations can be successfully executed at the same time.
sops
Each
nsops
Use all elements
struct sembuf
Specifies an operation to be performed on the semaphore. This structure includes the following members:
unsigned short sem_num;
Number of semaphores)
short sem_op;
Semaphore Operation)
short sem_flg;
Operation tag)
To obtain the semaphore, you can
sem_op
Set to-1 to call
semop()
After using the semaphore, you can
sem_op
Set to 1 to call
semop()
Release semaphores. By adding
sem_op
Set to-1 to call
semop()
, The semaphore counter will be reduced by 1. If the value is smaller than 0, the semaphore value cannot be smaller than 0), then the semaphore cannot be reduced, but the calling thread/process will be blocked, until its status changes to a signal state.
sem_flg
Which of the following can be recognized?
IPC_NOWAIT
And
SEM_UNDO
. If an operation is set
SEM_UNDO
When the process ends, the operation is canceled. If
sem_op
Set to 0, then
semop()
Will wait
semval
To 0. This is an operation that "waits for 0" and can be used to obtain the semaphore. Remember, the timeout operation is not applicable to System V semaphores. This allows you to use non-blocking
semop()
By adding
sem_flg
Set
IPC_NOWAIT
) To calculate the timeout value.
Release semaphoresIn Windows,
ReleaseSemaphore()
Used to release semaphores.
BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount); |
In this Code:
hSemaphore
Is a pointer to the semaphore handle.
lReleaseCount
Is a semaphore counter. You can increase the count by specifying the number.
lpPreviousCount
Is the pointer to the variable returned by the previous semaphore counter. If the value of the previous semaphore counter is not requested, this parameter can be NULL.
This function will increase the semaphore counter value in
lReleaseCount
And then set the semaphore state to a signal state. In Linux, we use
sem_post()
To release semaphores. This will wake up all threads blocking the semaphore. The semaphore counter is increased by 1 at the same time. You can use a mutex variable to call the following function multiple times to add a specified value to the counter of this semaphore, just like on Windows:
int sem_post(sem_t * sem)
. For System V semaphores, only
semop()
To release semaphores:
int semop(int semid, struct sembuf *sops, unsigned nsops)
. Function
semop()
Atomic execution
sops
Only when all operations can be successfully executed at the same time can all operations be completed at the same time ).
sops
Each
nsops
Each element uses
struct sembuf
The structure specifies an operation to be performed on the semaphore. The structure contains the following elements:
unsigned short sem_num;
Number of semaphores)
short sem_op;
Semaphore Operation)
short sem_flg;
Operation tag)
To release the semaphore, you can
sem_op
Set to 1 to call
semop()
. By adding
semop()
Set to 1 to call
semop()
, The counter of this semaphore will increase by 1, and the semaphore will be notified by the signal.
Disable/destroy semaphoresIn Windows, we use
CloseHandle()
To close or destroy the semaphore object.
BOOL CloseHandle( HANDLE hObject); |
hObject
Is a pointer to the synchronization object handle. In Linux,
sem_destroy()
Destroys the semaphore object and releases its resources:
int sem_destroy(sem_t *sem)
. For System V semaphores, only
semctl()
Function
IPC_RMID
Command to close the semaphore set:
int semctl(int semid, int semnum, int cmd = IPC_RMID, ...)
. This command will immediately delete the semaphore set and its data structure, wake up all pending processes if an error occurs, then return and
errno
Set
EIDRM
). The valid user ID of the calling process must be a superuser, or a user that can match the creator or owner of the semaphore set. Parameters
semnum
Will be ignored.