1.进程控制块(PCB)
OS是根据PCB来对并发执行的进程进行控制和管理的。系统在创建一个进程的时候会开辟一段内存空间存放与此进程相关的PCB数据结构。
PCB是操作系统中最重要的记录型数据结构。PCB中记录了用于描述进程进展情况及控制进程运行所需的全部信息。
PCB是进程存在的唯一标志,在Linux中PCB存放在task_struct结构体中。 调度数据:进程的状态、标志、优先级、调度策略等。
时间数据:创建该进程的时间、在用户态的运行时间、在内核态的运行时间等。
文件系统数据:umask掩码、文件描述符表等。
内存数据、进程上下文、进程标识(进程号)
2.进程控制
僵尸进程(Zombie Process)
进程已运行结束,但进程的占用的资源未被回收,这样的进程称为僵尸进程。子进程已运行结束,父进程未调用wait或waitpid函数回收子进程的资源是子进程变为僵尸进程的原因。
孤儿进程(Orphan Process)
父进程运行结束,但子进程未运行结束的子进程。
守护进程(精灵进程)(Daemon process)
守护进程是个特殊的孤儿进程,这种进程脱离终端,在后台运行(&)。
3.exec函数
调用exec函数的进程并不创建新的进程,故调用exec前后,进程的进程号并不会改变,其执行的程序完全由新的程序替换,而新程序则从其main函数开始执行。
int execl(const char *pathname,const char *arg0,…,NULL);
int execlp(const char *filename,const char *arg0,…,NULL);
int execle(const char *pathname,const char *arg0,…,NULL,char *const envp[]);
int execv(const char *pathname,char *const argv[]);
int execvp(const char *filename,char *constargv[]);
int execve(const char *pathname,char *const argv[],char *const envp[]);
六个exec函数中只有execve是真正意义的系统调用(内核提供的接口),其它函数都是在此基础上经过封装的库函数。
#include
int main()
{
char *arg={"test",NULL};
char *env[]={"USER=MAN","SB=U"};
printf("111
");
execve("./test",arg,env);
return 0;
}
test.c
#include
#include
int main()
{
printf("USER===%s
",getenv("USER"));
printf("U===%s
",getenv("U"));
return 0;
}
4.进程间通信(IPC:Inter Processes Communication)
进程间通信功能:
数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。
5.可重入函数
可重入函数是指函数可以由多个任务并发使用,而不必担心数据错误。
编写可重入函数:不使用(返回)静态的数据、全局变量(除非用信号量互斥)。
不调用动态内存分配、释放的函数。
不调用任何不可重入的函数(如标准I/O函数)。
注:即使信号处理函数使用的都是可重入函数(常见的可重入函数),也要注意进入处理函数时,首先要保存errno的值,结束时,再恢复原值。因为,信号处理过程中,errno值随时可能被改变。
信号是软件中断,它是在软件层次上对中断机制的一
种模拟。
6.信号
信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。信号是一种异步通信方式。进程不必等待信号的到达,进程也不知道信号什么时候到达。
信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生了哪些系统事件。
7.管道
管道是最古老的UNIX IPC方式,其特点是:1、半双工,数据在同一时刻只能在一个方向上流动。
2、数据只能从管道的一端写入,从另一端读出。
3、写入管道中的数据遵循先入先出的规则。
4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。
5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。
6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。
8、管道没有名字,只能在具有公共祖先的进程之间使用。
命名管道(FIFO)
int mkfifo( const char *pathname, mode_t mode);
1、半双工,数据在同一时刻只能在一个方向上流动。
2、写入FIFO中的数据遵循先入先出的规则。
3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。
4、FIFO在文件系统中作为一个特殊的文件而存在,但FIFO中的内容却存放在内存中。
5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。
7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。
9.消息队列(message queue)
消息队列是消息的链表,存放在内存中,由内核维护
消息队列的特点
1、消息队列中的消息是有类型的。
2、消息队列中的消息是有格式的。
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
10.共享内存(shared memory)
共享内存允许两个或者多个进程共享给定的存储区域。
共享内存的特点
1、共享内存是进程间共享数据的一种最快的方法。一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内
容。
2、使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。