Posix multithreading-advanced Thread Programming (thread and fork, exec)

Source: Internet
Author: User

When a multi-threaded process calls fork to create a sub-process, Pthreads specifies that only the thread that calls fork exists in the sub-process (indicating that only the thread that calls the sub-process is called ). Although only the calling thread exists in the subprocess when the fork call returns, all other Pthreads threads remain in the same state as the fork call. In a child process, the thread has the same status as in the parent process. It has the same mutex and the same thread private data key value. Although any thread waiting on the synchronization object does not wait when fork is called, all mutex and condition variables still exist (because other threads do not exist in the child process, so how can they wait ?).

Note: fork calls do not affect the status of mutex. If it is locked in the parent process, it is locked in the child process!

If a mutex is locked during the fork call, it is still locked in the child process. Because the mutex of a lock is owned by the thread that locks it, when the thread that locks the mutex is the thread that calls fork, the mutex can be unlocked in the child process. This is important. If you call fork and another thread locks a mutex, you will lose access to the mutex and any data controlled by the mutex.

You may need to worry about storage leakage because the thread private data destruction and clearing Handler are not called.

 

1. fork Processor

[Cpp]
Int pthread_atfork (void (* prepare) (void), void (* parent) (void), void (* child) (void ));

Int pthread_atfork (void (* prepare) (void), void (* parent) (void), void (* child) (void )); pthreads adds the pthread_atfork "fork processor" mechanism to allow your code to bypass fork calls to protect data and constants. This is a bit similar to atexit, which allows the program to clear when a process is terminated. To use pthread_atfork, you need to provide three independent processing function addresses. The prepare fork processor is called before the parent process calls fork. The parent fork processor is called within the parent process after the fork is executed. The child fork processor is called within the child process after the fork is executed.

Generally, the pthread fork processor locks some codes in the correct order and uses mutex to prevent deadlocks. The thread that calls fork will be blocked in the prepare fork processor until it locks all mutex, this ensures that other threads cannot lock a mutex or modify the data that a sub-process may need. The parent fork processor only needs to unlock all mutex to allow the parent process and all threads to continue working normally.

The child fork processor can often be the same as the parent fork processor, but sometimes the state of the program or library needs to be reset. For example, if you use a daemon thread to execute a function in the background, you must record the fact that the thread no longer exists, or create a new thread in the sub-process to execute the same function. You may need to reset the counter and release the Heap Storage.

[Cpp]
/*
* Atfork. c
*
* Demonstrate the use of "fork handlers" to protect data
* Invariants implements ss a fork.
*/
# Include <sys/types. h>
# Include <pthread. h>
# Include <sys/wait. h>
# Include "errors. h"
 
Pid_t self_pid;/* pid of current process */
Pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
/*
* This routine will be called prior to executing the fork,
* Within the parent process.
*/
Void fork_prepare (void)
{
Int status;
 
/*
* Lock the mutex in the parent before creating the child,
* To ensure that no other thread can lock it (or change any
* Associated shared state) until after the fork completes.
*/
Status = pthread_mutex_lock (& mutex );
If (status! = 0)
Err_abort (status, "Lock in prepare handler ");
Printf ("fork_prepare \ n ");
}
 
/*
* This routine will be called after executing the fork,
* The parent process
*/
Void fork_parent (void)
{
Int status;
 
/*
* Unlock the mutex in the parent after the child has been created.
*/
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock in parent handler ");
Printf ("fork_parent \ n ");
}
 
/*
* This routine will be called after executing the fork,
* The child process
*/
Void fork_child (void)
{
Int status;
 
/*
* Update the file scope "self_pid" within the child process, and unlock
* The mutex.
*/
Self_pid = getpid ();
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock in child handler ");
Printf ("fork_child: self_pid = % d \ n", self_pid );
}
 
