2018-2019-1 20189221 《Linux核心原理與分析》第七週作業
阿新 • • 發佈:2018-11-23
2018-2019-1 20189221 《Linux核心原理與分析》第七週作業
實驗六
程式碼分析
task_struct:
struct task_struct { volatile long state; //程序狀態/* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; // 指定程序核心堆疊 pid_t pid; //程序識別符號 unsigned int rt_priority; //實時優先順序 unsigned int policy; //排程策略 struct files_struct *files; //系統開啟檔案 … }
fork、vfork 和 clone 都可建立新程序,均通過 do_fork 來建立程序
do_fork程序:
新建程序:
/* 1694 * Create a kernel thread. 1695 */ 1696pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) 1697{ 1698 return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn, 1699 (unsigned long)arg, NULL, NULL); 1700} 1701 1702#ifdef __ARCH_WANT_SYS_FORK 1703SYSCALL_DEFINE0(fork) 1704{ 1705#ifdef CONFIG_MMU 1706 return do_fork(SIGCHLD, 0, 0, NULL, NULL); 1707#else 1708 /* can not support in nommu mode */ 1709 return -EINVAL; 1710#endif 1711} 1712#endif 1713 1714#ifdef __ARCH_WANT_SYS_VFORK 1715SYSCALL_DEFINE0(vfork) 1716{ 1717 return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, 1718 0, NULL, NULL); 1719} 1720#endif 1721 1722#ifdef __ARCH_WANT_SYS_CLONE 1723#ifdef CONFIG_CLONE_BACKWARDS 1724SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, 1725 int __user *, parent_tidptr, 1726 int, tls_val, 1727 int __user *, child_tidptr) 1728#elif defined(CONFIG_CLONE_BACKWARDS2) 1729SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags, 1730 int __user *, parent_tidptr, 1731 int __user *, child_tidptr, 1732 int, tls_val) 1733#elif defined(CONFIG_CLONE_BACKWARDS3) 1734SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp, 1735 int, stack_size, 1736 int __user *, parent_tidptr, 1737 int __user *, child_tidptr, 1738 int, tls_val) 1739#else 1740SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, 1741 int __user *, parent_tidptr, 1742 int __user *, child_tidptr, 1743 int, tls_val) 1744#endif 1745{ 1746 return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); 1747} 1748#endif 1749 1750#ifndef ARCH_MIN_MMSTRUCT_ALIGN 1751#define ARCH_MIN_MMSTRUCT_ALIGN 0 1752#endif 1753 1754static void sighand_ctor(void *data) 1755{ 1756 struct sighand_struct *sighand = data; 1757 1758 spin_lock_init(&sighand->siglock); 1759 init_waitqueue_head(&sighand->signalfd_wqh); 1760} 1761 1762void __init proc_caches_init(void) 1763{ 1764 sighand_cachep = kmem_cache_create("sighand_cache", 1765 sizeof(struct sighand_struct), 0, 1766 SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU| 1767 SLAB_NOTRACK, sighand_ctor); 1768 signal_cachep = kmem_cache_create("signal_cache", 1769 sizeof(struct signal_struct), 0, 1770 SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL); 1771 files_cachep = kmem_cache_create("files_cache", 1772 sizeof(struct files_struct), 0, 1773 SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL); 1774 fs_cachep = kmem_cache_create("fs_cache", 1775 sizeof(struct fs_struct), 0, 1776 SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL); 1777 /* 1778 * FIXME! The "sizeof(struct mm_struct)" currently includes the 1779 * whole struct cpumask for the OFFSTACK case. We could change 1780 * this to *only* allocate as much of it as required by the 1781 * maximum number of CPU's we can ever have. The cpumask_allocation 1782 * is at the end of the structure, exactly for that reason. 1783 */ 1784 mm_cachep = kmem_cache_create("mm_struct", 1785 sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN, 1786 SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL); 1787 vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC); 1788 mmap_init(); 1789 nsproxy_cache_init(); 1790} 1791 1792/* 1793 * Check constraints on flags passed to the unshare system call. 1794 */ 1795static int check_unshare_flags(unsigned long unshare_flags) 1796{ 1797 if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND| 1798 CLONE_VM|CLONE_FILES|CLONE_SYSVSEM| 1799 CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET| 1800 CLONE_NEWUSER|CLONE_NEWPID)) 1801 return -EINVAL; 1802 /* 1803 * Not implemented, but pretend it works if there is nothing to 1804 * unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND 1805 * needs to unshare vm. 1806 */ 1807 if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) { 1808 /* FIXME: get_task_mm() increments ->mm_users */ 1809 if (atomic_read(¤t->mm->mm_users) > 1) 1810 return -EINVAL; 1811 } 1812 1813 return 0; 1814} 1815
ret_from_fork:
*childregs = *current_pt_regs(); //複製核心堆疊(複製的pt_regs,是SAVE_ALL中系統呼叫壓棧的那一部分。)
childregs->ax = 0; // 子程序的fork返回0
p->thread.sp = (unsigned long) childregs; // 排程到子程序時的核心棧頂
p->thread.ip = (unsigned long) ret_from_fork; //排程到子程序時的第一條指令地址
gdb跟蹤
驗證fork功能:
在建立程序的關鍵函式上設定斷點:
跟蹤除錯過程:
copy_thread函式為子程序準備了上下文堆疊資訊,其工作流程如下:
獲取子程序暫存器資訊的存放位置
對子程序的thread.sp賦值,即程序的esp暫存器的值。
如果建立的是核心執行緒,則執行位置是ret_from_kernel_thread,將這段程式碼的地址賦給thread.ip,之後準備其他暫存器資訊,退出
將父程序的暫存器資訊複製給子程序。
子程序的eax暫存器值置0。
子程序從ret_from_fork開始執行,所以它的地址賦給thread.ip,也就是將來的eip暫存器。
繼續除錯:
遇到的問題
PID相關知識。。
pid結構體:
struct pid {
struct hlist_head tasks; //指回 pid_link 的 node
int nr; //PID
struct hlist_node pid_chain; //pid hash 散列表結點
};
pid_vnr:
pid_t pid_vnr(struct pid*pid)
{
return pid_nr_ns(pid,current->nsproxy->pid_ns); //current->nsproxy->pid_ns是當前pid_namespace
}
獲得 pid 例項之後,再根據 pid 中的numbers 陣列中 uid 資訊,獲得區域性PID。
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
{
struct upid *upid;
pid_t nr = 0;
if (pid && ns->level <= pid->level) {
upid = &pid->numbers[ns->level];
if (upid->ns == ns)
nr = upid->nr;
}
return nr;
}