Tiếp theo Mục lục Trước
2. Quy trình quản lý và ngắt kết cấu 2.1 Nhiệm vụ và quá trình Bảng Mọi tiến trình trong Linux được cấp phát động một cấu trúc struct task_struct. Số lượng tối đa của các quá trình đó có thể được tạo ra trên Linux chỉ bị hạn chế bởi sự có mặt của bộ nhớ vật lý, và bằng (xem kernel / fork.c: fork_init ()): / * * Số lượng tối đa mặc định của đề tài được thiết lập đến một nơi an toàn * Giá trị: các cấu trúc sợi có thể mất nhiều nhất là nửa * bộ nhớ. * / max_threads = mempages / (THREAD_SIZE / PAGE_SIZE) / 2; trong đó, trên kiến trúc IA32, về cơ bản có nghĩa là num_physpages / 4. Như một ví dụ, trên một máy 512M, bạn có thể tạo chủ đề 32k. Đây là một cải tiến đáng kể so với giới hạn 4k-epsilon cho cũ (2.2 và trước đó) hạt nhân. Hơn nữa, điều này có thể được thay đổi trong thời gian chạy bằng cách sử dụng sysctl KERN_MAX_THREADS (2), hoặc chỉ đơn giản là sử dụng procfs giao diện hạt nhân tunables: # cat / proc / sys / kernel / đề-max 32.764 # echo 100000> / proc / sys / kernel / chủ đề -Max # cat / proc / sys / kernel / đề-max 100000 # gdb -q vmlinux / proc / kcore Core được tạo ra bởi `BOOT_IMAGE = 240ac18 gốc ro = 306 video = Matrox: 0x118 ': VESA. # 0 0x0 trong? ? () (Gdb) p max_threads $ 1 = 100000 Các thiết lập các quy trình trên hệ thống Linux được biểu diễn như là một tập hợp các cấu trúc task_struct struct được nối với nhau bằng hai cách: như một Hashtable, băm bởi pid, và như một vòng tròn, gấp đôi được liên kết danh sách sử dụng p-> next_task và p-> prev_task con trỏ. Các hashtable được gọi là pidhash [] và được định nghĩa trong include / linux / sched.h: / * PID băm. (? should này là năng động) * / #define PIDHASH_SZ (4096 >> 2) extern struct task_struct * pidhash [PIDHASH_SZ]; #define pid_hashfn (x) ((((x) >> 8) ^ (x)) & ( PIDHASH_SZ - 1)) Các nhiệm vụ được băm bằng giá trị pid của họ và các hàm băm trên là vụ để phân phối các yếu tố thống nhất trong phạm vi của họ (0 đến PID_MAX-1). Hashtable được sử dụng để nhanh chóng tìm thấy một công việc bởi pid nhất định, sử dụng find_task_pid () inline từ bao gồm / linux / sched.h: tĩnh inline struct task_struct * find_task_by_pid (int pid) {struct task_struct * p, ** htable = & pidhash [pid_hashfn (pid)]; for (p = * htable; p && p-> pid = pid;! p = p-> pidhash_next); trở lại p;} Các nhiệm vụ trên mỗi hashlist (tức là băm để cùng giá trị) được liên kết bởi p-> pidhash_next / pidhash_pprev được sử dụng bởi hash_pid () và unhash_pid () để chèn và loại bỏ một quá trình nhất định vào hashtable. Chúng được thực hiện dưới sự bảo vệ của các đọc-ghi spinlock gọi tasklist_lock đưa cho WRITE. Các danh sách gấp đôi được liên kết thông tư có sử dụng p-> next_task / prev_task được duy trì để người ta có thể đi qua tất cả các nhiệm vụ trên hệ thống một cách dễ dàng. Điều này đạt được bởi các for_each_task () vĩ mô từ bao gồm / linux / sched.h: #define for_each_task (p) cho (! P = & init_task; (p = p-> next_task) = & init_task;) Người sử dụng for_each_task () nên mất tasklist_lock cho READ. Lưu ý for_each_task rằng () được sử dụng init_task để đánh dấu sự khởi đầu (và kết thúc) của danh sách - đây là an toàn bởi vì nhiệm vụ nhàn rỗi (pid 0) không bao giờ thoát. Các bổ từ của quá trình hashtable hoặc / và các liên kết bảng tiến trình, đặc biệt là ngã ba (), exit () và ptrace (), phải mất tasklist_lock cho VIẾT. Điều thú vị hơn nữa là các nhà văn cũng phải vô hiệu hóa ngắt trên các CPU địa phương. Lý do cho điều này là không nhỏ: các send_sigio () chức năng đi danh sách công việc và do đó mất tasklist_lock cho READ, và nó được gọi từ kill_fasync () trong bối cảnh gián đoạn. Đây là lý do tại sao các nhà văn phải vô hiệu hóa ngắt trong khi độc giả không cần phải. Bây giờ chúng ta hiểu cách cấu trúc task_struct được liên kết với nhau, chúng ta hãy xem xét các thành viên của task_struct. Họ lỏng lẻo tương ứng với các thành viên của UNIX 'struct proc' và 'struct dùng kết hợp với nhau. Các phiên bản khác của UNIX tách các thông tin trạng thái nhiệm vụ thành một phần cần được lưu giữ bộ nhớ thường trú tại mọi thời điểm (gọi là "cấu trúc proc' mà bao gồm các trạng thái quá trình, lập kế hoạch thông tin, vv) và một phần khác mà chỉ cần thiết khi tiến trình đang chạy (gọi là "khu vực u 'bao gồm các tập tin bảng mô tả, thông tin hạn ngạch đĩa vv). Lý do duy nhất cho thiết kế xấu xí như là bộ nhớ là một nguồn lực rất khan hiếm. Hệ điều hành hiện đại (tốt, chỉ Linux vào lúc này nhưng những người khác, ví dụ như FreeBSD dường như để cải thiện theo hướng này hướng tới Linux) không cần cách ly như vậy và do đó duy trì quá trình nhà nước trong một bộ nhớ thường trú cấu trúc dữ liệu hạt nhân ở tất cả các lần. Cấu trúc task_struct được khai báo trong bao gồm / linux / sched.h và hiện tại là 1680 byte. Các lĩnh vực nhà nước được khai báo là: nhà nước dài biến động; / * -1 Unrunnable, 0 Runnable,> 0 ngừng * / #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_ZOMBIE 4 #define TASK_STOPPED 8 #define TASK_EXCLUSIVE 32 Tại sao TASK_EXCLUSIVE định nghĩa là 32 và 16 không? . Bởi vì 16 được sử dụng hết bởi TASK_SWAPPING và tôi quên để chuyển TASK_EXCLUSIVE lên khi tôi loại bỏ tất cả các tài liệu tham khảo để TASK_SWAPPING (đôi khi trong 2.3.x) Những biến động trong p-> khai báo nhà nước có nghĩa là nó có thể được sửa đổi đồng bộ (từ xử lý ngắt): TASK_RUNNING : là công việc được "nghĩa là" trên hàng đợi chạy. Lý do nó có thể chưa được trên runqueue là đánh dấu một nhiệm vụ như TASK_RUNNING và đặt nó trên runqueue không phải là nguyên tử. Bạn cần phải giữ runqueue_lock đọc-ghi spinlock cho đọc để nhìn vào runqueue. Nếu bạn làm như vậy, sau đó bạn sẽ thấy rằng tất cả các nhiệm vụ trên runqueue ở trạng thái TASK_RUNNING. Tuy nhiên, chuyện này là không đúng đối với những lý do giải thích ở trên. Tương tự như vậy, trình điều khiển có thể đánh dấu bản thân (hay đúng hơn là bối cảnh quá trình họ chạy trong) như TASK_INTERRUPTIBLE (hoặc TASK_UNINTERRUPTIBLE) và sau đó gọi lịch (), sau đó sẽ loại bỏ nó từ runqueue (trừ khi có một tín hiệu cấp phát, trong trường hợp này nó là còn lại trên runqueue). TASK_INTERRUPTIBLE: có nghĩa là nhiệm vụ đang ngủ nhưng có thể được đánh thức bởi một tín hiệu hoặc hết hạn của một bộ đếm thời gian. TASK_UNINTERRUPTIBLE: giống như TASK_INTERRUPTIBLE, ngoại trừ nó không thể được đánh thức. TASK_ZOMBIE: nhiệm vụ đã chấm dứt nhưng đã không có thu thập tình trạng của nó (wait () - ed cho) của phụ huynh (tự nhiên hoặc do con nuôi). TASK_STOPPED:. nhiệm vụ đã được ngừng lại, hoặc là do các tín hiệu điều khiển công việc hoặc do ptrace (2) TASK_EXCLUSIVE: đây không phải là một nhà nước riêng biệt nhưng có thể là OR-ed để một trong hai TASK_INTERRUPTIBLE hoặc TASK_UNINTERRUPTIBLE. Điều này có nghĩa là khi nhiệm vụ này đang ngủ trên một hàng đợi để chờ đợi với nhiều nhiệm vụ khác, nó sẽ được đánh thức một mình thay vì gây "sấm bầy đàn" vấn đề bằng cách thức dậy tất cả những người phục vụ. Cờ công tác có chứa thông tin về các trạng thái quá trình mà không tương quan độc quyền: cờ unsigned dài; / * Mỗi cờ quy trình, quy định dưới đây * / / * * mỗi quá trình cờ * / bản tin #define PF_ALIGNWARN 0x00000001 / * In cảnh báo alignment * / / * Chưa triển khai, chỉ cho 486 * / #define PF_STARTING 0x00000002 / * được tạo ra * / # define PF_EXITING 0x00000004 / * lấy tắt * / #define PF_FORKNOEXEC 0x00000040 / * chia hai nhưng không exec * / #define PF_SUPERPRIV 0x00000100 / * sử dụng đặc quyền của siêu người dùng * / #define PF_DUMPCORE 0x00000200 / * đổ lõi * / # định nghĩa các PF_SIGNALED 0x00000400 / * giết bởi một tín hiệu * / PF_MEMALLOC #define 0x00000800 / * Cấp phát bộ nhớ * / #define PF_VFORK 0x00001000 / * Wake up phụ huynh trong mm_release * / #define PF_USEDFPU 0x00100000 / * nhiệm vụ sử dụng FPU lượng tử này (SMP) * / Các lĩnh vực p-> has_cpu, p-> xử lý, p-> truy cập, p-> ưu tiên, p-> chính sách và p-> rt_priority có liên quan đến lịch trình và sẽ được xem xét sau. Các trường p-> mm và p-> active_mm điểm tương ứng với quá trình không gian địa chỉ "được miêu tả bởi cấu trúc mm_struct và không gian địa chỉ hoạt động nếu quá trình không có một thực tế (ví dụ như kernel chủ đề). Điều này giúp giảm thiểu TLB bừng trên chuyển mạch không gian địa chỉ khi công việc được lên kế hoạch ra. Vì vậy, nếu chúng ta đang lên kế hoạch trong các chủ đề hạt nhân (mà không có p-> mm) sau đó nó next-> active_mm sẽ được thiết lập để các prev-> active_mm của nhiệm vụ đã được lên lịch ra, mà sẽ được giống như prev-> mm nếu prev-> mm! = NULL. Không gian địa chỉ có thể được chia sẻ giữa các chủ đề nếu CLONE_VM cờ được thông qua để clone (2) gọi hệ thống hoặc bằng phương tiện của vfork (2) gọi hệ thống. Các lĩnh vực p-> exec_domain và p-> cá tính liên quan đến tính cách của nhiệm vụ , tức là cách các cuộc gọi hệ thống nhất định ứng xử để tranh đua với "cá tính" của hương vị nước ngoài của UNIX. Các lĩnh vực p-> fs chứa thông tin hệ thống tập tin, mà dưới Linux có nghĩa là ba mẩu thông tin: dentry thư mục gốc của điểm láp, rễ thay thế dentry thư mục của điểm láp, dentry thư mục làm việc hiện tại của điểm láp. Cấu trúc này cũng bao gồm một số tài liệu tham khảo, vì nó có thể được chia sẻ giữa các công việc nhân bản vô tính khi CLONE_FS cờ được thông qua để clone (2) gọi hệ thống. Các lĩnh vực p-> file này đều chứa các tập tin bảng mô tả. Điều này cũng có thể được chia sẻ giữa các tác vụ, cung cấp CLONE_FILES được quy định với clone (2) gọi hệ thống. Các lĩnh vực p-> sig chứa xử lý tín hiệu và có thể được chia sẻ giữa các công việc nhân bản vô tính bằng phương tiện của CLONE_SIGHAND. 2.2 Tạo và chấm dứt nhiệm vụ và đề hạt nhân sách khác nhau trên hệ điều hành xác định một "quá trình" theo những cách khác nhau, bắt đầu từ "thể hiện của một chương trình trong thực hiện" và kết thúc bằng "rằng đó là sản phẩm của clone (2) hoặc cuộc gọi fork (2) Hệ thống". Trong Linux, có ba loại quy trình: thread nhàn rỗi (s), đề hạt nhân, nhiệm vụ người dùng. Các thread nhàn rỗi được tạo ra tại thời gian biên dịch cho các CPU đầu tiên; đó là sau đó "bằng tay" tạo ra cho mỗi CPU bằng vòm cụ thể fork_by_hand () trong arch / i386 / kernel / smpboot.c, mà unrolls ngã ba (2) hệ thống gọi bằng tay (trên một số archs). Nhiệm vụ nhàn rỗi chia sẻ một cấu trúc init_task nhưng có một cấu trúc TSS tin, trong mảng init_tss mỗi CPU. Nhiệm vụ nhàn rỗi tất cả đã PID = 0 và không có nhiệm vụ khác có thể
đang được dịch, vui lòng đợi..