/*
* Thread start routine, which will fork a new child process.
*/
Void * thread_routine (void * arg)
{
Pid_t child_pid;
Int status;
 
Child_pid = fork ();
If (child_pid = (pid_t)-1)
Errno_abort ("Fork ");
 
/*
* Lock the mutex -- without the atfork handlers, the mutex will remain
* Locked in the child process and this lock attempt will hang (or fail
* With EDEADLK) in the child.
*/
Status = pthread_mutex_lock (& mutex );
If (status! = 0)
Err_abort (status, "Lock in child ");
Printf ("After fork \ n ");
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock in child ");
Printf ("After fork: % d (% d) \ n", child_pid, self_pid );
If (child_pid! = 0 ){
If (pid_t)-1 = waitpid (child_pid, (int *) 0, 0 ))
Errno_abort ("Wait for child ");
}
Return NULL;
}
 
Int main (int argc, char * argv [])
{
Pthread_t fork_thread;
Int atfork_flag = 1;
Int status;
 
If (argc> 1)
Atfork_flag = atoi (argv [1]);
If (atfork_flag ){
Status = pthread_atfork (fork_prepare, fork_parent, fork_child );
If (status! = 0)
Err_abort (status, "Register fork handlers ");
}
Self_pid = getpid ();
Printf ("main self_pid = % d \ n", self_pid );
Status = pthread_mutex_lock (& mutex );
If (status! = 0)
Err_abort (status, "Lock mutex ");
/*
* Create a thread while the mutex is locked. It will fork a process,
* Which (without atfork handlers) will run with the mutex locked.
*/
Status = pthread_create (& fork_thread, NULL, thread_routine, NULL );
If (status! = 0)
Err_abort (status, "Create thread ");
Printf ("before sleep \ n ");
Sleep (5 );
Printf ("after sleep \ n ");
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock mutex ");
Printf ("main unlock \ n ");
Status = pthread_join (fork_thread, NULL );
If (status! = 0)
Err_abort (status, "Join thread ");
Printf ("huangcheng \ n ");
Return 0;
}

/*
* Atfork. c
*
* Demonstrate the use of "fork handlers" to protect data
* Invariants implements ss a fork.
*/
# Include <sys/types. h>
# Include <pthread. h>
# Include <sys/wait. h>
# Include "errors. h"

Pid_t self_pid;/* pid of current process */
Pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

/*
* This routine will be called prior to executing the fork,
* Within the parent process.
*/
Void fork_prepare (void)
{
Int status;

/*
* Lock the mutex in the parent before creating the child,
* To ensure that no other thread can lock it (or change any
* Associated shared state) until after the fork completes.
*/
Status = pthread_mutex_lock (& mutex );
If (status! = 0)
Err_abort (status, "Lock in prepare handler ");
Printf ("fork_prepare \ n ");
}

/*
* This routine will be called after executing the fork,
* The parent process
*/
Void fork_parent (void)
{
Int status;

/*
* Unlock the mutex in the parent after the child has been created.
*/
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock in parent handler ");
Printf ("fork_parent \ n ");
}

/*
* This routine will be called after executing the fork,
* The child process
*/
Void fork_child (void)
{
Int status;

/*
* Update the file scope "self_pid" within the child process, and unlock
* The mutex.
*/
Self_pid = getpid ();
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock in child handler ");
Printf ("fork_child: self_pid = % d \ n", self_pid );
}

/*
* Thread start routine, which will fork a new child process.
*/
Void * thread_routine (void * arg)
{
Pid_t child_pid;
Int status;

Child_pid = fork ();
If (child_pid = (pid_t)-1)
Errno_abort ("Fork ");

/*
* Lock the mutex -- without the atfork handlers, the mutex will remain
* Locked in the child process and this lock attempt will hang (or fail
* With EDEADLK) in the child.
*/
Status = pthread_mutex_lock (& mutex );
If (status! = 0)
Err_abort (status, "Lock in child ");
Printf ("After fork \ n ");
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock in child ");
Printf ("After fork: % d (% d) \ n", child_pid, self_pid );
If (child_pid! = 0 ){
If (pid_t)-1 = waitpid (child_pid, (int *) 0, 0 ))
Errno_abort ("Wait for child ");
}
Return NULL;
}

