Rumah > Tutorial sistem > LINUX > Ketahui lebih lanjut tentang proses: Lihat proses anak, maklumat urutan dan status biasa

Ketahui lebih lanjut tentang proses: Lihat proses anak, maklumat urutan dan status biasa

PHPz
Lepaskan: 2024-07-25 08:00:14
asal
398 orang telah melayarinya

Ketahui lebih lanjut tentang proses: Lihat proses anak, maklumat urutan dan status biasa

S&J

ps Lihat semua proses kanak-kanak? pstree-ppid

ps-eLf Apakah maksud setiap tatasusunan?

PID:process id 进程id
PPID: parent process id 父进程id
LWP:表示这是个线程;要么是主线程(进程),要么是线程
NLWP: num of light weight process 轻量级进程数量,即线程数量
TIME: 占用的CPU总时间
C CPU利用率,以整数表示。
Salin selepas log masuk

Lihat kadar penggunaan cpu/memori video/keutamaan dan maklumat lain bagi semua rangkaian di bawah proses? atas-H-p25120

Lihat CPU yang mana benang sedang berjalan? ps-eoruser,pid,ppid,lwp,psr,args-L|grepl3-agent

Status proses biasa?

-D:不可被唤醒的睡眠状态,通常用于 I/O 情况。
-R:该进程正在运行。
-S:该进程处于睡眠状态,可被唤醒。
-T:停止状态,可能是在后台暂停或进程处于除错状态。
-W:内存交互状态(从 2.6 内核开始无效)。
-X:死掉的进程(应该不会出现)。
-Z:僵尸进程。进程已经中止,但是部分程序还在内存当中。
-<:高优先级(以下状态在 BSD 格式中出现)。
-N:低优先级。
-L:被锁入内存。
-s:包含子进程。
-l:多线程(小写 L)。
-+:位于后台。
Salin selepas log masuk

Enam keadaan proses dan proses penukarannya (kitaran hayat) - keadaan sedia, keadaan berjalan, keadaan tidur nyenyak, keadaan tidur ringan, keadaan berhenti dan keadaan zombi.

Apakah proses ibu-anak yang dikongsi? bezanya? ——Selepas fork, proses ibu dan anak berkongsi deskriptor fail dan kawasan pemetaan yang dibina oleh mmap Lain-lain disalin, tetapi ruang proses dan juga alamat proses proses itu sama sekali dengan proses induk, dan ia adalah. ruang bebas sepenuhnya)

Perbezaan antara fork dan vfork - vfork digunakan untuk mencipta proses baharu, dan tujuan proses baharu adalah untuk melaksanakan program baharu. Perbezaan 1: Ruang alamat proses induk tidak disalin ke proses anak. Perbezaan 2: vfork memastikan proses anak berjalan dahulu Selepas ia memanggil exec atau (keluar), proses induk boleh dijadualkan untuk dijalankan.

**Adakah nilai alamat memori video yang dikongsi berada dalam julat ruang proses pengguna? **Selepas garpu, adakah anak akan memproses menggunakan alamat yang sama seperti proses induk? Jika ia adalah proses yang berbeza, adakah alamat yang dipaparkan dalam ruang proses memori video kongsi adalah sama?

Kitaran hayat proses, keadaan sedia dan keadaan berjalan adalah sama secara berangka dan ditakrifkan oleh makro TASK_RUNNING.

Bagaimana Task_struct diurus dalam Linux?

Fahami proses zombi - ia menyerahkan hampir semua ruang memori video, tidak mempunyai kod boleh laku dan tidak boleh dijadualkan Ia hanya mengekalkan kedudukan dalam senarai proses (sumber telah lama dikeluarkan, dan struktur Task_struct masih ada). . Jika proses induknya tidak memasang fungsi pemprosesan isyarat SIGCHLD dan memanggil wait()/waitpid() untuk menunggu proses anak tamat, dan tidak mengabaikan isyarat secara jelas, maka ia masih akan kekal dalam keadaan zombi proses induk tamat, maka proses init secara manual Ia akan mengambil alih proses anak ini dan membersihkannya dalam komuniti LINUX Ia masih boleh dihapuskan. Jika proses induk tidak berakhir, proses anak akan kekal dalam keadaan zombie, itulah sebabnya kadang-kadang terdapat banyak proses zombi dalam sistem. Nombor proses yang boleh digunakan oleh sistem adalah terhad (cat/proc/sys/kernel/pid_max) Jika sejumlah besar proses zombi terbentuk, sistem tidak akan dapat membentuk proses baharu kerana tiada nombor proses yang tersedia.

