Concurrency

Concurrency

Dispatchers ensure process isolation.
Allocate time to run the thread.

It’s ‘running’ because the thread started and didn’t end.
There is one core, but multiple threads are ‘running’, ensuring concurrency.

Unlike threads, memory cannot be divided over time.
Memory is limited and allocated by dividing it into spaces.

The OS swindles the process that it monopolizes all resources, but in reality,
resources are limited and used by everyone.
Traffic arrangement is necessary when trying to take limited resources with each other.

Limited resources should be mutual exclusion. And you have to synchronize what you use.
It is necessary to ensure concurrency.

multiple process

OS design is concerned with the management of processes and threads:

- Multi programming
- Multi processing (multi core)
- Distributed Processing (Multiple computers are connected by a network, etc)
  `clustering

Terms of Concurrency

atomic operation :
Operations that do not lose execution permission in the middle when certain conditions are present.

critical section :
The protected sections.

race condition :
The state of trying to use resources with each other.

mutual exclusion :
Using resources entirely alone.

deadlock :
The status of waiting for the required resources. And if that resource remains occupied by another process.
A state of being unable to do anything after all.

livelock :
In a state such as a deadlock, But The program is in operation(?)

starvation :
A state in which certain resources are required but are not assigned.

Suppose you have two programs.
It is written by C.


static int a = 100;

int Program1() {
	a = a + 10;
	return a
}

int Program2() {
	a = a - 10;
	return a
}

...
	Program1();
	Program2();
...

It is assumed that concurrency is guaranteed by time quantum of time.
Of course, C Program is eventually converted into assembly instructions.

atomic1
picture [1] Atomic Operation 1


When two processes are completed, the result should be 100.
However, if context switching occurs as follows, the result will be different.

atomic2
picture [2] Atomic Operation 2


So This case needs mutual exclusion .
The exclusion use of a should be guaranteed.

Here’s an example.

mutual_ex
picture [3] Mutual Exclusion


There is only one toilet in the park, so it is assumed that it is for one person only.
But if multiple people have to go to the bathroom, what should we do?

Here, people correspond to the process, and the toilet bowl in the bathroom corresponds to a shared resource.
What if someone opens the door when you poop?

Like you’re not supposed to go into the bathroom when someone’s using it,
When a process uses shared resources, it should not compete with shared resources.

Like hanging a sign while using the toilet and putting it down when you’re done using it,
Raise the flag when the process uses shared resources and lower the flag when it is finished.
The flag is usually used as a variable, and an example is a ‘semaphore’

The problem with ensuring this mutual exclusion is that there are different types and
numbers of shared resources, processes, and the shared resources required by each process.

I’ll give you an example.

Process 1 requires resource 1 and resource 2, and process 2 also requires resource 1 and resource 2,
and when resource 1 is allocated to process 1 and resource 2 is allocated to process 2, its own resource has other resources.
Process 1 and process 2 are not executed forever.

deadlock
picture [4] Dead Lock


Such a difficult situation can come.
A state in which processes are just waiting for each other and nothing is executed is called a deadlock .
If you prioritize a process separately to resolve the problem, some processes may never be allocated resources.
It’s called starvation .

summary

The state in which there are shared resources and various processes want to use each other is called race condition .
In order to solve the race condition , the critical section should be made into an atomic operation .
It is said to support mutual exclusion .
When mutual exclusion is guaranteed, a deadlock occurs that is waiting for the other party’s work to be completed.
If you give priority to resolve deadlocks, A process that continues to fall behind priorities and is unable to allocate resources is called starvation .

Principles of Concurrency

Interleaving and overlapping.
Uniprocessor - the relative speed of execution of processes can’t be predicted.

Uni - processor : (Interleaving only)

	| P 1 | --- | P 1 | --- |
	| --- | P 2 | --- | --- | ...
	| --- | --- | --- | P 3 |



Multi - processor : (Interleaving + Overlapping)
	| P 1 | --- | P 1 | --- |
	| --- | P 2 | --- | --- | ...
	| --- | --- | --- | P 3 |
	`-----`-----`-----`-----`  ...
	 `-----`-----`-----`-----`
	  `-----`-----`-----`-----`  ...
	   `-----`-----`-----`-----`

overlapping :
Since there are multiple cores, different processes are physically executed at the same time.

Difficulties of Concurrency

Considerations when ensuring concurrency.

- Sharing of global resources.
- Difficult for the OS to manage the allocation of resources optimally.
- Difficult to locate programming errors as result are not deterministic and reproducible.
- It may be a problem of its own, but it is also related to competition with other processes.

Race Condition

multiple process or threads read and write data items.
The final result depends on the order of execution.

Resource allocation should not be timed.
If you adjust the execution speed, it should depend on the user’s computer and how many processes there are.
So it should never be solved by timing.

Process Interaction

Degree of Awareness Relationship Potential Problems
Unaware of each other Competition - Mutual exclusion
- Deadlock
- Starvation
Indirect aware
(shared object)
Competition by sharing - Mutual exclusion
- Deadlock
- Starvation
- Data coherence
Directly aware Cooperation by communication - Deadlock
- Starvation

Requirements for Mutual Exclusion

- Must be enforced.
- A process that halts must do so without interfering with other processes
- No deadlock
- No starvation
- A process must not be denied access to a critical section when there is no toher process using it.
- No assumptions are made about relative process speeds or number of prcesses.
- A process remains inside its critical section for a finite time only.

Mutual Exclusion Implementing

Hardware Support

Atomic operations shall be implemented in critical sections.

blocking interrupt

Turn off interrupt before entering critical section and turn on interrupt when critical section is finished.

However, processes that are not related to shared resources are all put on waiting.
In addition, even if it is a process that uses shared resources, threads before using resources are also in a waiting state.
It is very inefficient.
When the interrupt is turned off, it does not work on the multiprocessor.

Compare & Swap Instruction

also called a compare and exchange instruction .

first, make a flag and make two simple code.
Assume that it enters the foo function and is called at the end of the bar function.

/* Program 1 */
 1 int flag;
 2
 3 foo() { // enter
 4 	while (flag == 0);
 5 		flag = 0;
 6 }
 7
 8 bar() { // exit
 9 	flag = 1;
10 }
/* Program 2 */
 1 int flag;
 2
 3 foo() { // enter
 4 	while (flag == 0);
 5 		flag = 0;
 6 }
 7
 8 bar() { // exit
 9 	flag = 1;
10 }

It looks like it’s been implemented,
If there is an interruption at line 4, and context switching occurs,
Both programs will enter the critical section.

You can also blocking interrupt line 4 up and down,
but it has become a little more efficient from the initial description,
and the problem remains the same.

So there is a command that supports reading and writing flag values at once.
That’s what a compare and swap is .

 1  /* program mutualexclusion */
 2 const int n = /* number of processes */;
 3 int flag; /* 0: allow 1: disallow */
 4
 5 void foo(int i) {
 6 	while (true) {
 7 		while (compare_and_swap(&flag, 0, 1) == 1); /* read & write at once */
 8 		/* critical section */
 9 		flag = 0;
10 		/* remainder */
11 	}
12 }

void main() {
	flag = 0;
	parbegin (P(1), P(2), ... , P(n));
}

another way is exchange .

 1 /* program mutualexclusion */
 2 const int n = /* number of processes */;
 3 int flag;
 4 
 5 void bar(int i) {
 6 	while (true) {
 7 		int keyi = 1;
 8 		do exchange (&keyi, &flag)
 9 		while (keyi != 0);
10  		/* critical section */
11  		flag = 0;
12  		/* remainder */
13  	}
14 }
15  
16 void main() {
17 	flag = 0;
18 	parbegin (P(1), P(2), ... , P(n));
19 }
advantages & disadvantages

advantages

- Applicable to any number of processes on either a uni or multi.
- Simple and easy to verfy.
- It can be used to support mulitple critical sections by its own variable.

disadvantages

- Busy waiting
- Starvation
- Deadlock

Ways of Mutual Exclusion

Name Info
Semaphore general
Binary Semaphore Use Binary Semapore var
Mutex Lock & unlock only in one process.
Condition Variable support by program language
Monitor support by program language
Event Flags  
Maliboxes/Messages synchronize by communication
Spinlocks use in multiprocessor

Semaphore

It is variable. type is integer.
The value of the semaphore is the number of accessible processes.
It has some operations.

- Initialize to a nonnegative integer value
- Use semWait # decrement
- Use semSignal # increment

example of Semaphore

 1 struct semaphore {
 2 	int count;
 3 	queueType queue; // FIFO
 4 };
 5
 6 void semWait(semaphore s) {
 7 	s.count--;
 8 	if (s.count < 0) {
 9 		/* place this process in s.queue */
10 		/* block this process */
11 	}
12 }
13
14 void semSignal(semaphore s) {
15 	s.count++;
16 	if (s.count <= 0) {
17 		/* remove a process from s.queue */
18 		/* place process on ready list */
19 	}
20 }

The fact that Semaphore’s value has become negative means that it is impossible to enter.
The negative value of the semaphore is the number of blocked because they did not enter.

When a semaphore signal is called and one process exits the critical section,
The first (or higher priority) process that is blocked is caught in the ready queue.

example of Binary Semaphore

 1 struct binary_semaphore {
 2 	int value = 0;
 3 	queueType queue;
 4 };
 5
 6 void semWait(binary_semaphore s) {
 7 	if (s.value == 1)
 8 		s.value = 0;
 9 	else {
10 		/* place this process in s.queue */
11 		/* block this process */
12 	}
13 }
14
15 void semSignal(binary_semaphore s) {
16 	if (s.queue is empty())
17 		s.value = 1;
18 	else {
19 		/* remove a process from s.queue */
20 		/* place process on ready list */
21 	}
22 }

examples of Semaphore Machanism

It is an image representing a semaphore operation.
It can be seen that the state of the process changes
while ensuring mutual exclusion as shared resources are used and access to critical sections.

semaphoreq1
picture [5-1] Semaphore Queue 1

semaphoreq2
picture [5-2] Semaphore Queue 2


The semaphore variable value is equal to the number of processes waiting in the blocked queue.
It may not be a unicore system, or it may vary depending on the situation.

semaphore vs blocking interrupt

I assume that there is a process that performs +1 and -1 on the shared resource,
and the result is a program that wants the same value as the first one.
Let’s compare inter-exclusion with blocking interrupt and semaphore.

sharedi1
picture [6-1] No ensure mutual exclsion


sharedi2
picture [6-2] Blocking Interrupt


sharedi3
picture [6-3] Using Semaphore


Blocking interrupts requires waiting for processes that are not related to shared resources when a process approaches a critical section.
It’s inefficient. Above all, it cannot be used in multi-core systems.

Semapore does not block interruptions,
so it is free to switch processes and can be applied to multi-core systems.

ETC

A system example that guarantees automatic-exclusive and synchronization like semaphores.

Monitors

Programming language constrcut that provides equivalent functionality
to that of semaphores and is easier to control.

Program languages

Pascal, Pascal-Plus, Modula-2, Modula-3, and Java

Characteristics

Local data vars are accessible only by the Monitor’s procedures and not by any external procedure.
Process enters monitor by invoking one of its procedures(functions).
Only one process may be executing in the monitor at a time.

synchronization

Achieved by the use of Condition variables that are combined within the monitor and accessible only within the monitor.
Condition variables are operated on by two functions:

cwait(c): suspend execution of the calling process on condition c.
csignal(c): resume execution of some process blocked after a cwait on the same condition.

Message Passing

Direct communication between processes with IPC .

ipc
picture [7] Message Passing


works with Distributed systems and shared memory multiprocessor and Uniprocessor systems .

format

The actual function is normally provided in the form of a pair of primitives:

send(destination, message)
receive(source, message)

message-format
picture [8] Message Format


Message Passing are exchanged in TLV format after the source and destination addresses are determined.

message-format
picture [9] TLV


There are fixed and variable message lengths.
It is usually used variable types because processes don’t know the length of messages that are actually exchanged.
CRC is optional.

addressing

There are two types of addressing: direct and indirect.

direct-addressing indirect-addressing
picture [10] addressing


The direct format is a method of designating and using a send/receive process with a PID, etc.
The redirect format is used in broadcasts that are sprayed to an unspecified number of people, or in systems that send messages and do not require feedback, etc.

mailbox-code

This is an example of mutualexclusion with mailbox.

 1 const int n = /* number of processes */
 2 void P(int i) {
 3 	message msg;
 4 	while (true) {
 5 		receive (box, msg);
 6 		/* critical section */
 7 		send (box, msg);
 8 		/* remainder */
 9 	}
10 void main() {
11 	create_mailbox (box);
12 	send(box, null);
13 	parbegin (P(1), P(2), P(3), ..., P(n));
14 }
}

Updated:

Leave a comment