Int main (int argc, char * argv [])
{
Pthread_t fork_thread;
Int atfork_flag = 1;
Int status;

If (argc> 1)
Atfork_flag = atoi (argv [1]);
If (atfork_flag ){
Status = pthread_atfork (fork_prepare, fork_parent, fork_child );
If (status! = 0)
Err_abort (status, "Register fork handlers ");
}
Self_pid = getpid ();
Printf ("main self_pid = % d \ n", self_pid );
Status = pthread_mutex_lock (& mutex );
If (status! = 0)
Err_abort (status, "Lock mutex ");
/*
* Create a thread while the mutex is locked. It will fork a process,
* Which (without atfork handlers) will run with the mutex locked.
*/
Status = pthread_create (& fork_thread, NULL, thread_routine, NULL );
If (status! = 0)
Err_abort (status, "Create thread ");
Printf ("before sleep \ n ");
Sleep (5 );
Printf ("after sleep \ n ");
Status = pthread_mutex_unlock (& mutex );
If (status! = 0)
Err_abort (status, "Unlock mutex ");
Printf ("main unlock \ n ");
Status = pthread_join (fork_thread, NULL );
If (status! = 0)
Err_abort (status, "Join thread ");
Printf ("huangcheng \ n ");
Return 0;
} 19 ~ The 32 function fork_prepare is the prepare processor. Before creating a child process, it will be called by fork in the parent process. Any state changed by this function (especially the locked mutex) will be copied to the sub-process. The fork_prepare function locks the mutex of a program.
38 ~ The fork_parent function is a parent processor. After a child process is created, it is called by fork in the parent process. In general, a parent processor should cancel the processing in the parent processor so that the parent process can continue normally. The fork_parent function unlocks the mutex locked by fork_prepare.
55 ~ The 68 function fork_child is a child processor. It will be called by fork in the sub-process. In most cases, the child processor needs to perform the processing performed on the fork_parent processor to unlock the status so that the child process can continue to run. It may also need to perform additional cleanup operations. For example, fork_child locks the self_pid variable as the pid of the sub-process and unlocks the process mutex.
73 ~ 100 after a child process is created, it will continue to execute the thread_routine code, and the thread_routine function will unlock the mutex. When the fork processor is running, the fork call will be blocked (when the prepare processor locks the mutex) until the mutex is available. Without the fork processor, the thread will call fork before the primary function unlocks the mutex, And the thread will be suspended in the child process at this point.
117 ~ 130 The main program locks the mutex before creating a thread that will call fork. It then sleeps for several seconds to ensure that when the mutex is locked, the thread can call fork and then unlock the mutex. The thread that uses pthread_routine will always succeed in the parent process, because it will simply block until the master program releases the lock.
Running result:

Main self_pid = 5564
Before sleep
After sleep
Main unlock
Fork_prepare
Fork_parent
After fork
After fork: 5568 (5564)
Fork_child: self_pid = 5568
After fork
After fork: 0 (5568)
Huangcheng

2. exec

The exec function is not significantly affected by the introduced thread. The exec function is used to remove the current program environment and replace it with a new program. The exec call will soon terminate all threads in the process except the threads that call exec. They do not run the clear processor or thread private data destructors -- the thread simply stops being present.

All synchronization objects also disappear, except for the pshared mutex (the mutex created using the PTHREAD_PROCESS_SHARED attribute value) and pshared condition variables. As long as the shared memory is mapped by some processes, the latter is still useful. However, you should unlock any pshared mutex that the current process may lock-the system will not unlock them for you.

3. process ended

In a non-threaded program, the display call of the exit function has the same effect as that returned from the main function of the program. This means that the process exits. Pthreads adds the pthread_exit function, which can cause the exit of a single thread while the process continues to run.

In a multi-threaded program, the main function is the "Start function of the main thread of the process ". Although the return from the startup function of any other thread is like calling pthread_exit to terminate the thread, the return from the main function will terminate the whole process. All memory (and threads) related to the process will disappear. The thread does not execute the destructors function to clear the processor or private data of the thread. Calling exit has the same effect.

When you do not want to start

 

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.