Subproses kitar semula:

Proses ibu bapa menunggu proses anak tamat melalui wait/waitpid dan fungsi lain, yang akan menyebabkan proses ibu bapa hang. Jika proses induk sangat sibuk, anda boleh menggunakan fungsi isyarat untuk memasang pengendali untuk SIGCHLD Kerana selepas proses anak tamat, proses induk akan menerima isyarat dan boleh memanggil tunggu di pengendali untuk mengitar semula. Jika proses induk tidak mengambil berat apabila proses anak tamat, ia boleh menggunakan isyarat (SIGCHLD, SIG_IGN) untuk memberitahu kernel bahawa ia tidak berminat dengan penghujung proses anak Selepas proses anak tamat, kernel akan mengitar semulanya dan tidak lagi menghantar mesej kepada proses induk Terdapat juga beberapa cara untuk memberi isyarat, iaitu, garpu dua kali proses induk dan kemudian terus berfungsi cara, proses anak diambil alih oleh init Selepas proses anak tamat, init akan mengitar semulanya. Walau bagaimanapun, anda perlu melakukan kitar semula proses kanak-kanak itu sendiri.

Mengapa proses anak memasuki keadaan zombi selepas ia tamat - Kerana proses ibu bapa mungkin perlu mendapatkan status keluar dan maklumat lain proses anak.

Adakah keadaan zombie adalah keadaan yang mesti dilalui oleh setiap proses kanak-kanak - Mana-mana proses kanak-kanak (kecuali init) tidak hilang serta-merta selepas keluar(), tetapi meninggalkan struktur data yang dipanggil proses zombi (Zombie) (Ia menduduki beberapa memori video? sumber, iaitu, masih terdapat rekod dalam jadual proses), menunggu proses induk diproses. Jika proses induk tidak mempunyai masa untuk memproses proses anak selepas keluar (), maka anda boleh menggunakan arahan ps untuk melihat bahawa status proses anak ialah "Z". Jika proses induk keluar sebelum proses anak tamat, proses anak akan diambil alih oleh init. init akan memproses proses anak dalam keadaan zombie sebagai proses induk.

Cara untuk menghapuskan proses zombi:

Tulis semula proses ibu bapa dan kumpulkan mayat proses anak selepas kematiannya. Kaedah khusus ialah menerima isyarat SIGCHLD. Selepas proses anak mati, isyarat SIGCHLD dihantar ke proses induk Selepas proses induk menerima isyarat ini, ia melaksanakan fungsi waitpid() untuk mengumpul mayat proses anak. Ini berdasarkan prinsip bahawa walaupun proses induk tidak memanggil tunggu, kernel akan menghantarnya mesej SIGCHLD Walaupun pemprosesan lalai adalah untuk mengabaikannya, jika anda ingin membalas mesej ini, anda boleh menetapkan fungsi pemprosesan . Isyarat SIGCHLD: Apabila proses anak tamat, proses induk akan menerima isyarat ini. Jika proses induk tidak mengendalikan isyarat ini dan tidak menunggu proses anak, proses anak sebenarnya ditamatkan dan akan menduduki entri dalam jadual proses kernel Pada masa ini, proses anak dipanggil proses zombie. Kita harus mengelakkan situasi seperti ini (proses induk sama ada mengabaikan isyarat SIGCHILD, atau menangkapnya, atau menunggu proses anak yang dihasilkannya, atau proses induk ditamatkan dahulu, dan kemudian penamatan proses anak diambil alih secara manual oleh proses init).

Fungsi exit() apabila proses ditamatkan, jadi apakah penamatan benang? ——Tiga situasi pengguguran benang:

Benang baru kembali daripada fungsi permulaan, dan nilai pulangan ialah kod keluar utas. Thread boleh dibatalkan oleh thread lain dalam proses yang sama. Urutan memanggil pthread_exit.

