Linux下进程管理

来源:互联网 发布:java集合类详解 编辑:程序博客网 时间:2024/05/16 10:48

一、进程

             进程:处于执行期的程序。(资源分配的最小对象)

          线程:进程中活动的对象。(CUP调度的最小单位)

          Linux下,通常调用fork产生一个新的进程,fork实际调用clone()系统调用实现的,最终通过exit()或退出main函数结束。父进程,调用wait()获取已退出子进程的状态。

二、进程描述符及任务结构

           Linux内核把进程存放在任务队列(task_struct)类型的双向循环链表中,task_struct类型的成员包括:打开的文件、进程地址空间、挂起的信号等等。

三、进程状态

  1. TASK_RUNNING(运行):处于执行期,或在执行队列中等待
  2. TASK_INTERRUPTIBLE(可中断):阻塞,正在等待某一条件的到达
  3. TASK_UNINTERRUPTIBLE(不可中断):除接受信号也不会唤醒外基本与上述状态相同,例如accept()系统调用
  4. __TASK_TRACED(被其他进程跟踪)
  5. __TASK_STOPED(停止):停止运行,没有运行也不能运行

四、进程家族树

            Linux下所有进程都是PID为1的(initd)进程的子进程,RHEL7.0中为systemd进程

        每个进程的task_struct结构体中都有指向父进程的指针(parent),以及指向子进程链表的指针(children)。并且可通过宏   next_task(task)   和   prev_task(task)  可实现,获取前一个和后一个进程。

五、进程创建

       fork创建一个子进程,通过exec读入可执行文件,最后开始运行。知道调用exit()系统调用,获退出main函数。

       传统的fork()系统调用直接把所有的资源复制给新进程。这种实现过于简单并且效率低,有可能拷贝的数据不用于共享,前功尽弃。Linux下的fork()系统调用实现了写时复制,顾名思义写时复制是一种推迟甚至免除拷贝数据的机制。调用完fork()之后子进程共享父进程数据,当需要写入时,才复制一份给子进程。如果调用完fork()后,立马调用exec(),则将免除拷贝数据。

       vfork创建子进程后,父进程阻塞,等待子进程退出或者调用exec后,才开始运行。而fork后父进程与子进程执行先后不确定。vfork()系统调用一般用exec()装入其他程序进行运行。

fork的具体实现:

  1. 调用dup_task_struct()创建内核栈,thread_info、task_struct
  2. 检查当前进程是否达到最大子进程数
  3. 分开子进程,并初始化子进程
  4. 子进程设置为TASK_UNINTERRUPTIBLE状态
  5. 调用copy_flags()以更新task_struct中flags成员,是否为超级用户
  6. 调用alloc_pid()分配一个合法的pid
  7. 根据标志,copy_process()拷贝父进程打开的文件、信息、函数、空间等等
  8. 完成工作,返回子进程指针

六、线程

线程仅仅被视为一个与其他进程共享某些资源的进程,内核中,它就是普通的进程

线程的创建于普通的进程类似,只不过在调用clone()系统调用时,通过传递的参数表明需要共享的资源:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,0)

fork的实现      clone(SIGHAND,0)

vfork的实现    clone(CLONE_VFORK | CLONE_VM | SIGHAND ,0)

内核线程:

没有独立的地址空间,只在内核空间运行。可通过ps -ef查看内核线程

内核线程通过kthread_create()创建,如果不用wake_up_process()唤醒它,它不会主动运行,可调用thread_run()达到目的。

内核线程终结1、调用do_exit()系统调用;2、调用kthread_stop()系统调用

七、进程终结

进程终结,内核必须释放它的资源,并告知其父进程

如果父进程在子进程退出前终结,子进程将成为孤儿进程,寄存在1号进程低下,所谓的守护进程的实现就是,父进程调用fork成功运行子进程后,自己退出。

终结方式:

  • 调用exit()
  • 退出main函数(C语言main函数返回后会隐式调用exit())
  • 不能处理也不能忽略的信号或异常

终结所做的工作:

  1. 将task_struct结构中标志为PF_EXITING
  2. 调用del_timer_sync()系统调用删除内核定时器(确保没有在可执行队列也没有运行)
  3. BSD通过exit()调用acct_update_integrals()来输出记账
  4. 调用exit_mm()来释放空间
  5. 调用sem_exit()离开IPC队列
  6. 调用exit_files()和exit_fs()递减文件描述符和文件系统的引用计数,如果递减后为0,则释放
  7. 完成退出动作
  8. 调用exit_notify()找父进程,并发信号,并将自己状态改为EXIT_ZOMBIE(僵尸进程)
  9. do_exit()调用schedule()切换到新进程运行,do_exit()永不返回
  10. 完成终结,(此时还占内核栈、thread_info、task_struct结构)供父进程查看状态

删除进程描述符:

         调用do_exit()后,进程将不在运行,但系统保留进程描述符。父进程可通过调用wait()来获取子进程状态。(wait()通过调用唯一的wait4()实现)。(调用wait()的进程会挂起,直到子进程退出)

完成的具体工作:

  1. 调用_exit_signal()删除任务列表中该任务
  2. 释放剩余资源
  3. 如果此进程为进程组中最后一个进程,并且领头进程已经僵死,则调用release_task()通知僵死进程的父进程
  4. release_task()调用put_task_struct()释放进程内核栈、和thread_info所占的页、以及task_struct所占用的slab高速缓存

至此,进程描述符和全部资源将释放。




0 0