进程结构
进程在内核的源代码中以结构体表示,篇幅很长,在此列举一小段关键代码,可以发现是个双向链表,具体的可以在内核目录下找一个叫“sched.h”的头文件。
struct task_struct {
struct task_struct *real_parent; /* real parent process */
struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
/*
* children/sibling forms the list of my natural children
*/
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */
……
};
进程被存放在叫做任务链表(tasklist)的双向循环链表中,linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓存着色(cache coloring)的目的。
结构体中主要由四部分组成
1.进程控制块:进程标志
2.进程程序块:可与其他程序共享
3.进程数据块:进程专属空间,用于存放各种私有数据以及堆栈空间
4.独立的空间:线程
进程状态
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
进程的状态一共有五种。
1.TASK_RUNNING(运行)--------进程正在执行,或者在队列中等待执行。这是进程在用户空间中唯一可能的状态,也可以应用到内核空间中正在执行的进程。
2.TASK_INTERRUPTIBLE(可中断)--------进程正在睡眠(也就是他被阻塞)等待某些条件达成。一旦这些条件达成,内核就会把进程状态设置为运行,处于此状态的进程也会因为收到信号而提前被唤醒并投入运行。
3.TASK_UNINTERRUPTIBALE(不可中断)--------除了会因为接收到信号而被唤醒从而投入运行外,这个状态与可打断状态相同。这个状态通常在进程必须等待时不受干扰或事件很快就会发生时出现。由于处于此状态的任务对信号不作响应,所以较之可中断状态,用的较少。
4.TASK_ZOMBIE(僵死)-------该进程已经结束了,但父进程还没有调用wait()系统调用。一旦父进程调用了wait(),进程描述符就会被释放。
5.TASK_STOPPED(停止)---------进程停止执行,进程没有投入运行也不能投入运行。
进程的创建
在linux系统中,所有进程都是PID为1的init进程的后代。内核在系统启动的最后阶段启动init进程。该进程读取系统的初始化脚本,并执行其他的相关程序,最终完成系统启动的整个进程。
进程是系统中基本的执行单位(线程是最小的调度单位),可以利用fork函数创建一个新的进程;
pid_t fork( void )
fork() 函数不需要参数,但返回两次,返回值有三种情况:
(1)对于父进程,fork函数返回新的子进程的ID。
(2)对于子进程,fork函数返回0。
(3)如果出错,fork函数返回-1。
fork函数创建一个新的进程,并从内核中为此进程得到一个新的可用进程ID,之后为这个新进程分配进程空间,并将父进程的进程空间中的内容复制到子进程的进程空间中,包括父进程的数据段+堆栈段,并与父进程共享代码段。
fork函数之后,子进程从等待fork返回开始执行,而不是从头开始。
注意:子进程完全复制了父进程的地址空间的内容,包括堆栈段+数据段的内容。但是,子进程并没有复制代码段,而是和父进程共享代码段。代码段是只读的,不存在修改的问题,因此可以共用。在创建一个子进程后,子进程的地址空间完全和父进程分开,父子进程是两个独立的进程。
linux环境下提供一个和fork函数类似的函数,可以用来创建一个共用父进程地址空间的子进程。
pid_t vfork();
vfork()与fork()的区别:
(1)vfork产生的子进程和父进程完全共享地址空间,包括代码段+数据段+堆栈段。子进程对共享资源进行的修改,也会影响到父进程。
(2)vfork函数产生的子进程一定比父进程先运行。即父进程调用了vfork函数后会等待子进程运行后再运行。
最后说说进程间通信:
linux下进程间通信的几种主要手段:
1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
2.信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期 信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上, 该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,sigaction函数重新实现了signal函数);
3.报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4.共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针其他通信机制运行效率较低设计的。往往与其它通信机制,如信号量结合使用, 来达到进程间的同步及互斥。
5.信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
6.套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix 系统上:Linux和System V的变种都支持套接字。
文章转载于:https://www.cnblogs.com/linquan/p/5001310.html