2017-04-18 14 views
7

Tôi đang cố gắng tạo mã mô phỏng trung tâm chăm sóc trẻ em. Ở trung tâm này, một người lớn có thể chăm sóc tối đa ba đứa con. Tình trạng này phải được đáp ứng mọi lúc. Người lớn và trẻ em là các quá trình được tạo ngẫu nhiên và số lượng trẻ em và người lớn được đặt trong các đối số chương trình. Trẻ em chỉ có thể nhập nếu có đủ người lớn bên trong và người lớn chỉ có thể để lại nếu có đủ người lớn khác chăm sóc trẻ em. Nếu không, chờ đợi thụ động nên được thực hiện, cho đến khi điều kiện cho phép trẻ em/người lớn để lại/nhập.Linux thụ động đang chờ cập nhật điều kiện

#include <time.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <signal.h> 
#include <string.h> 
#include <semaphore.h> 
#include <sys/mman.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 

void load_init_values(); 
void handler(int, int, char*); 

pid_t adults, children; 
int adult_max_t, child_max_t, adc, chc, amt, cmt, shm_a_id, shm_c_id; 
int *adults_inside, *children_inside; 
sem_t *adults_sem, *children_sem, *entry; 


int main(int argc, char *argv[]) 
{ 

srand(time(NULL)); 
setbuf(stdout,NULL); 

adc=atoi(argv[1]); 
chc=atoi(argv[2]); 
adult_max_t=atoi(argv[3]); 
child_max_t=atoi(argv[4]); 
amt=atoi(argv[5]); 
cmt=atoi(argv[6]); 

int pid=0; 

load_init_values(); 
adults = fork(); 

if (adults == 0) 
{ 

    for(int i=0; i<=adc-1; i++) 
    { 
     int adult_t = (random() % (adult_max_t + 1)); 
     usleep(adult_t*1000); 

     adults = fork(); 

     // Adult process is created here 
     if(adults == 0) 
     { 
     handler(getpid(), amt, "adult"); 
     } 
     else 
     { 
     } 
    } 
} 

else 
{ 
    children = fork(); 

    if (children == 0) 
    { 

    for(int i=0; i<=chc-1; i++) 
    { 
     int child_t = (random() % (child_max_t + 1)); 
     usleep(child_t*1000); 

     children = fork(); 

     // Child process is created here 
     if(children == 0) 
     { 
     handler(getpid(), cmt, "child"); 
     break; 
     } 
     else 
     { 
     } 
    } 
    } 

    else 
    { 
    } 
} 
return 0; 
} 

void handler(int pid,int maxtime, char* type) 
{ 
sem_wait(entry); 

    printf("%s %i%s\n",type,pid," attempting to enter..."); 

    if(type == "child") 
    { 
    int child_leave_t = (random() % (maxtime + 1)); 

    if((*adults_inside) != 0) 
    { 

    if(((*children_inside)+1)/(*adults_inside) <= 3) 
    { 
     (*children_inside)++; 
     printf("%s %i%s\n",type,pid," entered!"); 

     usleep(child_leave_t*1000); 

     printf("%s %i%s\n",type,pid," left!"); 
     (*children_inside)--; 
    } 

    else 
    { 
     printf("%s %i%s\n",type,pid," can not enter. Waiting..."); 
    } 

    } 
    else 
    { 
     printf("%s %i%s\n",type,pid," can not enter. Waiting..."); 
    } 
    } 

    else if(type == "adult") 
    { 

    (*adults_inside)++; 
    printf("%s %i%s\n",type,pid," entered."); 

    } 

sem_post(entry); 
} 

void load_init_values() 
{ 
adults_sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); 
children_sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); 
entry = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); 
shm_a_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | IPC_EXCL | 0666); 
shm_c_id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | IPC_EXCL | 0666); 
adults_inside = (int *) shmat(shm_a_id, NULL, 0); 
children_inside = (int *) shmat(shm_c_id, NULL, 0); 
sem_init(adults_sem,1,1); 
sem_init(children_sem,1,1); 
sem_init(entry,1,1); 
} 

Mã này chỉ mô phỏng tạo quy trình. Có một semaphore được chia sẻ entry chỉ cho phép một quy trình tại thời điểm yêu cầu nhập. Biến bộ nhớ chia sẻ adults_insidechildren_inside theo dõi trạng thái bên trong.

Vấn đề của tôi về cơ bản nằm ở hàm xử lý. Sau khi điều kiện không cho phép con nhập vào được kích hoạt, tôi không thể tìm ra cách để thực hiện chờ đợi thụ động. Tôi đã suy nghĩ về việc sử dụng hệ thống pause() gọi và lưu trữ các quá trình chờ đợi trong hàng đợi, nhưng có vẻ khá kém hiệu quả. Tôi nên chọn phương pháp nào?

+0

Âm thanh như biến điều kiện là những gì bạn đang tìm kiếm, hãy kiểm tra chủ đề này: http://stackoverflow.com/questions/20772476/when-to-use-pthread-conditional-variables – Rogus

+2

