简介进程和线程是所有程序员都熟悉的概念。简单的说,进程就是一个正在执行的程序,线程就是进程中的一条执行路径。进程是操作系统中的一个基本抽象概念。本文介绍Linux中进程和线程的使用和原理,包括创建和死亡。进程的创建和执行Linux中进程的创建和执行分为fork和exec两个函数,如下代码所示:intmain(){pid_tpid;if((pid=fork()<0){printf("forkerror\n");}elseif(pid==0){//childif(execle("/home/work/bin/test1","test1",NULL)<0){printf("execerror\n");}}//parentif(waitpid(pid,NULL)<0){printf("waiterror\n");}}fork从当前进程创建一个子进程,该函数返回两次,对于父进程,返回子进程的进程号,对于子进程返回0。子进程是父进程的副本,有和父进程一样的数据空间,堆和栈的一份副本,共享代码段。由于子进程通常会调用exec加载其他程序执行,Linux采用了copy-on-write技术,即数据段、堆和栈的副本在fork之后不会被复制,但是对这些内存区域的访问权限变为只读,如果父子进程中的任何一个想要修改这些区域,则对应的内存页会被修改,生成一个新的副本。这是为了提高性能。fork后,父进程先执行或者子进程先执行。肯定的,所以如果需要父子进程同步,往往需要使用进程间通信。fork之后,子进程会继承父进程的很多东西,比如:打开文件的实际用户ID,组用户ID等进程组的当前工作目录信号屏蔽和排列...父子进程的区别在于进程ID不同,子进程不继承父进程的文件锁。子进程的unhandledsignalset为空...fork之后,子进程可以执行不同的代码段,或者使用exec函数执行其他程序。进程描述符进程在运行时,除了加载程序外,还会打开文件,占用一些资源,进入睡眠等其他状态。为了支持进程的运行,操作系统必须有一个数据结构来存储这些东西。在Linux中,一个名为task_struct的结构保存了进程运行时的所有信息,称为进程描述符:structtask_struct{unsignedlongstate;内部优先级;pid_tpid;...}进程描述符完整地描述了一个进程:打开文件、进程地址空间、挂起信号、进程信号等。系统将所有进程描述符放在一个双端循环链表中:进程描述符存放在内存的什么位置?在内核堆栈的末尾。众所周知,进程占用的内存中有一部分是栈,栈主要用于函数调用,但这里所说的栈一般指的是用户空间的栈。事实上,进程也有一个内核堆栈。当一个进程调用系统调用时,进程被困在内核中。这时,内核代表进程执行一个操作。这时候就用到了内核空间中的栈。进程状态进程描述符中的状态描述了进程的当前状态。有以下五种类型:TASK_RUNNING:进程可执行。此时,进程要么正在执行,要么在运行队列中等待调度。TASK_INTERRUPTIBLE:进程正在休眠(阻塞),等待条件满足。如果满足条件或者接收到信号,进程将被唤醒,进入可运行状态被其他进程跟踪,通常用于调试_TASK_STOPPED:进程停止运行,通常是在收到SIGINT、SIGTSTP信号时。fork和vfork使用copy-on-write后,fork的实际开销是复制父进程的页表,为子进程创建唯一的进程描述符。fork到底做了什么来创建一个进程?fork实际上调用了clone,这是一个系统调用。通过给clone传递参数,表示父子进程需要共享资源。clone内部会调用do_fork,do_fork的主要逻辑在copy_process中。大致有以下步骤:创建内核栈和task_struct。这时候他们的值和父进程是一样的。将task_struct中的一些变量,比如统计信息,设置为0。将子进程的状态设置为TASK_UNINTERRUPTIBLE,保证不会投入运行。根据传递给pid的参数分配clone、复制或共享打开的文件、文件系统信息、信号处理函数和进程的地址空间等。除了fork,Linux还有一个类似的函数vfork。它的作用与vfork相同,子进程运行在父进程的地址空间中。但是,父进程会阻塞,直到子进程退出或执行exec。需要注意的是,子进程不能向地址空间写入数据。如果子进程修改数据、进行函数调用或不调用exec,则结果未知。vfork在fork没有copy-on-write技术时有性能优势,现在意义不大。退出进程的运行当有退出时,有8种方式终止进程,其中5种是正常终止:returnfrommaincallexitcall_exitor_Exit最后一个线程从它的启动例程返回从最后一个线程调用pthread_exit异常终止的三种方式:调用abort、接收信号、最后一个线程响应取消请求。exit函数会执行标准的I/O库清理和关闭操作:对所有打开的流调用fclose函数,缓冲区中的所有数据都会被刷新,而_exit会直接陷入内核。看下面的代码:#include
