Accessing a variable or function in the other process is hard. Why?
p1
:
x = msgget(75, 0777|IPC_CREAT);
msgsnd(x, &msg, ......);
p2
:
x = msgget(75, 0777);
msgrcv(x, &msg);
msgget returns struct msg_queue
.
struct msg_queue{
struct kern_ipc_perm q_perm;
.........
struct list_head q_messages;
};
struct kern_ipc_perm{
.........
int key;
unsigned short mode; // permission bit mask
........
};
p1
:
x = shmget(75, 4, 0777|IPC_CREAT);
y= (int *)shmat(x, ......);
p2
:
x = shmget(75, 4, 0777);
y = (int *)shmat(x, ....);
example :
x = shmget(75, 4, 0777|IPC_CREAT);
if ((fork())==0){ // child
y= (int *) shmat(x, 0, 0);
for(j=0;j<100;j++)
for(i=0;i<10000;i++)
*y = *y + 1;
printf("child: the result is %d\n", *y);
}
else{ // parent
y= (int *) shmat(x, 0, 0);
for(j=0;j<100;j++)
for(i=0;i<10000;i++)
*y = *y + 1;
printf("parent: the result is %d\n", *y);
}
shmget()
returns struct shmid_kernel
.
struct shmid_kernel{
struct kern_ipc_perm shm_perm;
struct file * shm_file; // this file represents the shared memory
// this file exists in the physical memory only except
// during swapped-out state. The physical location of
// this file is the physical location of the shared memory
.........
};
shmat()
allocates a space in the virtual address space and map this address to the physical location of the shared memory through the page table.
Shared memory is fast but can cause โrace conditionโ. When the outcome of a computation depends on how two or more processes are scheduled, the code is incorrect. We say that there is a race condition.
The example in 4.1..
*y = *y + 1 ==>
mov *y, eax ; *y -> eax
inc eax
mov eax, *y
P1, P2:
lp1: mov r0, lock
cmp r0, 1
je lp
mov lock, 1
-- CS --
mov lock, 0
P1,P2:
cli
--CS--
sti
Or
lp: tsl ro, lock
cmp r0, 1
je lp
--CS--
mov lock, 0
tsl ro, lock is same as the following operations all executed atomically
ro = lock
if (r0==0)
lock=1
P1,P2:
wait(s);
--CS--
signal(s);
wait(s):
s = s - 1;
if (s < 0)
someone is already in CS. wait here. (insert into waiting list on s)
else
enter CS
signal(s):
s = s + 1;
if (s <=0)
someone is in the waiting list. wake one up.
int semid;
struct sembuf psembuf={0,-1,SEM_UNDO};
struct sembuf vsembuf={0,1,SEM_UNDO};
main(){
.............
semid = semget(75, 1, 0777|IPC_CREAT); // get a semaphore
sem_union.val=1;
semctl(semid, 0, SETVAL, sem_union); // initial value is 1
semop(semid, &psembuf, 1); // wait(s)
--CS--
semop(semid, &vsembuf, 1); // signal(s)
semget()
returns struct sem_array
.
struct sem_array{
struct kern_ipc_perm sem_perm;
struct sem * sem_base; // link list of sem structure
.........
};
struct sem{
unsigned long semval; // value of this semaphore
......
};
Using semaphore correctly not easy. Example: producer-consumer problem.
[producer] produce item and push to the stk
lp: get_item(&item);
if (top==MAX)
sleep();
top=top+1;
stk[top]=item
if (top==1)
wakeup(์๋น์);
goto lp;
[consumer] pop item from stk and consume
lp: if (top==0)
sleep();
item = stk[top];
top = top-1;
if (top==MAX-1)
wakeup(์์ฐ์);
consume(item);
goto lp;
mutex : intialize with 1. sema for mutual exclusion holes : initial val=MAX. sema for holes items : initial val=0. sema for items produced so far
[producer]
lp: get_item(&item);
wait(holes);
wait(mutex);
top=top+1;
stk[top]=item
signal(mutex);
signal(items);
goto lp;
[consumer]
lp:
wait(items);
wait(mutex);
item = stk[top];
top = top-1;
signal(mutex);
signal(holes);
consume(item);
goto lp;
ex1.c
) and explain the result.ex1.c
:
#include <stdio.h>
unsigned long long sum = 0;
int main(){
int x = fork();
if (x == 0) { // child
int i, j;
for (i=0; i<20000; i++)
for(j=0; j<2000; j++)
sum++;
printf("child sum: %llu\n", sum);
} else { // parent
int i, j;
for(i=0; i<20000; i++)
for(j=0; j<2000; j++)
sum++;
printf("parent sum: %llu\n", sum);
}
return 0;
}
$ gcc -o ex1 ex1.c
$ ./ex1
fork
๋ก ์๋ก์ด ํ๋ก์ธ์ค๋ฅผ ๋ง๋ค๋ฉด ํ๋ก์ธ์ค ๊ฐ์ ๋ณ์๋ฅผ ๊ณต์ ํ์ง ์๊ณ ์๋ก์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋น๋ฐ๋๋ค. ๊ทธ ๊ฒฐ๊ณผ, Race Condition์ด ์ผ์ด๋์ง ์๊ณ ๋ ํ๋ก์ธ์ค ๋ชจ๋ 40000000๋ฒ์ ์ฐ์ฐ์ ์ํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๋ค.
th.c
) and explain the result.#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_t t1, t2; // thread 1, thread 2
unsigned long long sum=0;
void * foo1(void *arg){
int i,j;
for(i=0;i<20000;i++){
for(j=0;j<2000;j++)
sum += 1;
}
printf("thread 1 sum:%llu\n", sum);
return NULL;
}
void * foo2(void *arg){
int i,j;
for(i=0;i<20000;i++){
for(j=0;j<2000;j++)
sum += 1;
}
printf("thread 2 sum:%llu\n", sum);
return NULL;
}
int main(void){
pthread_create(&t1, NULL, &foo1, NULL);
pthread_create(&t2, NULL, &foo2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
}
undefined reference to 'pthread_create'
์๋ฌ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด, -lpthread
์ต์
์ ์ฃผ์ด th.c
๋ฅผ ์ปดํ์ผํ์๋ค.
$ gcc -o th -lpthread th.c
$ ./th
....
$ ./th
....
$ ./th
....
sum
์ ๊ธฐ๋๊ฐ์ธ 80000000์ ๋ฏธ์น์ง ๋ชปํ๋ค.foo1
๊ณผ foo2
๊ฐ ์ ์ญ๋ณ์ sum
์ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ thread2์ sum
์ 80000000
์ด ๋์ด์ผํ๋ค.sum += 1
. ์ฝ๋๋ ์ด์
๋ธ๋ฆฌ ์ฝ๋๋ก ๋ํ๋ด๋ฉด ์๋์ ๊ฐ๋ค
mov eax, sum // eax โ sum`
inc eax // eax = eax + 1
mov sum, eax // sum โ eax
mov eax, sum
์ด๋ inc eax
๊น์ง ์คํ ํ timeout๋๊ณ , thread2๋ฅผ ๊ฑฐ์น ํ ๋ค์ thread1์ด
schedule ๋ ๋๋ฅผ ๊ฐ์ ํ๋ฉด, thread2์์ ๊ณ์ฐํ๋ sum
์ด thread1์ eax์ ๋ฐ์๋์ง ์๋๋ค. thread2 ์
์ฅ์์๋ ๋๊ฐ์ด
์๊ฐํด๋ณผ ์ ์๋ค. th2.c
) and explain the result.#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_t t1, t2; // thread 1, thread 2
pthread_mutex_t lock; // semaphore
unsigned long long sum=0;
void * foo1(void *arg){
int i,j;
for(i=0;i<20000;i++){
pthread_mutex_lock(&lock);
for(j=0;j<2000;j++)
sum += 1;
pthread_mutex_unlock(&lock);
}
printf("thread 1 sum:%llu\n", sum);
return NULL;
}
void * foo2(void *arg){
int i,j;
for(i=0;i<20000;i++){
pthread_mutex_lock(&lock);
for(j=0;j<2000;j++)
sum += 1;
pthread_mutex_unlock(&lock);
}
printf("thread 2 sum:%llu\n", sum);
return NULL;
}
int main(void){
pthread_mutex_init(&lock, NULL);
pthread_create(&t1, NULL, &foo1, NULL);
pthread_create(&t2, NULL, &foo2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
$ gcc -o th2 -lpthread th2.c
$ ./th2
...
$ ./th2
...
$ ./th2
.....
pthread_mutex_t lock;
(semaphore)์ ์ด์ฉํด ์ฌ๋ฌ thread๊ฐ ๋์์ ํ ๋ณ์์ ์ ๊ทผํ ์ ์๊ฒ ํ๋ค.
๊ทธ ๊ฒฐ๊ณผ, 2๋ฒ์ฒ๋ผ ์ต์ข
๊ณ์ฐ ๊ฐ์ด ์ ์ค๋์ง ์๊ณ 80000000์ด ํญ์ ๋์จ๋ค.
thread1์ sum
์ด 80000000์ด ์๋ ์ด์ ๋ thread2๊ฐ ๋๋๊ธฐ ์ ์ ๋จผ์ ์ข
๋ฃ๋๊ณ thread2์์ 80000000์ด ๊ณ์ฐ๋๊ธฐ ๋๋ฌธ์ด๋ค.
th3.c
) and explain the result. Modify the code so that it wonโt have a deadlock.th3.c
:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_t t1, t2; // thread 1, thread 2
pthread_mutex_t lock1; // semaphore 1 for sum 1
pthread_mutex_t lock2; // semaphore 2 for sum 2
unsigned long long sum1=0;
unsigned long long sum2=0;
void * foo1(void *arg){
int i,j;
for(i=0;i<20000;i++){
pthread_mutex_lock(&lock1);
pthread_mutex_lock(&lock2);
for(j=0;j<2000;j++)
sum1 += 1;
pthread_mutex_unlock(&lock1);
for(j=0;j<2000;j++)
sum2 += 1;
pthread_mutex_unlock(&lock2);
}
printf("thread 1 sum1:%llu\n", sum1);
printf("thread 1 sum2:%llu\n", sum2);
return NULL;
}
void * foo2(void *arg){
int i,j;
for(i=0;i<20000;i++){
pthread_mutex_lock(&lock2);
pthread_mutex_lock(&lock1);
for(j=0;j<2000;j++)
sum1 += 1;
pthread_mutex_unlock(&lock1);
for(j=0;j<2000;j++)
sum2 += 1;
pthread_mutex_unlock(&lock2);
}
printf("thread 2 sum1:%llu\n", sum1);
printf("thread 2 sum2:%llu\n", sum2);
return NULL;
}
int main(void){
pthread_mutex_init(&lock1, NULL);
pthread_mutex_init(&lock2, NULL);
pthread_create(&t1, NULL, &foo1, NULL);
pthread_create(&t2, NULL, &foo2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&lock1);
pthread_mutex_destroy(&lock2);
return 0;
}
$ gcc -o th3 -lpthread th3.c
$ ./th3
# deadlock
์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ์คํํ์๋๋, ๋ ํ๋ก์ธ์ค๊ฐ ์๋ก ์๋๋ฐฉ์ semaphore๊ฐ ํ๋ฆฌ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ํ๊ฐ ๋์ด ํ๋ก๊ทธ๋จ์ด ๋ ์ด์ ์งํ๋์ง ์์๋ค. ์ด๋ฌํ ์ํฉ์ deadlock์ด๋ผ๊ณ ํ๋ค.
์ด๋ lock
์ด๋ unlock
์ ์์๋ฅผ ์๋ชป ์ฌ์ฉํ๋ฉด ๋ฐ์ํ๊ฒ ๋๋ค. ์ ์ฝ๋์์ thread1์ด pthread_mutex_lock(&lock1);
๋ฅผ ์คํํ ์งํ timeout์ผ๋ก thread2๊ฐ schedule๋๋ฉด, thread2๋ pthread_mutex_lock(&lock2);
๋ฅผ ์คํํ๊ณ ๊ทธ ๋ค์ ์ฝ๋์ธ pthread_mutex_lock(&lock1);
๋ฅผ ๋ง์ฃผ์น๋ฉด thread1์ ์ํด ์๋์ CS๋ก ์ ๊ทผํ ์ ์๊ฒ ๋๋ค.
์ด ์ํ์์ ๋ค์ thread1์ด schedule๋์ด foo1
์ ๋ค์ ์ฝ๋์ธ pthread_mutex_lock(&lock2);
๋ฅผ ์คํํ๋ฉด ์ด ์ญ์ lock2
์ CS๊ฐ thread2์ ์ํด block ๋์๊ธฐ ๋๋ฌธ์ ๋ ์ด์ ์ฝ๋๋ฅผ ์งํํ ์ ์๊ฒ ๋๋ค.
deadlock์ ๋ฐฉ์งํ๊ณ ์, foo1
๊ณผ foo2
์ lock ์์๋ฅผ ์๋์ ๊ฐ์ด ์์ ํ์๋ค.
th3-modified.c
:
void * foo2(void *arg){
int i,j;
for(i=0;i<20000;i++){
pthread_mutex_lock(&lock1);
pthread_mutex_lock(&lock2);
for(j=0;j<2000;j++)
sum1 += 1;
pthread_mutex_unlock(&lock1);
for(j=0;j<2000;j++)
sum2 += 1;
pthread_mutex_unlock(&lock2);
}
printf("thread 2 sum1:%llu\n", sum1);
printf("thread 2 sum2:%llu\n", sum2);
return NULL;
}
$ gcc -o th3 -lpthread th3.c
$ ./th3
pthread_mutex_lock()
and trace the code. arch/x86/kernel/syscall_table_32.S
:
32๋ฒ ์๋ฆฌ์ my_syscall_number_toggle
์ ์ถ๊ฐํด์ฃผ์๋ค.
fs/read_write.c
:
my_syscall_number_toggle
์ ์ ์ํด์ฃผ์๋ค.
arch/x86/kernel/entry_32.S
:
โฆโฆ
์ด๋ ๊ฒ ํ์ง๋ง pthread_mutex_lock
์ ์์คํ
์ฝ ํธ์ถ์ด ๋ณด์ด์ง ์์๋ค.