1、每一个进程都有一个唯一的非负整型作为标识符
pid_t getpid();//获取进程id
pid_t getppid();//获取父进程id
pid_t getuid();//获取实际用户id
pid_t geteuid();//获取有效用户id
pid_t getgid();//获取实际组id
pid_t getegid();//获取有效组id
2、fork
pid_t fork();
//创建一个新的子进程,这个函数一次调用,两次返回,在原进程(即未来的父进程)中,返回的值为子进程id,在子进程中,返回值为0,子进程只能拥有一个父进程,
可以通过getppid获取到父进程id
理论上来说,子进程会复制一份父进程的数据空间、堆、栈;父进程和子进程并不共享这些存储空间部分,父进程和子进程共享正文段;但是,往往在程序中,执行fork之后,常常在子进程中调用exec,该子进程完全由新的程序替换,覆盖了原先子进程复制的这部分数据空间,堆、栈;使得之前的复制白费力气,因此在大部分的实现中,子进程并不立马复制拷贝一份,而是使用一种copy-on-write,当哪个进程要改变一小部分内容的时候,就将小部分内存拷贝一份,供其使用。
父进程和子进程每个相同的打开描述符共享一个文件表项,所以父进程和子进程共享同一个文件偏移量
fork失败的两个主要原因:1)系统中已经有太多的进程
2)该实际用户ID的进程总数超过了系统限制
fork有两种用法:
1)一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段,这在网络服务进程中是常见的-----父进程等待客户端的服务请求。当这种请求到达时,父进程调用
fork,使子进程处理此请求。父进程则继续等待下一个服务请求;
2)一个进程要执行一个不同的程序,在这种情况下,子进程从fork返回后,立即调用exec
3、vfork
vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行,当子进程调用这两个函数中的任意一个时,父进程会恢复运行。(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁)
产生一个新进程,与fork不同的是:
1)父子进程共享数据空间
2)子进程先运行,直到子进程调用exec或者退出后,父进程才会运行,如果子进程运行过程中,需要等待父进程接下来运行的某个状态或者数值,则会死锁
4、进程退出,释放所占用的资源,包括打开的文件描述符,申请的内存等,但是仍然保留了一定的信息,包括进程号、终止状态、运行时间等。直到父进程调用
wait/waitpid来获取才会释放;
如果一个进程,大量产生子进程,等待子进程结束之后,却不主动调用wait/waitpid去释放子进程的保留信息,就会造成不良后果,比如进程号被占用(系统所能
使用的进程号是有限的),积累到一定程度后,会造成进程由于进程号限制而创建失败。
1)当子进程先于父进程退出,释放一定的资源后,还会留下一些信息,此时的子进程称为僵尸进程,这些信息由父进程调用wait/waitpid后才会释放,子进程在退出
的时候,会发送SIGCHLD信号给父进程,父进程接到信号后,在信号处理函数中调用wait/waitpid处理这些僵尸进程,默认情况下,父进程是忽略这些信号的;
2)当父进程在子进程之前退出,即子进程失去了父亲,成为了孤儿进程,这时候,init进程就会收养这些孤儿进程,并且循环调用wait来释放已经退出的子进程。
5、wait/waitpid
pid_t wait(int*stat_loc);
pid_t waitpid(pid_t pid,int*stat_loc,int options);
无论一个进程是正常结束还是异常终止,内核都会发送SIGCHLD信号给其父进程。子进程的终止是一个异步事件,可能发生在父进程运行的任何时候,内核通过信号
异步通知父进程,父进程接到该信号后,可以选择忽略和处理,默认情况下是忽略该信号。父进程在信号处理函数中,可以调用以下两个函数之一来释放子进程退出后
还保留的信息。
wait阻塞当前进程,直到有信号来或者子进程结束,如果在调用时子进程已经结束,则wait会立即返回子进程的结束状态值,子进程的状态值由stat_loc返回。
参数pid可选值:
<-1:等待进程组识别码为pid绝对值的任何子进程
=-1:等待任何子进程,相当于wait();
=0:等待进程组识别码中与当前进程相同的任何子进程
>0:等待指定进程ID的子进程
6、虽然是唯一的,但是进程ID是可复用的,大多数UNIX系统实现延迟复用算法,这防止了将新进程误认为是使用同一ID的某个已终止的先前进程;
ID为0的进程通常是调度进程,常常被称为交换进程,该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程;
ID为1的进程通常是init进程,在自举过程结束时由内核调用,init通常读取与系统有关的初始化文件,并将系统引导到一个状态,init进程绝不会终止,它是一个普通
的用户进程,与交换进程不同,他不是内核中的系统进程,但是它以超级用户特权运行
7、进程正常终止方式有五种
1)在main函数内执行return语句
2)调用exit函数,其操作包括调用各终止处理程序,然后关闭所有标准I/O流,然后关闭进程
3)调用_exit或_Exit函数,其目的是为进程提供一种无需运行终止处理程序或信号处理程序而终止的方法,它不冲洗I/O流
4)进程的最后一个线程在其启动例程中执行return语句,但是该线程的返回值不用做进程的返回值,当最后一个线程从其启动例程返回时,该进程以终止状态0返回;
5)进程的最后一个线程调用pthread_exit函数,如同前面一样,在这种情况下,在这种情况中,进程终止状态总是0,这与传送给pthread_exit的参数无关
进程异常终止方式
1)调用abort,它产生SIGABRT信号,
2)当进程收到某些信号时。信号可由进程自身、其它进程或内核产生。例如,若进程引用地址空间之外的存储单元、或者除以0,内核就会为该进程产生相应的信号
3)最后一个线程对“取消”请求作出响应。默认情况下,“取消”以延迟方式发生:一个线程要求取消另一个线程,若干时间之后,目标线程终止
在任意一种情况下,终止进程的父进程都能用wait或waitpid函数取得其终止状态
对于父进程已经终止的所有进程,它们的父进程都改变为init进程。大致过程是:在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的
子进程,如果是,则该进程的父进程ID就更改为1;这种处理方法保证了每个进程有一个父进程。
内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到这些信息,这些信息至少包括进程ID、该进程的终止状态以及该
进程使用的CPU时间总量。
在UNIX术语中,一个已经终止,但是其父进程尚未对其进行善后处理的进程被称为僵尸进程。
一个由init进程收养的进程终止时不会变成僵尸进程,因为init被编写成无论何时,只要有一个子进程终止,init就会调用一个wait函数取得其终止状态,这样也就防止了在系统中塞满僵尸进程,当提及一个“init的子进程”时,这指的可能是init直接产生的进程,也可能是其父进程已经终止,由init收养的进程
pid_t getpid();//获取进程id
pid_t getppid();//获取父进程id
pid_t getuid();//获取实际用户id
pid_t geteuid();//获取有效用户id
pid_t getgid();//获取实际组id
pid_t getegid();//获取有效组id
2、fork
pid_t fork();
//创建一个新的子进程,这个函数一次调用,两次返回,在原进程(即未来的父进程)中,返回的值为子进程id,在子进程中,返回值为0,子进程只能拥有一个父进程,
可以通过getppid获取到父进程id
理论上来说,子进程会复制一份父进程的数据空间、堆、栈;父进程和子进程并不共享这些存储空间部分,父进程和子进程共享正文段;但是,往往在程序中,执行fork之后,常常在子进程中调用exec,该子进程完全由新的程序替换,覆盖了原先子进程复制的这部分数据空间,堆、栈;使得之前的复制白费力气,因此在大部分的实现中,子进程并不立马复制拷贝一份,而是使用一种copy-on-write,当哪个进程要改变一小部分内容的时候,就将小部分内存拷贝一份,供其使用。
父进程和子进程每个相同的打开描述符共享一个文件表项,所以父进程和子进程共享同一个文件偏移量
fork失败的两个主要原因:1)系统中已经有太多的进程
2)该实际用户ID的进程总数超过了系统限制
fork有两种用法:
1)一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段,这在网络服务进程中是常见的-----父进程等待客户端的服务请求。当这种请求到达时,父进程调用
fork,使子进程处理此请求。父进程则继续等待下一个服务请求;
2)一个进程要执行一个不同的程序,在这种情况下,子进程从fork返回后,立即调用exec
3、vfork
vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行,当子进程调用这两个函数中的任意一个时,父进程会恢复运行。(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁)
产生一个新进程,与fork不同的是:
1)父子进程共享数据空间
2)子进程先运行,直到子进程调用exec或者退出后,父进程才会运行,如果子进程运行过程中,需要等待父进程接下来运行的某个状态或者数值,则会死锁
4、进程退出,释放所占用的资源,包括打开的文件描述符,申请的内存等,但是仍然保留了一定的信息,包括进程号、终止状态、运行时间等。直到父进程调用
wait/waitpid来获取才会释放;
如果一个进程,大量产生子进程,等待子进程结束之后,却不主动调用wait/waitpid去释放子进程的保留信息,就会造成不良后果,比如进程号被占用(系统所能
使用的进程号是有限的),积累到一定程度后,会造成进程由于进程号限制而创建失败。
1)当子进程先于父进程退出,释放一定的资源后,还会留下一些信息,此时的子进程称为僵尸进程,这些信息由父进程调用wait/waitpid后才会释放,子进程在退出
的时候,会发送SIGCHLD信号给父进程,父进程接到信号后,在信号处理函数中调用wait/waitpid处理这些僵尸进程,默认情况下,父进程是忽略这些信号的;
2)当父进程在子进程之前退出,即子进程失去了父亲,成为了孤儿进程,这时候,init进程就会收养这些孤儿进程,并且循环调用wait来释放已经退出的子进程。
5、wait/waitpid
pid_t wait(int*stat_loc);
pid_t waitpid(pid_t pid,int*stat_loc,int options);
无论一个进程是正常结束还是异常终止,内核都会发送SIGCHLD信号给其父进程。子进程的终止是一个异步事件,可能发生在父进程运行的任何时候,内核通过信号
异步通知父进程,父进程接到该信号后,可以选择忽略和处理,默认情况下是忽略该信号。父进程在信号处理函数中,可以调用以下两个函数之一来释放子进程退出后
还保留的信息。
wait阻塞当前进程,直到有信号来或者子进程结束,如果在调用时子进程已经结束,则wait会立即返回子进程的结束状态值,子进程的状态值由stat_loc返回。
参数pid可选值:
<-1:等待进程组识别码为pid绝对值的任何子进程
=-1:等待任何子进程,相当于wait();
=0:等待进程组识别码中与当前进程相同的任何子进程
>0:等待指定进程ID的子进程
6、虽然是唯一的,但是进程ID是可复用的,大多数UNIX系统实现延迟复用算法,这防止了将新进程误认为是使用同一ID的某个已终止的先前进程;
ID为0的进程通常是调度进程,常常被称为交换进程,该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程;
ID为1的进程通常是init进程,在自举过程结束时由内核调用,init通常读取与系统有关的初始化文件,并将系统引导到一个状态,init进程绝不会终止,它是一个普通
的用户进程,与交换进程不同,他不是内核中的系统进程,但是它以超级用户特权运行
7、进程正常终止方式有五种
1)在main函数内执行return语句
2)调用exit函数,其操作包括调用各终止处理程序,然后关闭所有标准I/O流,然后关闭进程
3)调用_exit或_Exit函数,其目的是为进程提供一种无需运行终止处理程序或信号处理程序而终止的方法,它不冲洗I/O流
4)进程的最后一个线程在其启动例程中执行return语句,但是该线程的返回值不用做进程的返回值,当最后一个线程从其启动例程返回时,该进程以终止状态0返回;
5)进程的最后一个线程调用pthread_exit函数,如同前面一样,在这种情况下,在这种情况中,进程终止状态总是0,这与传送给pthread_exit的参数无关
进程异常终止方式
1)调用abort,它产生SIGABRT信号,
2)当进程收到某些信号时。信号可由进程自身、其它进程或内核产生。例如,若进程引用地址空间之外的存储单元、或者除以0,内核就会为该进程产生相应的信号
3)最后一个线程对“取消”请求作出响应。默认情况下,“取消”以延迟方式发生:一个线程要求取消另一个线程,若干时间之后,目标线程终止
在任意一种情况下,终止进程的父进程都能用wait或waitpid函数取得其终止状态
对于父进程已经终止的所有进程,它们的父进程都改变为init进程。大致过程是:在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的
子进程,如果是,则该进程的父进程ID就更改为1;这种处理方法保证了每个进程有一个父进程。
内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到这些信息,这些信息至少包括进程ID、该进程的终止状态以及该
进程使用的CPU时间总量。
在UNIX术语中,一个已经终止,但是其父进程尚未对其进行善后处理的进程被称为僵尸进程。
一个由init进程收养的进程终止时不会变成僵尸进程,因为init被编写成无论何时,只要有一个子进程终止,init就会调用一个wait函数取得其终止状态,这样也就防止了在系统中塞满僵尸进程,当提及一个“init的子进程”时,这指的可能是init直接产生的进程,也可能是其父进程已经终止,由init收养的进程