天天看點

Linux下程序的狀态查詢與多程序程式設計入門

作者:haifeng695

一,程序的概念

程序是可并發執行的程式在一個資料集合上的運作過程。從這句描述可以看出,程序是一個動态的運作過程,它和程式有本質上的差別。程式是靜态的檔案,是儲存在磁盤上的指令、資料等的有序集合,沒有動态執行的概念,而程序則是程式的動态執行過程,包括動态建立、動态排程、動态執行、動态消亡的整個過程。程序是程式執行時,資源管理的最小機關。

二,Linux下程序的資訊檢視

在Linux下,每個程序被建立的時候都會被配置設定一個資料結構,稱為程序控制塊PCB。PCB中包含了程序的描述資訊、控制資訊和資源資訊,PCB是程序的一個靜态說明書。

Linux下的PCB資訊定義在一個task_struct結構體裡,該結構體放在include/linux/sched.h 頭檔案裡,大家感興趣可以直接打開頭檔案檢視完整的内容。task_struct結構體定義單獨拷貝出來放在這裡供大家參考,不感興趣的可以跳過這一段,直接閱讀第三節“Linux下程序資訊的檢視方法”。

struct task_struct {
    /*
     * offsets of these are hardcoded elsewhere - touch with care
     */
    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */ //程序目前的狀态
    unsigned long flags;    /* per process flags, defined below */    //反應程序狀态的資訊,但不是運作狀态,定義見下
    int sigpending; //程序收到了信号,但尚未處理
    mm_segment_t addr_limit;    /* thread address space: //虛存位址上限
                         0-0xBFFFFFFF for user-thead
                        0-0xFFFFFFFF for kernel-thread
                     */
    struct exec_domain *exec_domain;
    volatile long need_resched;    //與程序排程有關表示使用者從系統空間按傳回使用者空間要執行的一次排程
    unsigned long ptrace;
 
    int lock_depth;        /* Lock depth */
 
/*
 * offset 32 begins here on 32-bit platforms. We keep
 * all fields in a single cacheline that are needed for
 * the goodness() loop in schedule().
 */
    long counter; //與程序排程相關
    long nice;
    unsigned long policy;    //實用于本程序的排程政策
    struct mm_struct *mm;
    int processor;
    /*
     * cpus_runnable is ~0 if the process is not running on any
     * CPU. It's (1 << cpu) if it's running on a CPU. This mask
     * is updated under the runqueue lock.
     *
     * To determine whether a process might run on a CPU, this
     * mask is AND-ed with cpus_allowed.
     */
    unsigned long cpus_runnable, cpus_allowed;
    /*
     * (only the 'next' pointer fits into the cacheline, but
     * that's just fine.)
     */
    struct list_head run_list;
    unsigned long sleep_time;
 
    struct task_struct *next_task, *prev_task; //核心會對每一個程序做點什麼事情的時候,常常需要将其連成一個隊列,這2個指針用于這個目的
    struct mm_struct *active_mm;
    struct list_head local_pages;
    unsigned int allocation_order, nr_local_pages;
 
/* task state */
    struct linux_binfmt *binfmt;//應用檔案格式
    int exit_code, exit_signal;
    int pdeath_signal;  /*  The signal sent when the parent dies  */
    /* ??? */
    unsigned long personality; //程序的個性化資訊,詳細見下
    int did_exec:1;
    unsigned task_dumpable:1;
    pid_t pid; //程序号
    pid_t pgrp;
    pid_t tty_old_pgrp;
    pid_t session;
    pid_t tgid;
    /* boolean value for session group leader */
    int leader;
    /* 
     * pointers to (original) parent process, youngest child, younger sibling,
     * older sibling, respectively.  (p->father can be replaced with 
     * p->p_pptr->pid)
     */
    struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; //用于族譜資訊的,例如p_opptr指向父程序
    struct list_head thread_group;
 
    /* PID hash table linkage. */
    struct task_struct *pidhash_next;
    struct task_struct **pidhash_pprev; //pid是随機配置設定的,我們常常使用kill pid想程序發送信号(大部分人認為是殺死程序,其實這是個發送信号的指令,預設的參數為殺死。如果想暫停某程序,隻需kill STOP 程序的PID),這裡可以看到根據pid尋找程序的操作是經常被使用的,而pid又是随機配置設定,于是這裡邊用這2個指針指向一個雜湊數組,數組是按照雜湊的算法,以pid為關鍵字建立,友善根據pid來尋找task_struct
 
    wait_queue_head_t wait_chldexit;    /* for wait4() */
    struct completion *vfork_done;        /* for vfork() */
    unsigned long rt_priority;    //優先級
    unsigned long it_real_value, it_prof_value, it_virt_value;
    unsigned long it_real_incr, it_prof_incr, it_virt_incr;
    struct timer_list real_timer;
    struct tms times; //運作時間的總彙
    unsigned long start_time;
    long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; //在多個處理器上運作于系統空間和使用者空間的時間
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
    unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;//發生頁面異常的次數和換入換出的次數
    int swappable:1;
/* process credentials */
    uid_t uid,euid,suid,fsuid;
    gid_t gid,egid,sgid,fsgid; //與檔案權限有關的
    int ngroups;
    gid_t    groups[NGROUPS];
    kernel_cap_t   cap_effective, cap_inheritable, cap_permitted; //權限,比如該程序是否有權限從新引導系統,這裡是大概介紹
    int keep_capabilities:1;
    struct user_struct *user;    //指向該程序擁有的使用者
/* limits */
    struct rlimit rlim[RLIM_NLIMITS]; //程序對各種資源使用數量的限制,詳細見下
    unsigned short used_math;
    char comm[16];
/* file system info */
    int link_count, total_link_count;
    struct tty_struct *tty; /* NULL if no tty */
    unsigned int locks; /* How many file locks are being held */
/* ipc stuff */
    struct sem_undo *semundo;
    struct sem_queue *semsleeping;
/* CPU-specific state of this task */
    struct thread_struct thread;
/* filesystem information */
    struct fs_struct *fs;
/* open file information */
    struct files_struct *files;
/* namespace */
    struct namespace *namespace;
/* signal handlers */
    spinlock_t sigmask_lock;    /* Protects signal and blocked */
    struct signal_struct *sig;
 
    sigset_t blocked;
    struct sigpending pending;
 
    unsigned long sas_ss_sp;
    size_t sas_ss_size;
    int (*notifier)(void *priv);
    void *notifier_data;
    sigset_t *notifier_mask;
    
/* Thread group tracking */
       u32 parent_exec_id;
       u32 self_exec_id;
/* Protection of (de-)allocation: mm, files, fs, tty */
    spinlock_t alloc_lock;
 
/* journalling filesystem info */
    void *journal_info;
};           

關于task_struct結構體中重要資訊的簡單說明:

1、pid辨別符:與程序相關的唯一辨別符,差別正在執行的程序和其他程序

2、status狀态:描述程序的狀态,因為程序有阻塞、挂起、運作等好幾個狀态,是以都有個表示符來記錄程序的執行狀态。

3、prio優先級:如果有好幾個程序正在執行,就涉及到程序的執行的先後順序,這和程序的優先級這個辨別符有關。

4、程式計數器:程式中即将被執行指令的下一條位址。

5、記憶體指針:程式代碼和程序相關資料的指針。

6、上下文資料:程序執行時處理器的寄存器中的資料。

7、I/O狀态資訊:包括顯示的I/O請求,配置設定給程序的I/O裝置和被程序使用的檔案清單。

8、記賬資訊:包括處理機的時間總和,記賬号等等。

三, Linux下程序資訊的檢視方法

本文隻講述最為常見的3種程序資訊的檢視方式,通過這三種方式基本可以滿足日常開發調試過程中對于程序資訊的擷取需求。

3.1 ps -elf 指令

該指令按照UNIX風格列印程序的資訊

Linux下程式的狀态查詢與多程式程式設計入門

如上圖所示,-elf 指令列印的資訊自左向右依次為:

UID: 程序所屬使用者

PID: 程序的ID

PPID: 該程序的父程序的ID

C:CPU占用率

