进程控制
Linux进程
1.进程是一个动态的实体,是程序一次执行的过程。进程和程序的区别在于进程是动态的,程序是静态的,进程是运行中的程序,程序是一些保存在硬盘上的可执行代码。
线程是比进程更小的能独立运行的基本单位,线程是在进程内部。一个线程可以创建和撤销另一个线程,同一个进程中可以有多个线程并行执行。
2.Linux操作系统中,每个进程都是通过唯一的进程id标识的。而这个id是一个非负数,每个进程除了id还有其他的标识信息,可以通过相应的函数获得。这些函数在unistd.h
头文件中。
pid_t getpid(id) 获得进程id
pdi_t getppid(id) 获得进程父进程的id
pid_t getuid() 获得进程的实际用户id
pid_t geteuid() 获得进程的有效用户id
pid_t getgid() 获得进程的实际组id
pid_t getegid() 获得进程的有效组id
实际用户id: 标识运行改进程的用户。
有效用户id: 标识以什么用户身份来运行进程。
实际组id: 它是实际用户所属的组id
有效组id有效组id是有效用户所属组的id
3.在Linux系统运行的时候系统有以下集中状态:
运行状态: 进程正在运行或在运行队列中等待运行。
可中断等待状态: 进程正在等待某个事件完成,等待过程中可以被信号或定时器唤醒。
不可中断等待状态:进程也在等待某个事件完成,在等待中不可以被信号或定时器唤醒,必须等待直到等待的事件发生。
僵死状态:进程已终止,但进程描述符依然存在,直到父进程调用wait()
函数后释放。
停止状态:进程因为收到SIGSTOP、SIGSTP、SIGTIN、SIGTOU信号后停止运行或者该进程正在被跟踪(调试程序时,进程处于被跟踪状态)
在Linux下可以运行ps
命令查看进程的当前状态。运行状态为R,可中断等待状态为S,不可中断等待状态为D,僵死状态为Z,停止状态为T。
运行时还会有一些后缀字符,其意义分别为<(高优先级进程),N(低优先级队列),L(内存锁页,即页不可以被换出内存),s(该进程为会话首进程),l(多线程进程),+(进程位于前台进程组)。
4.Linux进程控制包括创建进程、执行新程序、退出进程及改变进程优先级等。
在Linux系统中,用于对进程进行控制的主要系统调用以下所示:
fork: 用于创建一个新进程
exit: 用于终止进程
exec: 用于终止进程
wait: 将父进程挂起,等待子进程终止。
getpid: 获取当前进程的优先级
nice: 改变进程的优先级
5.创建进程有两种方式,一种是由操作系统创建,二是由父进程创建。有操作系统创建的进程。它们是平等的,不存在资源继承关系。而对于父进程创建的进程他们和父进程存在隶属关系。子进程又可以创建进程,这样形成了一个进程家族。子进程可以继承其父进程几乎所有的资源。在系统启动的时候会,操作系统会创建一些进程,它们承担着管理和分配系统资源的任务,这些进程通常被称为系统进程。
系统调用fork
是创建一个新进程的唯一方法,除了极少数的方式创建的进程,如init
进程,它是内核启动时以特殊方式创建的。进程调用fork
函数就是创建了一个子进程。
创建了一个子进程后,父进程和子进程争夺CPU,抢到CPU者执行,另外一个挂起等待。如果想要父进程等待子进程执行完毕在继续执行,可以在fork
调用后调用wait
或者waitpid
。一个刚刚被fork
的子进程回合它的父进程一样,继续执行当前程序。通常是子进程在被fork
之后可以通过调用exec
函数执行其他程序。
6.fork
函数调用的时候会返回两个值,实际上调用成功后,当前进程分裂为两个进程,一个是原来的父进程,另一个是刚刚创建的子进程。父子进程在调用fork
函数的地方分开,fork
函数有两个返回值,一个是函数的父进程调用fork
函数后的返回值,这个返回值是刚刚创建的子进程的ID,另一个是子进程中fork
函数的返回值,这个返回值是0。fork
函数返回两个值的前提是进程创建成功,,如果失败只会返回-1。
7.fork
创建的子进程会继承父进程的很多属性,主要包括用户ID、组ID、当前工作目录、根目录、打开的文件、创建的文件使用的屏蔽字、信号屏蔽子、上下文环境、共享存储段、资源限制等。
8.孤儿进程就是一个子进程的父进程先于子进程结束,子进程就成为了一个孤儿进程,它由init
进程收养,成为init进程的子进程。
9.vfork
也可以创建新进程,与fork
相比,他又自己独特的用处。
vfork
和fork
一样都是调用一次,返回两个。
使用fork
创建一个子进程时,子进程只是完全复制父进程的资源。这样得到的子进程独立于父进程,并具有良好的并发行。而使用vfork
创建一个子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork
创建的子进程共享父进程的地址空间,也就是子进程完全运行在父进程地址空间上,子进程对地址空间任何修改对父进程都是可见的。
最重要的是fork
创建的子进程,哪个进程先运行取决于系统的调度算法,但是vfork
一个进程时,保证子进程先运行,当他调用exec
或者exit
之后,父进程才可能被调度运行。如果在exiec
或者exit
之前要依赖父进程的某个行为,就会导致死锁。
同时fork
创建一个子进程所有父进程的资源都要复制,有时候子进程只是调用一个exec
所以这样会浪费大量的系统资源。
10.创建守护进程。
守护进程时指在后台运行的、没有控制终端与之相连的进程。他独立于控制终端,通常周期性地执行某种任务。Linux大多数服务器就是用守护进程方式实现的。类似windows的系统服务。
守护进程启动方式有很多种:可以在/etc/rc.d
中启动,也可以有crond
启动,还可以由用户终端执行。编写创建守护进程的程序时,要尽量避免产生不必要的交互。
1.让进程在后台执行,方法时fork产生一个子进程,然后使父进程退出。
2.调用setsid创建一个新对话期。控制终端,登陆会话和进程组通常时父进程继承下来的,守护进程要摆脱它们,不受它们影响。其方法是调用setsid使进程成为一个会话组长。
3.禁止进程重新打开终端。经过以上步骤,进程已经成为一个无终端的会话组长,但是它可以重新申请一个终端。为了避免这种情况,使调用fork的进程退出。
4.关闭不再需要的文件描述符。创建的子进程会从父进程继承打开的文件描述符。如果不关闭,将会浪费系统资源,早曾进程所在文件系统无法卸下以及引起无法预料的错误。
5.将当前目录更改为根目录。当守护进程当前工作目录在一个装配文件系统时,该文件系统不能被拆卸。一般需要将工作目录改为根目录。
6.将文件创建时使用的屏蔽字设置为0.进程从创建它的父进程哪里继承的文件创建屏蔽字可能会拒绝某些许可权。为了防止这一点,使用`umask(0)`将屏蔽字清零。
7.处理SIGCHLD信号。这一步不是必须的,但是对于某些进程,特别是服务器进程往往在请求到来的时侯产生子进程处理请求。如果父进程不等待子进程结束,子进程将会变成僵尸进程,从而占用系统资源。如果父进程等待子进程结束,将会增加父进程的负担,影响服务器的并发性能。在Linux下将SIGCHLD信号操作设置为SIG_IGN。这样,子进程结束时就不会产生僵尸进程。
11.exec
函数通过路径方式调用可执行文件为新的进程映象,他的argv参数用来提供给main函数的argv参数。argv参数时NULL结尾的一个字符串数组。子进程在调用exec
函数的时候已经结束。
12.改变进程的优先级是通过系统调用nice
函数改变。
getpriority 该函数返回一组进程的优先级。
setpriority 该函数用来设置制定进程的优先级。
通过上面两个函数可以改变进程的优先级。nice
系统调用是它们的一种组合形式。
以上为LinuxC编程实战第七章学习总结,每个项目的代码都已经上传到github上。