Sử dụng '_t' trên sự kết thúc của biến _names_ là bất thường trong cùng cực, và sẽ dẫn đến sự nhầm lẫn. Hậu tố được sử dụng chủ yếu cho các tên kiểu ('uint8_t',' uintptr_t', 'intmax_t', vv từ tiêu chuẩn C; nhiều tên hơn từ POSIX). Nó không sai; nó chỉ là lời mời gây nhầm lẫn. –

+1

Bạn đang tìm kiếm các quy trình con (đôi khi để mô phỏng trẻ em trong trung tâm chăm sóc trẻ em). Điều đó có nghĩa là các cơ chế đồng bộ hóa dựa trên luồng POSIX không có sẵn. Bạn nên suy nghĩ rất kỹ về cấu trúc quy trình mà bạn đang theo đuổi. –

Trả lời

3

Bạn sẽ cần thực hiện điều này dưới dạng một số hình thức IPC. Bạn đã đề cập đến việc sử dụng Linux, nhưng tôi sẽ giả sử POSIX-với-không có tên-semaphores (nghĩa là không phải OS X) vì bạn chưa sử dụng bất kỳ điều gì cụ thể cho Linux. Những người khác đã đề cập đến điều này có thể đơn giản hơn nếu bạn sử dụng các chủ đề. Nhưng có lẽ bạn có một số lý do để sử dụng nhiều quy trình, vì vậy tôi sẽ chỉ giả định điều đó.

Như được chỉ định, mã không xuất hiện để cho phép người lớn thoát ra, điều này làm cho mọi việc đơn giản hơn một chút. Bạn đã biết có bao nhiêu trẻ em được phép vào bất kỳ thời điểm nào, vì đó là tỷ lệ thuận với số lượng người lớn có mặt tại bất kỳ thời điểm nào.

Để tìm ra cách giải quyết vấn đề, chúng ta hãy xem xét một thứ như thế có thể được xử lý như thế nào trong cuộc sống thực. Chúng ta có thể tưởng tượng rằng có một số loại "gatekeeper" tại nhà trẻ. Điều này "gatekeeper" được đại diện trong mã bằng tổng của nhà nước: semaphore và các biến bộ nhớ chia sẻ đại diện cho số lượng người lớn và trẻ em có mặt tại bất kỳ điểm nào trong thời gian. Khi không có trẻ em nào được phép vào, người gác cổng chặn lối vào và trẻ em phải tạo thành một dòng. Tôi cho rằng ý định là trẻ em được phép vào căn bản ai đến trước được phục vụ trước; điều này có nghĩa là bạn sẽ cần phải có một số loại FIFO để đại diện cho hàng đợi. Khi một đứa trẻ rời đi, người gác cổng phải có khả năng thông báo cho đứa trẻ đầu tiên trong hàng rằng chúng đủ điều kiện để vào.

Vì vậy, mã này là mất tích hai điều:

  1. Một số loại FIFO đại diện cho thứ tự của trẻ em chờ đợi để nhập
  2. Một số loại cơ chế thông báo rằng một đứa trẻ có thể chờ đợi một thông báo trên, và rằng người gác cổng có thể kích hoạt để "thức dậy" đứa trẻ.

Bây giờ, câu hỏi là dữ liệu chúng tôi lưu trữ trong hàng đợi này và cách chúng tôi thực hiện thông báo. Có một vài lựa chọn, nhưng tôi sẽ thảo luận hai cách rõ ràng nhất.

Làm cho việc chờ đợi trẻ có thể đơn giản như việc người gác cổng đặt con PID vào đuôi của FIFO và gửi PID SIGSTOP bằng cách sử dụng kill(2). Điều này có thể xảy ra nhiều lần. Khi một đứa trẻ rời đi, người gác cổng dequeues từ người đứng đầu của FIFO và gửi pid SIGCONT.

Như hiện được kiến ​​trúc, "gatekeeper" trong chương trình của bạn là một khái niệm trừu tượng hơn.Việc triển khai rõ ràng hơn có thể thực hiện một gatekeeper như một quá trình quản lý.

Nhưng vì không có quá trình như vậy tồn tại, chúng ta cần phải tưởng tượng một cái gì đó giống như đứa trẻ nhìn thấy một dấu hiệu "xin vui lòng chờ đợi" ở cửa và chờ đợi. Quá trình đại diện cho đứa trẻ có thể tự chờ đợi bằng cách đặt chính nó ở đuôi của FIFO, và sử dụng chức năng thư viện raise(3), và gửi chính nó SIGSTOP. Sau đó, khi bất kỳ đứa trẻ nào rời khỏi, nó đọc từ phía trước của FIFO và gửi pid SIGCONT bằng cách sử dụng kill(2).

Triển khai này tương đối đơn giản và chỉ các tài nguyên bổ sung được yêu cầu bằng cách nào đó thể hiện hàng đợi trong bộ nhớ dùng chung.