Jika anda tidak menunggu benang dan tidak berminat dengan nilai pulangan benang, anda boleh menetapkan benang kepada keadaan tertanggal dan biarkan sistem menuntut semula sumber yang didudukinya secara manual apabila benang itu keluar. A thread tidak boleh memanggil pthread_detach sendiri untuk menukar dirinya kepada keadaan tertanggal Ia hanya boleh memanggil pthread_detach oleh thread lain.

pthread_cancel() membenarkan satu thread untuk membatalkan thread lain yang ditentukan oleh th.

Proses - unit terkecil peruntukan sumber, benang - unit terkecil pelaksanaan program. Suatu proses mempunyai ruang alamat bebas, dan utas tidak mempunyai ruang alamat bebas (benang dalam proses yang sama berkongsi ruang alamat proses tersebut, ia tidak akan menjejaskan proses lain dalam mod dilindungi, dan a utas hanyalah sebahagian daripada laluan pelaksanaan yang berbeza. Benang mempunyai tindanan sendiri dan pembolehubah setempat, tetapi utas tidak mempunyai ruang alamat yang berasingan Jika satu utas ditutup, ini bermakna keseluruhan proses telah lari Oleh itu, program berbilang proses lebih kuat daripada program berbilang benang menggunakan lebih banyak sumber apabila menukar proses Lebih besar, kurang cekap. Walau bagaimanapun, untuk beberapa operasi serentak yang memerlukan pelaksanaan serentak dan perkongsian pembolehubah individu, hanya benang, bukan proses, boleh digunakan.

Sebab guna multi-threading?

Proses

Proses ialah unit asas peruntukan sumber, dan utas ialah unit asas penjadualan

Memproses maklumat

Penjadual Linux sebenarnya mengenal pasti task_struct untuk penjadualan. Terlepas dari utas proses, lapisan bawah sepadan dengan task_struct Perbezaan antara proses dan utas ialah bilangan sumber yang dikongsi tidak ada sumber yang dikongsi antara kedua-dua proses dan semua sumber dikongsi antara dua utas.

Blok kawalan proses PCB(ProcessControlBlock)

task_struct ialah perihalan proses oleh kernel Linux, yang juga boleh dipanggil "deskriptor proses". Penerangan tentang struktur yang menyimpan semua sumber yang diperlukan oleh proses. /proc/${pid} memproses maklumat berkaitan. Bagi sistem pengendalian, proses ialah struktur data.

struct task_struct {
longstate; // 进程状态-1为不可运行, 0为可运行, >0为已中断

struct mm_struct*mm; // 指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方
	pid_t pid; // 进程标识符,用来代表一个进程
 
struct task_struct __rcu*parent; // 指向父进程的指针
struct list_headchildren; // 子进程列表
 	struct list_head sibling; // 兄弟进程
struct fs_struct*fs;// 存放文件系统信息的指针
 
struct files_struct *files; // 一个数组,包含该进程打开的文件指针
unsigned int policy; // 调度策略:一般有FIFO,RR,CFS
...
};
Salin selepas log masuk

从2.6版本之后,Linux改用了slab分配器动态生成task_struct,只须要在栈底(向上下降的栈)或栈顶(向下下降的栈)创建一个新的结构structthread_info(这儿是栈对象的尾端),你可以把slab分配器觉得是一种分配和释放数据结构的优化策略。通过预先分配和重复使用task_struct,可以防止动态分配和释放带来的资源消耗。

进程的地址空间ref

所谓进程地址空间(processaddressspace),就是从进程的视角听到的地址空间,是进程运行时所用到的虚拟地址的集合。

进程的显存

程序段(Text):程序代码在显存中的映射,储存函数体的二补码代码。

初始化过的数据(Data):在程序运行初早已对变量进行初始化的数据。

未初始化过的数据(BSS):在程序运行初未对变量进行初始化的数据。

栈(Stack):储存局部、临时变量,函数调用时,储存函数的返回表针,用于控制函数的调用和返回。在程序块开始时手动分配显存,结束时手动释放显存,其操作方法类似于数据结构中的栈。

堆(Heap):储存动态显存分配,须要程序员手工分配,手工释放.注意它与数据结构中的堆是两码事,分配方法类似于数组。

注:1.Text,BSS,Data段在编译时早已决定了进程将占用多少VM

可以通过size,晓得这种信息:

正常情况下,Linux进程不能对拿来储存程序代码的显存区域执行写操作,即程序代码是以只读的形式加载到显存中,但它可以被多个进程安全的共享。

创建进程后都创建了什么资源

进程创建

system()通过调用shell启动一个新进程

exec()以替换当前进程映像的方法启动一个新进程

fork()以复制当前进程映像的方法启动一个新进程

fork(2)

执行fork后,父进程的task_struck对拷给子进程,母子进程最初资源完全一样,而且是两份不同的拷贝,因而任何改动都导致两者的分裂。

兄妹进程对显存资源(mm)的管理使用了COW(Copy-On-Write,写时拷贝)技术:

在fork之前,一片显存区对应一份数学地址和一份虚拟地址,显存区的权限为RW;在fork以后,母子进程听到的显存区虚拟地址相同,化学地址也相同,母女进程使用的虽然是同一片化学显存,未发生显存拷贝,操作系统会将此显存区权限改为RO;父或子进程对显存区执行写操作将触发PageFault,操作系统此时会将显存区拷贝一份,母女进程见到的虚拟地址仍旧一样,而且化学地址早已不同。各进程虚拟地址到化学地址的映射由MMU(MemoryManagementUnit,显存管理单元)管理。fork运行在有MMU的CPU上。

對於無MMU的CPU,難以應用COW,難以支援fork。無MMU的CPU使用vfork建立進程,父進程將始終阻塞直至子進程exit或exec。 vfork和fork的本質差異是,vfork中的母子程序共用同一片顯存區。

fork(2)系統呼叫用於建立一個新進程,稱為子進程,它與父進程同時運行(並發),且運行次序不定(非同步)。 pid_t是一個巨集定義,其實質是int,若成功則回傳兩個值,子程序回傳0,父行程回傳子程序ID;否則,錯回傳-1

父級的整個虛擬位址空間在子進程中複製,包括互斥鎖的狀態,

子進程與父進程完全相同,不只以下幾點:

行程回收wait()和waitpid()

透過waitpid()/wait()回收子程序的task_struct結構。

區別:

孤兒行程與殭屍行程殭屍行程

殭屍進程:一個進程使用fork創建子進程,如果子進程退出,而父進程並沒有調用wait或waitpid獲取子進程的狀態信息,這麼子進程的進程描述符始終保存在系統中。這些進程稱之為僵死進程。 (子進程可以透過/proc/$pid看見,執行緒沒有)

一個進程在調用exit命令結束自己的生命的時侯,雖然它並沒有真正的被銷毀,而是留下一個稱為殭屍進程(Zombie)的資料結構(系統調用exit,它的作用是使進程退出,但也僅限於將一個正常的進程弄成一個殭屍進程,並不能將其完全銷毀)。

在Linux進程的狀態中,殭屍進程是十分特殊的一種,它早已捨棄了幾乎所有顯存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等資訊供其他流程蒐集,除此之外,殭屍行程不再佔有任何顯存空間。它須要它的父進程來為它收屍。

假如他的父進程沒安裝SIGCHLD訊號處理函數呼叫wait或waitpid()等待子程序結束,又沒有明確忽視該訊號,這麼它就仍然保持殭屍狀態,假如這時父進程結束了,這麼init進程手動會接手這個子進程,為它收屍,它還是能被消除的。

而且假如父進程是一個循環,不會結束,這麼子進程都會仍然保持殭屍狀態,這就是為何系統中有時會有好多的殭屍進程。系統所能使用的進程號是有限的,假如大量的形成僵死進程,將由於沒有可用的進程號而造成系統不能形成新的進程.

殭屍行程形成緣由:子行程結束後向父行程發出SIGCHLD訊號,父行程預設忽略了它;父行程沒有呼叫wait()或waitpid()函式來等待子行程的結束。防止殭屍行程方式:父行程呼叫wait()/waitpid()等待子程序結束,這樣處理父行程通常會阻塞在wait處而不能處理其他事情。捕捉SIGCHLD訊號號,並在訊號處理函數上面呼叫wait函數,這樣處理可防止1中描述的問題。 fork兩次,父進程創建父親進程,父親進程再創建一個兒子進程,之後母親進程自盡,兒子進程成為孤兒進程被init進程收留。孤兒進程

孤兒進程:父進程退出,而它的一個或多個子進程還在運行,這麼這些子進程將成為孤兒進程。孤兒程序將被init程序(pid=1)所收留,並由init程序對它們完成狀態蒐集工作。 (假若係統中出現孤兒進程,說明主進程在退出前沒有清除子進程)

線程(LightweightProcess,LWP)

同一进程的多个线程获取进程ID时得到的是惟一ID值。Linux同一进程的多线程,在内核视角实际上每位线程都有一个PID,但在用户空间须要getpid()返回惟一值,Linux使用了一个小方法,引入了TGID的概念linux进程与线程 内核,getpid()返回的的TGID值。

pthread_create()

Linux线程本质上就是进程,只是与进程间资源共享方法不同,线程间共享所有资源。每位线程都有自己的task_struct,因而每位线程都可被CPU调度。多线程间又共享同一进程资源。

在一个线程中创建了另外一个线程,主线程要等到创建的线程返回了,获取该线程的返回值后主线程才退出。这个时侯就须要用到线程挂起。pthread_join函数用于挂起当前线程,直到指定的线程中止为止。

说线程的PID,是指用户空间的进程ID,值就是TGID(threadgroupIDforthethreadgroupleader);当非常强调,线程在内核空间的PID,则指线程在内核中task_struct里特有的PID。top–H命令从线程视角显示CPU占用率。不带参数的top命令,进程ID是主线程的PID(也就是TGID)。

Linux的进程和线程

进程是处于运行期的程序和相关资源的统称,具备一些要素:

拥有一段可执行程序代码。如同一场戏须要一个剧本。代码段可以多个进程共用,如同许多场表演都可以用一份剧本一样。拥有一段进程专用的系统堆栈空间。可以觉得是这个进程的“私有财产”,相应的,也肯定有系统空间堆栈在系统中有进程控制块(或称进程描述符,本文两种说法通用)描述这个进程的相关信息。可以觉得是进程的“户口”。系统通过这个控制块来控制进程的相关行为有独立的储存空间,也就是专有的用户空间,相应的又会有用户空间堆栈。理解各类ID

# ps -eo ppid,pid,tid,lwp,tgid,pgrp,sid,tpgid,args -L | awk &#039;{if(NR==1) print $0; if($9~/a.out/) print $0}&#039;
 PPID PID TID LWPTGIDPGRP SID TPGID COMMAND
 579046 2436128 2436128 2436128 2436128 2436128579046 2436128 ./a.out
 579046 2436128 2436129 2436129 2436128 2436128579046 2436128 ./a.out
 579046 2436128 2436130 2436130 2436128 2436128579046 2436128 ./a.out
Salin selepas log masuk

pidstat-t[-ppid号]可以复印出线程之间的关系。

各类ID最后都归结到pid和lwp(tid)上。所以理解各类ID,最终归结为理解pid和lwp(tid)的联系和区别。

PID:进程ID。

LWP:线程ID。在用户态的命令(例如ps)中常用的显示方法。

TID:线程ID,等于LWP。TID在系统提供的插口函数中更常用,例如syscall(SYS_gettid)和syscall(__NR_gettid)。

TGID:线程组ID,也就是线程组leader的进程ID,等于PID。

pgid(processgroupID):进程组ID,也就是进程组leader的进程ID。

pthreadid:pthread库提供的ID,生效范围不在系统级别,可以忽视。

sid:sessionIDforthesessionleader。

TPGID:ttyprocessgroupIDfortheprocessgroupleader。

上图挺好地描述了用户视角(userview)和内核视角(kernelview)看见线程的差异:

轮询

輪詢(用戶級線程),這是對核心透明的,也就是係統並不曉得有輪詢的存在,是完全由用戶的程式自己調度的,由於是由用戶程式自己控制,這麼就很難像佔領式調度那樣做到強制的CPU控制權切換到其他進程/線程,一般只能進行協作式調度,須要輪詢自己主動把控制權出售出去以後,其他輪詢能夠被執行到。

goroutine和解釋器區別

本質上,goroutine就是輪詢。不同的是,Golang在runtime、系統呼叫等多方面對goroutine調度進行了封裝和處理,當遇見長時間執行或則進行系統調用時,會主動把當前goroutine的CPU(P)出售出去,讓其他goroutine能被調度並執行linux維運招聘,也就是Golang從語言層面支持了解釋器。

其他方面不同進程調度

linux核心的三種主要調度策略:

1,SCHED_OTHER分時調度策略,

2,SCHED_FIFO即時調度策略linux進程與執行緒 核心,先到先服務

3,SCHED_RR即時調度策略,時間片輪轉

即時進程調度

SCHED_FIFO:不同優先順序依優先順序高的先挪到睡眠,優先順序低的再跑;同等優先權先進先出。

SCHED_RR:不同優先順序依優先順序高的先挪到睡眠,優先順序低的再跑;同等優先輪替。

內核RT補丁:

如下兩個參數

/proc/sys/kernel/sched_rt_period_us

/proc/sys/kernel/sched_rt_runtime_us

表示在period的時間裡RT最多只能跑runtime的時間

普通進程調度

SCHED_OTHER:

CFS:完全公正調度(新核心)

黑紅樹,右側節點大於左側節點的值

運行到目前為止vruntime最小的進程

同時考慮了CPU/IO和nice

總是找vruntime最小的執行緒調度。

vruntime=pruntime/weight×1024;

vruntime是虛擬運行時間,pruntime是化學運行時間,weight權重由nice值決定(nice越低權重越高),則運行時間少、nice值低的的線程vruntime小,將得到優先調度。這是一個隨運行而動態變化的過程。

內核態與用戶態內核空間與用戶空間

Linux的虛擬位址空間範圍為0~4G,Linux核心將這4G位元組的空間分成兩份,將最高的1G位元組(從虛擬位址0xCxC0000000到0xFFFFFFFF)供核心使用,稱為「核心空間」。而將較低的3G位元組(從虛擬位址0x00000000到0xBFFFFFFF)供各個進程使用,稱為「用戶空間。由於每位進程可以透過系統呼叫步入內核,因而,Linux內核由系統內的所有進程共享。

Linux使用兩級保護機制:0級供核心使用,3級供用戶程式使用,每位進程有各自的私有用戶空間(0~3G),這個空間對系統中的其他進程是不可見的,最高的1GB位元組虛擬核心空間則為所有行程以及核心所共用。

核心空間中儲存的是核心程式碼和數據,而進程的使用者空間中儲存的是使用者程式的程式碼和資料。不管是核心空間還是使用者空間,它們都處於虛擬空間。其實核心空間搶佔了每位虛擬空間中的最高1GB字節,但映射到化學顯存卻總是從最低位址(0x00000000),另外,使用虛擬位址可以挺好的保護核心空間被用戶空間破壞,虛擬位址到化學位址轉換過程有作業系統和CPU共同完成(作業系統為CPU設定好頁表,CPU透過MMU單元進行位址轉換)。

註:多任務作業系統中的每一個進程都運行在一個屬於它自己的顯存沙盒中,這個沙盒就是虛擬位址空間(virtualaddressspace),在32位元模式下,它總是一個4GB的記憶體位址塊。這種虛擬位址透過頁表(pagetable)映射到化學顯存,頁表由作業系統維護並被處理器引用。每位行程都擁有一套屬於它自己的頁表。

進程顯存空間分佈如右圖:

顯存

堆向下下降,棧向上下降(為何我的是相反的呢???)

堆疊、記憶體的下降方向與大小端

低->|------------------|

|全域量(所有已初始化量.data,|

|未初始化量.bss)|

堆起始->|---------------|

|堆向高位址下降|

||

||

|自由空間|

||

||

|棧向低位址下降|

高堆疊起始->|------------------|

Atas ialah kandungan terperinci Ketahui lebih lanjut tentang proses: Lihat proses anak, maklumat urutan dan status biasa. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:itcool.net
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan