一、进程概述
进程(任务):是处于执行期的程序以及它所包含的资源的总称。
线程:是一种特殊的进程。
虚拟处理器:让进程觉得自己在独享处理器
虚拟内存:在获取和使用内存时,觉得自己拥有整个系统的所有内存资源。
线程之间,共享虚拟内存,拥有各自的虚拟处理器。
fork/exit/wait
二、进程描述符及其任务结构
内核把进程放在叫做任务队列的双向循环链表中。
链表中的每一项都是task_struct,成为进程描述符(process descriptor)。
2.1 进程描述符分配
Linux通过slab分配器分配task_struct。
进程的栈底或栈顶存放着struct thread_info,thread_info中存有task_struct,可以通过thread_info快速找到当前进程。
当前进程current,通过thread_info来获取。(include/asm-generic/current.h)
#ifndef __ASM_GENERIC_CURRENT_H
#define __ASM_GENERIC_CURRENT_H
#include <linux/thread_info.h>
#define get_current() (current_thread_info()->task)
#define current get_current()
#endif /* __ASM_GENERIC_CURRENT_H */
2.2 进程描述符存放
进程标识符PID,用pid_t表示,实际是一个int类型。
最大值为32768。
可以通过/proc/sys/kernel/pid_max修改系统最大进程数。
2.3 进程状态与设置
TASK_RUNNING(运行)
进程是可执行的;它正在执行,或者在运行队列中等待执行。
TASK_INITERRUPTIBLE(可中断)
进程正在睡眠(阻塞),等待某些条件的达成。
TASK_UNINITERRUPTIBLE(不可中断)
除了不会因为接收到信号而被唤醒,投入运行外,这个状态与可中断状态相同。
TASK_ZOMBIE(僵死)
进程已经结束了,但是其父进程还没有调用wait4()系统调用。
TASK_STOPPED(停止)
进程停止运行。
设置进程状态
#define set_task_state(tsk, state_value) \
set_mb((tsk)->state, (state_value))
#define set_current_state(state_value) \
set_mb(current->state, (state_value))
2.4 进程上下文
2.5 进程家族树
所有的进程都是PID为1的init进程的后代。
系统中每个进程都有一个父进程。
每个进程可以有零个或多个子进程。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/err.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/list.h>
unsigned int hook_mark1(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct udphdr *udph;
u16 dst_port,src_port;
u32 src_ip,dst_ip;
struct task_struct *task;
struct list_head *list;
iph = ip_hdr(skb);
if (iph->protocol == 17)
{
udph = (struct udphdr*)((u_int8_t*)iph + (iph->ihl << 2));
dst_port = ntohs(udph->dest);
src_port = ntohs(udph->source);
src_ip = ntohl(iph->saddr);
dst_ip = ntohl(iph->daddr);
if (dst_port == 53)
{
printk("----process parameter:\n");
printk("state:%d\n", current->state);
printk("prio:%d\n", current->prio);
printk("static_prio:%d\n", current->static_prio);
printk("normal_prio:%d\n", current->normal_prio);
printk("rt_priority:%d\n", current->rt_priority);
printk("policy:%d\n", current->policy);
printk("task pid:%d\n", current->pid);
printk("task tgid:%d\n", current->tgid);
printk("\n----child process:\n");
list_for_each(list, ¤t->children)
{
task = list_entry(list, struct task_struct, tasks);
if (task)
{
printk("task pid:%d\n", task->pid);
}
}
printk("----parent process:\n");
for (task = current; task != &init_task; task = task->parent)
{
printk("pid:%d\n", task->pid);
}
printk("----prev process:\n");
task = list_entry(task->tasks.prev, struct task_struct, tasks);
if (task)
{
printk("pid:%d\n", task->pid);
}
printk("----next process:\n");
task = list_entry(task->tasks.next, struct task_struct, tasks);
if (task)
{
printk("pid:%d\n", task->pid);
}
task = next_task(task);
if (task)
{
printk("pid:%d\n", task->pid);
}
printk("----all process:");
for_each_process(task)
{
printk("pid:%d\n", task->pid);
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho_marker1;
u8 nfho_marker1_flag = 0;
static int __init init_marker(void)
{
printk("init marker.\n");
nfho_marker1.hook=hook_mark1;
nfho_marker1.hooknum=NF_INET_POST_ROUTING;
nfho_marker1.pf=PF_INET;
nfho_marker1.priority=NF_IP_PRI_LAST;
nf_register_hook(&nfho_marker1);
nfho_marker1_flag = 1;
return 0;
}
static void __exit exit_marker(void)
{
if(nfho_marker1_flag == 1)
{
nf_unregister_hook(&nfho_marker1);
}
printk("exit marker.\n");
}
module_init(init_marker);
module_exit(exit_marker);
MODULE_VERSION("1.0.0_0");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gwy");
MODULE_ALIAS("the alias of module name");
MODULE_DESCRIPTION("the description about the use of the module");
输出结果:
三、进程创建
fork通过拷贝当前进程创建一个子进程,子进程与父进程的区别在于pid,ppid,以及资源计数。
exec读取可执行文件,载入地址空间开始运行。
3.1 写时拷贝
是一种推迟甚至免除拷贝的技术。
3.2 fork()
clone()
do_fork()
copy_process()
3.3 vfork()
不拷贝父进程页表项;
子进程作为父进程的一个线程运行,父进程阻塞,直到子进程退出或执行exec;
子进程不能向地址空间写入。
四、线程
线程是与其他进程共享资源的进程。
内核线程没有独立的地址空间(与进程的区别)只在内核运行。
五、进程销毁
exit()/do_exit()释放资源并处于TASK_ZOMBIE状态。
wait()释放内核栈、thread_info、task_struct。
孤儿进程,在当前进程组找一个线程作为父进程,不行的话,让init作为其父进程。