PRI:程序優先級

NI:程序的nice值,用來做優先級的改變,注:PRI-NI=預設優先級。大家對nice用法感興趣可進一步百度查閱。

ADDR: 這個是 kernel function,指出該程式在記憶體的那個部分。如果是個 running的程式,一般就是 "-"

SZ:占用記憶體大小

WCHAN:目前這個程式是否正在運作當中,若為 "-" 表示正在運作

TTY:程序與終端連接配接的端口号

TIME:程序運作的總時間

CMD:開始程序執行的指令

3.2 ps -aux 指令

該指令,按照BSD風格列印程序的資訊,内容基本和-elf相同

Linux下程式的狀态查詢與多程式程式設計入門

如上圖所示,-aux指令列印的資訊自左向右一次為:

USER:程序所屬的使用者

PID:程序ID

%CPU:運作程序所占用的CPU百分比

%MEM:運作程序所占用的記憶體的百分比

VSZ:占用的虛拟記憶體的大小

RSS:占用記憶體的大小

TTY:程序與終端連接配接的端口号

STAT:程序運作的狀态

START:啟動時間

TIME:程序執行的時間

COMMAND:開始程序所執行的指令

3.3 top指令

如果我們想要檢視占用資源最多的程序,我們應該優先使用 “top” 指令,而非ps指令。 “top” 指令會按照資源占用的多少自動的排好序,并動态的列印在終端上。Top指令預設每3秒重新整理一次狀态,可以用 “-d”參數制定重新整理周期。這裡用預設值示範一下一般用法:

3.3.1 按CPU占用率從高到低排列:

“top -o %CPU”

Linux下程式的狀态查詢與多程式程式設計入門

3.3.2 按Memory占用率從高到低排列:

“top -o %MEM”

Linux下程式的狀态查詢與多程式程式設計入門

四, 多程序程式設計

Linux下通過調用fork函數建立一個程序,通過調用getpid函數可以擷取程序的PID。

fork函數和getpid函數定義在unistd.h 頭檔案裡,pid_t變量類型定義在sys/types.h頭檔案裡。

下面通過一個簡單的示例代碼來示範這兩個函數的用法,實作建立一個新的子程序,父程序建立完成後,等待子程序退出,然後列印“等到了子程序”後結束運作。

示例代碼如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int
main(void)
{
	pid_t pid_child;
	pid_t pid_wait;
	pid_child = fork();
	int count = 0;

	if(pid_child<0) /*create failed*/
		printf("Create children process failed.\n");
	else if(0 == pid_child) /*children*/
	{
		printf("I'm the new created children process. My pid is:%d.\n", getpid());
		sleep(2);
		printf("I'm the child. I'm exiting. My pid is:%d.\n", getpid());
	}
	else /*parent*/
	{
		printf("I'm the parent process. My pid is:%d.\n", getpid());
		do
		{
			printf("I'm waiting the child process, child pid is:%d.\n", pid_child);
			pid_wait = waitpid(pid_child, NULL, WNOHANG);
			if(0 == pid_wait) /*Child process not yet exited*/
			{
				sleep(1);
			}
			else
				break; 
			count ++;
		}while(count<10);

		if(pid_wait == pid_child)
			printf("Get the child exit info, child pid is:%d\n", pid_wait);
		else
			printf("Something is wrong, wait child exit timeout. child pid is:%d\n", pid_child);
	}

  return 0;
}
           

編譯執行後,結果如下:

Linux下程式的狀态查詢與多程式程式設計入門

可見,父程序建立子程序後就開始了等待子程序退出。而子程序退出後,父程序又sleep了2次,才得到子程序退出的傳回資訊。

這裡沒有特别設定優先級,父程序和子程序的運作次序完全由排程系統随機決定。

以上就是Linux下最簡單的多程序程式設計的示範,代碼照着樣子敲一邊就可以跑起來,希望對初學者能起到抛磚引玉的作用。

後面的文章,我會給大家進一步講解多程序之間的通信問題,以及給出一個實際的多程序通信的程式設計示例。

好了,本文就到這裡。謝謝大家!

繼續閱讀