Một phương pháp thay thế sẽ là cung cấp cho mỗi đứa trẻ (các) bộ mô tả tệp của riêng chúng. Đây có thể là pipe(2) hoặc mô tả tệp hai chiều như PF_LOCALsocket(2). Để lại các mô tả tập tin trong chế độ chặn, khi một đứa trẻ không được phép vào, nó đặt (có thể là bên ghi, nếu một ống) mô tả tệp của nó ở đuôi của FIFO và chặn read(2) ing một byte từ đọc -side (mà sẽ là cùng một fd nếu không phải là một đường ống).

Khi trẻ em thoát, nó sẽ kéo mục nhập từ phía trước của FIFO và write(2) s một byte vào bộ mô tả tệp ở đó. Điều này sẽ đánh thức quá trình con bị chặn trong read(2), và nó sẽ tiếp tục trên đường vui vẻ của nó vào nhà trẻ.

Như đã nêu trước đây, các biến điều kiện cũng đã được đề xuất. Tôi thường tránh chúng; chúng dễ bị lạm dụng và bạn đã sắp xếp hàng loạt quy trình nhập.

Trong cả trường hợp tín hiệu và bộ mô tả tệp, bộ đệm vòng của số nguyên đủ - vì vậy đó là tất cả trạng thái bạn cần lưu trữ trong FIFO.

FIFO yêu cầu xem xét cẩn thận. Vì nhiều tiến trình sẽ đọc và thao tác nó, nó cũng phải ở trong bộ nhớ dùng chung. Cho dù FIFO được thực hiện như là một bộ đệm vòng hoặc một số cách khác, bạn có thể sẽ muốn xác định một số giới hạn về độ dài của hàng đợi của bạn. Nếu có quá nhiều trẻ em trong dòng, có thể đến trẻ em chỉ cần "về nhà." Ngoài ra, bạn sẽ cần phải chắc chắn rằng bạn xử lý trường hợp của một FIFO rỗng một cách duyên dáng khi nhập/thoát, và đảm bảo rằng việc chuyển từ 1 người phục vụ sang 0 hoạt động như dự định. Vì bạn đang tuần tự hóa mục nhập/thoát với một semaphore, điều này nên đơn giản.

3

2 Cột chính xác mô hình các vấn đề thực tế

Trong khi đó là hấp dẫn để kết hợp số liệu thống kê vào đồng bộ, tối thiểu bạn cần phải đồng bộ hóa cho việc chăm sóc đứa trẻ này thực sự chỉ:

  • số vị trí tuyển dụng cho trẻ em là > 0 để trẻ em vào, nếu không trẻ sẽ chờ.
  • người lớn thoát khỏi 3 vị trí tuyển dụng của họ về mặt nguyên tử đối với nhau hoặc chờ. (Một người lớn từ chối chịu trách nhiệm nhiều hơn trên con đường ra không rõ ràng trong mô hình của bạn, nhưng ngăn chặn một sự bỏ đói xuất cảnh tương tự như đói nhà văn trong việc triển khai rwlock.)

Nhưng họ phải được ánh xạ chính xác

Khi Cột nhấn 0 họ buộc một chờ, vì vậy để mô hình này với 2 Cột bạn bắt đầu cài đặt, sử dụng của họ cần phải phù hợp với một vài chi tiết cụ thể hơn :

sem_init(adults_exiting_sem,1,1); /* Allow 1 adult to be decrementing */ 
sem_init(children_spots_sem,1,0); /* Allow no child without an adult */ 

Sau đó, mã xử lý có thể dựa vào các mô hình chính xác của Cột để buộc phải chờ đợi:

void handler(int pid,int maxtime, char* type) 
{ 
    int leave_t = (random() % (maxtime + 1)); 

    if(type == "child") 
    { 

    printf("%s %i%s\n",type,pid," attempting to enter..."); 
    sem_wait(children_spots_sem); 

    printf("%s %i%s\n",type,pid," entered!"); 
    sleep(leave_t); 

    sem_post(children_spots_sem); 
    } 
    else if(type == "adult") 
    { 
    /* probably an inline function */ 
    sem_post(children_spots_sem); 
    sem_post(children_spots_sem); 
    sem_post(children_spots_sem); 

    printf("%s %i%s\n",type,pid," entered."); 
    sleep(leave_t); 
    printf("%s %i%s\n",type,pid," attempting to leave..."); 

    /* adult exit funnel */ 
    sem_wait(adults_exiting_sem); 

    /* probably an inline function */ 
    sem_wait(children_spots_sem); 
    sem_wait(children_spots_sem); 
    sem_wait(children_spots_sem); 

    sem_post(adults_exiting_sem); 

    } 
    printf("%s %i%s\n",type,pid," left!"); 
} 

Và có phải lúc nào cũng hơn để muốn

Đương nhiên, bạn có thể muốn mở rộng hơn nữa mô hình theo:

  1. sử dụng để mô hình sem_timedwait cha mẹ từ bỏ rơi khỏi con cái của họ.
  2. thống kê lại giới thiệu bằng tính năng đồng bộ hóa bổ sung hoặc chỉ đăng nhập để phân tích bài đăng.
Các vấn đề liên quan