Computer-Science

IPC(Interprocess Communication)

1. Communication between two processes

Accessing a variable or function in the other process is hard. Why?

2. Two approaches: MP, SM

3. messge passing

3.1 Usage

p1:

x = msgget(75, 0777|IPC_CREAT);
msgsnd(x, &msg, ......);

p2:

x = msgget(75, 0777);
msgrcv(x, &msg);

3.2 data structure

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
   ........
};

4. Shared memory

4.1) usage

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);
}

4.2) data structure

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.

5. Race condition

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.

5.1) Example

The example in 4.1..

5.2) Why it happens?

*y = *y + 1 ==>
mov *y, eax ; *y -> eax
inc eax
mov eax, *y

5.3) When it happens?

5.4) How to solve?

5.5) Implementing mutual exclusion

5.6) sw

P1, P2:

lp1: mov r0, lock
     cmp r0, 1
     je lp
     mov lock, 1
     -- CS --
     mov lock, 0

5.7) hw

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

5.8) semaphore

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.

5.9) using semaphore

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)

5.10) data structure for semaphore

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
   ......
};

5.11) Deadlock

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;

6. Exercise

1) Try below (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๋ฒˆ์˜ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

2) Try below (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
....

3) Try below(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์ด ๊ณ„์‚ฐ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

4) (Deadlock) Try below(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

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์˜ ์‹œ์Šคํ…œ ์ฝœ ํ˜ธ์ถœ์ด ๋ณด์ด์ง€ ์•Š์•˜๋‹ค.