Process Synchronization events of shared objects on Linux

Source: Internet
Author: User
Process Synchronization events of shared objects on Linux-Linux general technology-Linux programming and kernel information. The following is a detailed description. In an object-oriented system, when an object receives a message, a series of events may occur. Generally, these events are processed in synchronous mode: The Calling process or the thread that sends a message to this object receives and processes a series of events before the message call is completed. However, if the objects that generate these events are shared by multiple processes and saved in the shared memory, the situation is slightly different.

This article will introduce this situation in detail using two C ++ design modes, and use some sample code to demonstrate this solution (these sample code can be obtained from the download section of this article ):

We will briefly introduce the sample code that does not use shared memory.
Use the first design mode to modify the code and use the shared memory.
The second design mode is used to implement inter-process communication (IPC ).
You can apply the concepts in these two design patterns on any machine architecture, operating system, or compiler. For this article, we use RedHat Linux 7.1 for 32-bit x86 Intel®The release version of the architecture. Use the gnu c ++ compiler version 3.2.3 compiler and related tools to compile and test the sample program.

Do not use shared memory

Let's start with the example program. The first is a program that does not use shared memory:


Listing 1. common. h

# Ifndef _ COMMON_H __
# Define _ COMMON_H __

Class IObjectWithEvents
{
Public:
Class IEventSink
{
Public:
Virtual void OnEvent (pid_t pid, const char * msg) = 0;
};

Static IObjectWithEvents * getInstance ();

Virtual bool AddEventHandler (IEventSink * pEI) = 0;
Virtual void SendMessage () = 0;
};

# Endif/_ COMMON_H __




The Interface Class IObjectWithEvents contains an embedded interface class IEventSink, which defines the OnEvent () method. This event handler receives a sender's id and a string message. The getInstance () method returns a reference to an object in the shared memory. AddEventHandler () registers an event handler and SendMessage () sends a message to this object. Because shared memory does not need to be referenced, you can use IObjectWithEvents as in Listing 2:


Listing 2. shm-client1.cpp

# Include
# Include
# Include

# Include "common. h"

# Define HERE _ FILE _ <":" <_ LINE _ <""

Using namespace std;

Class EventSink: public IObjectWithEvents: IEventSink
{
Public:
Void OnEvent (pid_t pid, const char * msg)
{
Cout <HERE <"Message from pid (" <pid <") \ t:" <msg <endl;
}
};

Int main ()
{
IObjectWithEvents * powe = IObjectWithEvents: getInstance ();

EventSink sink;
Powe-> AddEventHandler (& sink );

Powe-> SendMessage ();
Return 0;
}




EventSink class provides the implementation of this event handler. The main function provides a standard sequence for sending messages and processing events.

Typical implementations of ObjectWithEvents are shown in listing 3 and 4:


Listing 3. ObjectWithEvents. h

# Include "common. h"

Class ObjectWithEvents: public IObjectWithEvents
{
Public:
// We assume singleton design pattern for registration
Static ObjectWithEvents * ms_pObjectWithEvents;

ObjectWithEvents ();

// The implementation for IObjectWithEvents
Void FireEvent ();
Virtual bool AddEventHandler (IEventSink * pEI );
Virtual void SendMessage ();

// Collection for maintaining events
Enum {MAX_EVENT_HANDLERS = 16 ,};
Long m_npEI;
IEventSink * m_apEI [MAX_EVENT_HANDLERS];
Pid_t m_alPID [MAX_EVENT_HANDLERS];
};


Listing 4. ObjectWithEvents. cpp



# Include
# Include
# Include
# Include
# Include

# Include "ObjectWithEvents. h"

Using namespace std;

ObjectWithEvents * ObjectWithEvents: ms_pObjectWithEvents = NULL;

IObjectWithEvents * IObjectWithEvents: getInstance ()
{
// The following commented code is for authentication only.
/*
If (NULL = ObjectWithEvents: ms_pObjectWithEvents)
{
ObjectWithEvents: ms_pObjectWithEvents = new ObjectWithEvents ();
}
*/

Return ObjectWithEvents: ms_pObjectWithEvents;
}

ObjectWithEvents: ObjectWithEvents (): m_npEI (0)
{
}

Void ObjectWithEvents: FireEvent ()
{
// Iterate through the collection
For (long I = 0; I <m_npEI; I ++)
{
// Recheck for NULL
If (0! = M_apEI )
{
// Fire the event
M_apEI-> OnEvent (m_alPID,"");
}
}

Return;
}

Bool ObjectWithEvents: AddEventHandler (IEventSink * pEI)
{
// NULL check
If (NULL = pEI)
{
Return false;
}

// Check if there is space for this event handler
If (MAX_EVENT_HANDLERS = m_npEI)
{
Return false;
}

// Add this event handler to the collection
M_alPID [m_npEI] = getpid ();
M_apEI [m_npEI ++] = pEI;

Return true;
}

