Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 120 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
120
Dung lượng
1,5 MB
Nội dung
Creating and Managing Processes 933 Alternatively we can get, manipulate, and set a stat object as returned by the stat method in the same manner as IPC::Msg objects. Name Action stat Generate an IPC::Semaphore::stat object we can manipulate and then use with set. For example: $semstat = $sem->stat; $semstat->mode(0722); $sem->set($semstat); getpid Return the process ID of the last process to perform a semop operation on the semaphore set. getncnt Return the number of processes that have executed a semop and are blocked waiting for the value of the specified semaphore to increase in value. $ncnt = $sem->getncnt; getzcnt Return the number of processes that have executed a semop and are blocked waiting for the value of the specified semaphore to become zero. The real power of semaphores is bound up in the op method, which performs one or more semaphore operations on a semaphore set. This is the mechanism by which processes can block and be unblocked by other processes. Each operation consists of three values; the semaphore number to operate on, an operation to perform, and a flag value. The operation is actually a value to increment or decrement by, and follows these rules: ❑ If the value is positive, the semaphore value is incremented by the supplied value. This always succeeds, and never blocks. ❑ If the supplied value is zero, and the semaphore value is zero, the operation succeeds. If the semaphore value is not zero then the operation blocks until the semaphore value becomes zero. This increases the value returned by getzcnt. ❑ If the value is negative, then the semaphore value is decremented by this value, unless this would take the value of the semaphore negative. In this case, the operation blocks until the semaphore becomes sufficiently positive enough to allow the decrement to happen. This increases the value returned by getncnt. We can choose to operate as many semaphores as we like. All operations must be able to complete before the operation as a whole can succeed. For example: $sem->op( 0, -1, 0, # decrement semaphore 1 1, -1, 0, # decrement semaphore 2 3,0,0 #semaphore 3 must be zero ); TEAMFLY Team-Fly ® Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Chapter 22 934 The rules for blocking on semaphores allow us to create applications that can cooperate with each other; one application can control the execution of another by setting semaphore values. Applications can also coordinate over access to shared resources. This a potentially large subject, so we will just give a simple illustrative example of how a semaphore can coordinate access to a common shared resource: Application 1 creates a semaphore set with one semaphore, value 1, and creates a shared resource, for example a file, or an IPC shared memory segment. However, it decides to do a lot of initialization and so doesn't access the resource immediately. Application 2 starts up, decrements the semaphore to 0, and accesses the shared resource. Application 1 now tries to decrement the semaphore and access the resource. The semaphore is zero, so it cannot access, it therefore blocks. Application 2 finishes with the shared resource and increments the semaphore, an operation that always succeeds. Application 1 can now decrement the semaphore since it is now 1, and so the operation succeeds and no longer blocks. Application 2 tries to access the resource a second time. First it tries to decrement the semaphore, but is unable to, and blocks. Application 1 finishes and increments the semaphore. Application 2 decrements the semaphore and accesses the resource. and so on. Although this sounds complex, in reality it is very simple. In code, each application simply accesses the semaphore, creating it if isnot present, and then adds two lines around all accesses to the resource to be protected: sub access_resource { # decrement semaphore, blocking if it is already zero $sem->op(0, -1, 0); access resource # increment semaphore, allowing access by other processes $sem->op(0, 1, 0); } If we have more than one resource to control, we just create a semaphore set with more semaphores. The basis of this approach is that the applications agree to cooperate through the semaphore. Each one has the key for the semaphore (because it is given in a configuration file, for instance), and it becomes the sole basis for contact between them. Even though the resource being controlled has no direct connection to the semaphore, each application always honors it before accessing it. The semaphore becomes a gatekeeper, allowing only one application access at a time. If we do not want to block while waiting for a semaphore we can specify IPC_NOWAIT for the flag value. We can do this on a 'per semaphore basis' too, if we want, though this could be confusing. For example: Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Creating and Managing Processes 935 sub access_resource { return undef unless $sem->op(0, -1, IPC_NOWAIT); access resource $sem->op(0, 1, 0); } We can also set the flag SEM_UNDO (if we import it from IPC::SysV first). This causes a semaphore operation to be automatically undone if the process exits, either deliberately or due to an error. This is helpful in preventing applications that abort while locking a resource and then never releasing it again. For example: $sem->op(0, -1, IPC_NOWAIT | SEM_UNDO); die unless critical_subroutine(); As with message queues, care must be not to leave unused segments around after the last process exits. We will return to the subject of semaphores when we come to talk about threads, which have their own semaphore mechanism, inspired greatly by the original IPC implementation described above. Shared Memory Segments While message queues and semaphores are relatively low level constructs made a little more accessible by the IPC::Msg and IPC::Semaphore modules, shared memory has an altogether more powerful support module in the form of IPC::Shareable. The key reason for this is that IPC::Shareable implements shared memory through a tie mechanism, so rather than reading and writing from a memory block we can simply attach a variable to it and use that. The tie takes four arguments, a variable (which may be a scalar, an array or a hash), IPC::Shareable for the binding, and then an access key, followed optionally by a hash reference containing one or more key-value pairs. For example, the following code creates and ties a hash variable to a shared memory segment: use IPC::Shareable; %local_hash; tie %local_hash, 'IPC::Shareable', 'key', {create => 1} $local_hash{'hashkey'} = 'value'; This creates a persistent shared memory object containing a hash variable which can be accessed by any application or process by tie-ing a hash variable to the access key for the shared memory segment (in this case key): # in a process in an application far, far away %other_hash; tie %other_hash, 'IPC::Shareable', 'key'; $value = $other_hash{'hashkey'}; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Chapter 22 936 A key feature of shared memory is that, like memory queues and semaphores, the shared memory segment exists independently of the application that created it. Even if all the users of the shared memory exit, it will continue to exist so long as it is not explicitly deleted (we can alter this behavior, though, as we will see in a moment). Note that the key value is actually implemented as an integer, the same as semaphores and message queues, so the string we pass is converted into an integer value by packing the first four characters into a 32 bit integer value. This means that only the first four characters of the key are used. As a simple example, baby and babyface are the same key to IPC::Shareable. The tied variable can be of any type, including a scalar containing a reference, in which case whatever the reference points to is converted into a shared form. This includes nested data structures and objects, making shared memory ties potentially very powerful. However, each reference becomes a new shared memory object, so a complex structure can quickly exceed the system limit on shared memory segments. In practice we should only try to tie relatively small nested structures to avoid trouble. The fourth argument can contain a number of different configuration options that determine how the shared memory segment is accessed: Option Function create If true, create the key if it does not already exist. If the key does exist, then the tie succeeds and binds to the existing data, unless exclusive is also true. If create is false or not given then the tie will fail if the key is not present. exclusive Used in conjunction with create. If true, allows a new key to be created but does not allow an existing key to be tied to successfully. mode Determine the access permissions of the shared memory segment. The value is an integer, traditionally an octal number or a combination of flags like S_IRWXU |S_IRGRP. destroy If true, cause the shared memory segment to be destroyed automatically when this process exits (but not if it aborts on a signal). In general only the creating application should do this, or be able to do this (by setting the permissions appropriately on creation). size Define the size of the shared memory segment, in bytes. In general this defaults to an internally set maximum value, so we rarely need to use it. key If the tie is given three arguments, with the reference to the configuration options being the third, this value specifies the name of the shared memory segment: tie %hash, 'IPC::Shareable' {key => 'key', }; For example: tie @array, 'IPC::Shareable', 'mysharedmem', { create => 1, exclusive => 0, mode => 722, destroy => 1, } Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Creating and Managing Processes 937 Other than the destroy option, we can remove a shared memory segment by calling one of three methods implemented for the IPC::Shareable object that implements the tie (which may be returned by the tied function): Option Function remove Remove the shared memory segment, if we have permission to do so. clean_up Remove all shared memory segments created by this process. clean_up_all Remove all shared memory segments in existence for which this process has permissions to do so. For example: # grab a handle to the tied object via the 'tied' command $shmem = tied $shared_scalar; # use the object handle to call the 'remove' method on it print "Removed shared scalar" if $shmem->remove; We can also lock variables using the IPC::Shareable object's shlock and shunlock methods. If the variable is already locked, the process attempting the lock will block until it becomes free. For example: $shmem->lock; $shared_scalar = "new value"; $shmem->unlock; Behind the scenes this lock is implemented with IPC::Semaphore, so for a more flexible mechanism, use IPC::Semaphore objects directly. As a lightweight alternative to IPC::Shareable, we can make use of the IPC::ShareLite module, naturally available from CPAN. This provides a simple store-fetch mechanism using object methods and does not provide a tied interface. However, it is faster than IPC::Shareable. Threads Threads are, very loosely speaking, the low fat and lean version of forked processes. The trouble with fork is that it not only divides the thread of execution into two processes, it divides their code and data too. This means that where we had a group containing the Perl interpreter, %ENV hash, @ARGV array, and the set of loaded modules and subroutines, we now have two groups. In theory, this is wasteful of resources, very inconvenient, and unworkable for large numbers of processes. Additionally, since processes do not share data they must use constructs like pipes, shared memory segments or signals to communicate with each other. In practice, most modern UNIX systems have has become very intelligent in sharing executable segments behind the scenes, reducing the expense of a fork. Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Chapter 22 938 Regardless, threads attempt to solve all these problems. Like forked processes, each thread is a separate thread of execution. Also similar to forked processes, newly created threads are owned by the thread that created them, and they have unique identifiers, though these are thread IDs rather than process IDs. We can even wait for a thread to finish and collect its exit result, just like waitpid does for child processes. However, threads all run in the same process and share the same interpreter, code, and data, nothing is duplicated except the thread of execution. This makes them much more lightweight, so we can have very many of them, and we don't need to use any of the workarounds that forked processes need. Thread support in Perl is still experimental. Added in Perl 5.6, work continues into Perl 5.7 and Perl 6. As shipped, most Perl interpreters do not support threads at all, but if we build Perl from scratch, as in Chapter 1, we can enable them. There are actually two types of thread; the current implementation provides for a threaded application but with only one interpreter dividing its attention between them. The newer implementation uses an interpreter that is itself threaded, greatly improving performance. However, this thread support is so new it isn't even available in Perl code yet. The likelihood is that full official thread support will arrive with Perl 6, but that doesn't mean we can't use it now – carefully – for some useful and interesting applications. Checking for Thread Support To find out if threads are available programmatically we can check for the usethreads key in the %Config hash: BEGIN { use Config; die "Threadbare! \n" unless $Config{'usethreads'}; } From the command line we can check if threads are present by trying to read the documentation, the Thread module will only be present if thread support is enabled: > perldoc Thread The Thread module is the basis of handling threads in Perl. It is an object-oriented module that represents threads as objects, which we can create using new and manipulate with methods. In addition it provides a number of functions that operate on a per-thread basis. Creating Threads Threads resemble forked processes in many ways, but in terms of creation they resemble and indeed are subroutines. The standard way of creating a thread, the new method, takes a subroutine reference and a list of arguments to pass to it. The subroutine is then executed by a new thread of execution, while the parent thread receives a thread object as the return value. The following code snippet illustrates how it works: use Thread; sub threadsub { $self = Thread->self; } $thread = new Thread \&threadsub, @args; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Creating and Managing Processes 939 This creates a new thread, with threadsub as its entry point, while the main thread continues on. The alternative way to create a thread is with the async function, whose syntax is analogous to an anonymous subroutine. This function is not imported by default so we must name it in the import list to the Thread module: use Thread qw(async); $thread = async { $self = Thread->self; }; Just like an anonymous subroutine, we end the async statement with a semicolon; the block may not need it, but the statement does. The choice of new or async depends on the nature of the thread we want to start; the two are identical in all respects apart from their syntax. If we only want to start one instance of a thread then async should be used whereas new is better if we want to use the same subroutine for many different threads: $thread1 = new Thread \&threadsub, $arg1; $thread2 = new Thread \&threadsub, $arg2; $thread3 = new Thread \&threadsub, $arg3; Or, with a loop: # start a new thread for each argument passed in @ARGV: @threads; foreach (@ARGV) { push @threads, new Thread \&threadsub, $_; } We can do this with a certain level of impunity because threads are so much less resource consumptive than forked processes. Identifying Threads Since we can start up many different threads all with the same subroutine as their entry point, it might seem tricky to tell them apart. However, this is not so. First, we can pass in different arguments when we start each thread to set them on different tasks. An example of this would be a filehandle, newly created by a server application, and this is exactly what an example of a threaded server in Chapter 23 does. Second, a thread can create a thread object to represent itself using the self class method: $self = Thread->self; With this thread object the thread can now call methods on itself, for example tid, which returns the underlying thread number: $self_id = $self->tid; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Chapter 22 940 It is possible to have more than one thread object containing the same thread ID, and this is actually common in some circumstances. We can check for equivalence by comparing the IDs, but we can do better by using the equals method: print "Equal! \n" if $self->equal($thread); Or, equivalently: print "Equal! \n" if $thread->equal($self); Thread identities can be useful for all kinds of things, one of the most useful being thread-specific data. Thread-specific Data One of the drawbacks of forked processes is that they do not share any of their data, making communication difficult. Threads have the opposite problem; they all share the same data, so finding privacy is that much harder. Unlike some other languages, Perl does not have explicit support for thread-specific data, but we can improvise. If our thread all fits into one subroutine we can simply create a lexical variable with my and use that. If we do have subroutines to call and we want them to be able to access variables we create the our can be used. Alternatively we can create a global hash of thread IDs and use the values for thread- specific data: %thread_data; sub threadsub { $id = Thread->self->tid; $thread_data{$id}{'Started'} = time; $thread_data{$id}{'Ended'} = time; } new Thread \&thread_sub, $arg1; new Thread \&thread_sub, $arg2; The advantage of using my or our is that the data is truly private, because it is lexically scoped to the enclosing subroutine. The advantage of the global hash is that threads can potentially look at each other's data in certain well-defined circumstances. Thread Management Perl keeps a list of every thread that has been created. We can get a copy of this list, as thread objects, with the Thread->list class method: @threads = Thread->list; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Creating and Managing Processes 941 One of these threads is our own thread. We can find out which by using the equal method: $self = Thread->self; foreach (@threads) { next if $self->equal($_); } Just because a thread is present does not mean that it is running, however. Perl keeps a record of the return value of every thread when it exits, and keeps a record of the thread for as long as that value remains unclaimed. This is similar to child processes that have not had waitpid called for them. The threaded equivalent of waitpid is the join method, which we call on the thread we want to retrieve the exit value for: $return_result = $thread->join; The join method will block until the thread on which join was called exits. If the thread aborts (for example by calling die) then the error will be propagated to the thread that called join. This means that it will itself die unless the join is protected by an eval: $return_result = eval {$thread->join;} if ($@) { warn "Thread unraveled before completion \n"; } As a convenience for this common construct, the Thread module also provides an eval method, which wraps join inside an eval for us: $return_result = $thread->eval; if ($@) { warn "Thread unraveled before completion \n"; } It is bad form to ignore the return value of a thread, since it clutters up the thread list with dead threads. If we do not care about the return value then we can tell the thread that we do not want it to linger and preserve its return value by telling it to detach: $thread->detach; The catch to this is that if the thread dies, nobody will notice, unless a signal handler for the __DIE__ hook, which checks Thread->self for the dying thread, has been registered. To reiterate: if we join a moribund thread from the main thread without precautions we do have to worry about the application dying as a whole. As a slightly fuller and more complete (although admittedly not particularly useful) example, this short program starts up five threads, then joins each of them in turn before exiting: #!/usr/bin/perl # join.pl use warnings; use strict; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Chapter 22 942 # check we have threads BEGIN { use Config; die "Threadbare! \n" unless $Config{'usethreads'}; } use Thread; # define a subroutine for threads to execute sub threadsub { my $self = Thread->self; print "Thread ", $self->tid, " started \n"; sleep 10; print "Thread ", $self->tid, " ending \n"; } # start up five threads, one second intervals my @threads; foreach (1 5) { push @threads, new Thread \&threadsub; sleep 1; } # wait for the last thread started to end while (my $thread = shift @threads) { print "Waiting for thread ", $thread -> tid, " to end \n"; $thread->join; print "Ended \n"; } # exit print "All threads done \n"; Typically, we care about the return value, and hence would always check it. However, in this case we are simply using join to avoid terminating the main thread prematurely. Variable Locks When multiple threads are all sharing data together we sometimes have problems stopping them from treading on each other's toes. Using thread-specific data solves part of this problem, but it does not deal with sharing common resources amongst a pool of threads. The lock subroutine does handle this, however. It takes any variable as an argument and places a lock on it so that no other thread may lock it for as long as the lock persists, which is defined by its lexical scope. The distinction between lock and access is important; any thread can simply access the variable by not bothering to acquire the lock, so the lock is only good if all threads abide by it. As a short and incomplete example, this subroutine locks a global variable for the duration of its body: $global; sub varlocksub { lock $global; } Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... other network for local delivery: 192 .168.1.10 TE 192 .168.1.7 3C: 89: 00:10:FA:0B 5A:03:26:DE:02:A1 192 .168.1.1 2B:00:3E:46:20:01 2B:00:3E:56:F2:FA 192 .168.2.1 5A:03:A3:56:02:01 3C:E9:34:01:0A:AC 192 .168.2.4 192 .168.2.101 96 3 Team-Fly® Chapter 23 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com So, if 192 .168.1.7 wanted to send a message to 192 .168.2.4, the transaction would look... network and the host segment: 2 09. 165.161 .91 In RFC 1700, the Assigned Numbers standard, five classes of networks are defined: Class Range Bitmask of first octet A 0-127 0xxxxxxx B 128- 191 10xxxxxx C 192 -223 110xxxxx D 224-2 39 1110xxxx E 240-255 1111xxxx The 10.0.0.0 network address, for instance, is a class A network, since the first octet falls in the class A range, and 192 .168.1.0 is a class C network... network is available 96 6 Networking with Perl Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Networking with Perl Sockets are Perl' s interface into the networking layer, mapping the details of a network connection into something that looks and feels exactly like a filehandle This analogy didn't start with Perl, of course, but came into popular usage with BSD UNIX Perl merely continues... clients and servers using Perl' s standard libraries We will also look at the functions Perl provides for determining network configurations, and the helper modules that make analyzing this information simpler An Introduction to Networks Most Perl network programming involves establishing connections across, and communicating through, TCP/IP networks In order to understand how network -programming interfaces... still experimental, so an in-depth discussion of them is perhaps inappropriate However, more examples and a more detailed discussion of threaded programming models and how Perl handles threads can be found in the perlthrtut manual page by executing: > perldoc perlthrtut Thread Safety and Locked Code AM FL Y Due to the fact that threads share data, modules and applications that were not built with them... neighbor, 192 .168.1.10): 96 2 Networking with Perl Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com ❑ IP receives a packet (in this case, packet refers to some kind of payload) for delivery ❑ IP determines that local delivery can be done since the destination address appears to be on the same network as its own ❑ An ARP request is broadcasted asking, 'Who has 10?' ❑ 192 .168.1.10... Creating and Managing Processes Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 95 5 Chapter 22 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 95 6 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Networking with Perl Network programming is a very large topic In this chapter, we will attempt to provide a solid foundation in the... 2 09. 165.161 .91 :80 12.18. 192 .3:4616 These features make TCP the most widely used protocol on the Internet Applications like web services (HTTP protocol) would certainly not be as enjoyable if the content came in out of order and occasionally corrupted The same goes for other application protocols like mail (SMTP), and news (NNTP) Both protocols are covered by their own RFCs: 768 for UDP, and 793 ... layers to deliver the data: 192 .168.1.7 3C: 89: 00:10:FA:0B 192 .168.1.10 5A:03:26:DE:02:A1 In this example, both hosts are on the same network, so IP requests that Ethernet deliver the data directly A separate protocol (called ARP – Address Resolution Protocol, which is covered in RFC 826) is used for this That transaction will go along these lines (imagine that the host above, 192 .168.1.7, is sending a... chapter we have covered the following: ❑ ❑ Using Perl' s fork function to start new processes ❑ Inter-Process Communication and the various ways in which different processes can communicate with one another ❑ Sharing of data between processes, using message queues, semaphores, and shared memory segments ❑ 95 4 Signals and how to handle them Thread support in Perl Creating and Managing Processes Simpo PDF . support in Perl is still experimental. Added in Perl 5.6, work continues into Perl 5.7 and Perl 6. As shipped, most Perl interpreters do not support threads at all, but if we build Perl from scratch,. thread support is so new it isn't even available in Perl code yet. The likelihood is that full official thread support will arrive with Perl 6, but that doesn't mean we can't use it. module will only be present if thread support is enabled: > perldoc Thread The Thread module is the basis of handling threads in Perl. It is an object-oriented module that represents threads