Void ObjectWithEvents: SendMessage ()
{
// Some processing
// And then fire the event

FireEvent ();

Return;
}




The code in Listing 4 can be compiled using the following script:

G ++-g-o shm_client shm_client1.cpp ObjectWithEvents. cpp

When running shm_client, you can see the following output:

$./Shm_client shm_client1.cpp: 16 Message from pid (3920 ):




Use shared memory: No event Cache

Now, to instantiate ObjectWithEvents in the shared memory, we need to modify the implementation of ObjectWithEvents.


Listing 5. Modifying ObjectWithEvents. cpp

// To add a declaration for the "new" operator:

Class ObjectWithEvents: public IObjectWithEvents
{
Public:
Void * operator new (unsigned int );
};


// To include an additional header for the Initializer class:

# Include "Initializer. h"


// To overload the operator "new ":

Void * ObjectWithEvents: operator new (unsigned int)
{
Return ms_pObjectWithEvents;
}


// Then, FireEvent is completely changed:

Void ObjectWithEvents: FireEvent ()
{
// We need to serialize all access to the collection by more than one process
Int iRetVal = Initializer: LockMutex ();

If (0! = IRetVal)
{
Return;
}

Pid_t pid = getpid ();

// Iterate through the collection and fire only events belonging to the current process
For (long I = 0; I <m_npEI; I ++)
{
// Check whether the handler belongs to the current process.
If (pid! = M_alPID)
{
Continue;
}

// Recheck for NULL
If (0! = M_apEI)
{
M_apEI-> OnEvent (pid ,"");
}
}

// Release the mutex
If (0 = iRetVal) & (0! = Initializer: UnlockMutex ()))
{
// Deal with error.
}

Return;
}


// The following are changes to ObjectWithEvents: AddEventHandler ():

// 1. Before accessing the collection, we lock the mutex:

Int bRetVal = Initializer: LockMutex ();

If (0! = BRetVal)
{
Return false;
}

// 2. After accessing the collection, we release the mutex:

If (0 = bRetVal) & (0! = Initializer: UnlockMutex ()))
{
// Deal with error.
}




To instantiate objects in the shared memory, define another class Initializer.


Listing 6. Initializer. h

# Ifndef _ Initializer_H __
# Define _ Initializer_H __

Class Initializer
{
Public:
Int m_shmid;
Static Initializer ms_Initializer;
Initializer ();

Static pthread_mutex_t ms_mutex;
Static int LockMutex ();
Static int UnlockMutex ();
};

# Endif/_ Initializer_H __




Initializer defines the shared memory id m_shmid and a semaphore ms_mutex used to process synchronization events.

The LockMutex () function locks the mutex, and UnlockMutex () locks the mutex.

The implementation of Initializer is shown in listing 7:


Listing 7. Initializer. cpp

# Include
# Include
# Include
# Include
# Include

# Include "Initializer. h"
# Include "ObjectWithEvents. h"

Using namespace std;

Initializer: ms_Initializer;

Pthread_mutex_t Initializer: ms_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

Initializer: Initializer (): m_shmid (-1)
{
Bool bCreated = false;
Key_t key = 0x1234;

M_shmid = shmget (key, sizeof (ObjectWithEvents), 0666 );

If (-1 = m_shmid)
{
If (ENOENT! = Errno)
{
Cerr <"Critical Error" < Return;
}

M_shmid = shmget (key, sizeof (ObjectWithEvents), IPC_CREAT | 0666 );

If (-1 = m_shmid)
{
Cout <"Critical Error" <errno <endl;
Return;
}

BCreated = true;
}


ObjectWithEvents: ms_pObjectWithEvents = (ObjectWithEvents *) shmat (m_shmid, NULL, 0 );

If (NULL = ObjectWithEvents: ms_pObjectWithEvents)
{
Cout <"Critical Error" <errno <endl;
Return;
}

If (true = bCreated)
{
ObjectWithEvents * p = new ObjectWithEvents ();
}

// Create a mutex with no initial owner.


Pthread_mutex_init (& ms_mutex, NULL );


}

Int Initializer: LockMutex ()
{
// Request ownership of mutex.

Pthread_mutex_lock (& ms_mutex );

If (EDEADLK = errno)
{
Cout <"DeadLock" <endl;
Return-1;
}

Return 0;
}

Int Initializer: UnlockMutex ()
{
Return pthread_mutex_unlock (& ms_mutex );
}


If the shared memory does not exist, create the shared memory and create an object in it. If the shared memory already exists, skip object construction. Initializer: m_shmid records this identifier. ObjectWithEvents: ms_pObjectWithEvents records references to this shared object.

This shared memory will not be destroyed even after all processes are detached from it. In this way, you can use ipcrm to explicitly delete or use the ipcs command for viewing. The compilation method of the test program is as follows:

G ++-g-o shm_client shm_client1.cpp ObjectWithEvents. cpp Initializer. cpp

The result of running this program on the console is as follows:


Listing 8. Console results

$./Shm_client
Shm_client1.cpp: 16 Message from pid (4332 ):

$ Ipcs
------ Shared Memory Segments --------
Key shmid owner perms bytes nattch status
0x00001234 327686 running in 666 136 0

$./Shm_client
Shm_client1.cpp: 16 Message from pid (4333 ):

$ Ipcrm-m 327686




The ObjectWithEvents instance collects events from various processes. It can only release events registered by the current process. This design pattern describes two points:

Any access to a group of events is protected by a mutex.
Filter by process ID before an event is sent.



Shared Memory and event cache for IPC

Now let's take a look at how to use the shared memory and event cache for inter-process communication. If events are cached in shared objects, they may be issued later. The receiving process must query the events of the shared object. Therefore, communication between processes can be achieved by using a Synchronization Model. This is the motivation for developing the following design patterns.

Add two methods to IObjectWithEvents:


Listing 9. Add methods to IObjectWithEvents

Class IObjectWithEvents
{
Public:
Virtual bool EnqueueEvent (const char * msg) = 0;
Virtual bool PollForEvents () = 0;
};




EnqueueEvent () simply adds the event cache to the shared object. PollForEvents () Searches the cached data.

Shm_client1 will use the EnqueueEvent () method, as shown below:

Powe-> EnqueueEvent ("Message from shm_client1 ");

Shm_client2 (actually a copy of shm_client1) will use the PollForEvents () method, as shown below:

Powe-> EnqueueEvent ("Message from shm_client2"); powe-> PollForEvents ();

In addition, we add something to ObjectWithEvents, as shown below:


Listing 10. ObjectWithEvents Modification

Class ObjectWithEvents: public IObjectWithEvents
{
Public:
Virtual bool EnqueueEvent (const char * msg );
Virtual bool PollForEvents ();

// The event cache
Enum {MAX_EVENTS = 16, MAX_EVENT_MSG = 256 ,};
Long m_nEvents;
Pid_t m_alPIDEvents [MAX_EVENTS];
Char m_aaMsgs [MAX_EVENTS] [MAX_EVENT_MSG];
};




These generate the new constructor:

ObjectWithEvents: ObjectWithEvents (): m_npEI (0), m_nEvents (0 ){}

EnqueueEvent () adds an event (such as the message and process id of each event) to a queue. PollForEvents () traverses this queue and calls OnEvent () for events in the queue one by one ().


Listing 11. EnqueueEvent

Bool ObjectWithEvents: EnqueueEvent (const char * msg)
{
If (NULL = msg)
{
Return false;
}

If (MAX_EVENTS = m_nEvents)
{
// IEventSink collection full
Return false;
}

Int bRetVal = Initializer: LockMutex ();

If (0! = BRetVal)
{
Return false;
}

M_alPIDEvents [m_nEvents] = getpid ();
Strncpy (m_aaMsgs [m_nEvents ++], msg, MAX_EVENT_MSG-1 );

If (0 = bRetVal) & (0! = Initializer: UnlockMutex ()))
{
// Deal with error.
}

Return true;
}

Bool ObjectWithEvents: PollForEvents ()
{
If (0 = m_nEvents)
{
Return true;
}

Int bRetVal = Initializer: LockMutex ();

If (0! = BRetVal)
{
Return false;
}

Pid_t pid = getpid ();

For (long I = 0; I <m_npEI; I ++)
{
// Does the handler belongs to current process?

If (pid! = M_alPID )
{
Continue;
}

// Recheck for NULL

If (0 = m_apEI)
{
Continue;
}

For (long j = 0; j <m_nEvents; j ++)
{
M_apEI-> OnEvent (m_alPIDEvents [j], m_aaMsgs [j]);
}
}

If (0 = bRetVal) & (0! = Initializer: UnlockMutex ()))
{
// Deal with error.
}

Return true;
}




Now try to run the compilation script:

G ++-g-o shm_client1 shm_client1.cpp ObjectWithEvents. cpp Initializer. cpp g ++-g-o shm_client2 shm_client2.cpp ObjectWithEvents. cpp Initializer. cpp

The output on the console should be as follows:


Listing 12. Output of shm_client1 and shm_client2

$./Shm_client1

$./Ipcs

------ Shared Memory Segments --------
Key shmid owner perms bytes nattch status
0x00001234 360454 running in 666 4300 0

$./Shm_client2
Shm_client2.cpp: 16 Message from pid (4454): Message from shm_client1
Shm_client2.cpp: 16 Message from pid (4456): Message from shm_client2

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.