4种主要事件导致进程创建:

  1. 系统的初始化;
  2. 执行了正在运行的进程所调用的进程创建系统调用;
  3. 用户请求创建一个进程;
  4. 一个批处理作业的初始化;

进程的终止:

  1. 正常退出;
  2. 出错退;
  3. 严重错误;
  4. 被其他进程杀死;

当编译器给定程序的编译工作之后,编译器执行一个系统调用,通知操作系统它的工作已经完成,在unix/linux系统中调用的是exit()。

进程的状态:运行态,就绪态,阻塞态;

多线程提供了一种解决方案,有关的进程可以用一个输入线程,一个处理线程和一个输出线程构造。输入线程把数据读入到输入到缓冲区中,处理线程从输入缓冲区中取出数据,处理数据,并把结果放到输出缓冲区中;输出线程把这些结果写到磁盘上。这种模型只有当系统调用只阻塞调用线程而不是阻塞整个进程时,才能正常工作。

进程通信(Inter Process Communication, IPC)

涉及共享内存、共享文件和共享任何资源的情况都会引起冲突,最终的结果取决于程序运行的精确时序,称为竞争机制。需要采取“互斥”操作,使多个进程不可能同时处于临界区中。一个好的解决方案,需要满足4个条件:

  1. 任何两个进程不能同时处于其临界区;
  2. 不应对CPU的速度和数量做任何假设;
  3. 临界区外运行的进程不得阻塞其他进程;
  4. 不得使进程无限期等待进入临界区;

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

进程中的ID,声明为pid_t类型:

  1. 进程ID:pid,利用getpid()函数可以获得;
  2. 父进程ID:ppid,利用getppid()函数可以获得;
  3. 有效用户ID:euid,可以由geteuid()函数获得;
  4. 有效组ID:egid,可以由getegid()函数得到;
  5. 实际用户ID:uid,可以由getuid()函数获得;

这六个ID保存在内核中的数据结构中。

进程函数:

pid_t fork(void);对于父进程返回紫禁城的进程ID,对于子进程返回0。子进程复制的是父进程的数据段和堆栈段,并和父进程共享代码段。fork()函数出错的原因:系统中已经有太多进程,调用fork()函数的用户进程太多。

pid_t vfork(void);与其说是创建进程不如说是创建了一个线程,子进程与父进程共享所有资源,子进程对共享资源的修改会影响到父进程,两者共用一个进程ID。子进程一定比父进程先运行完,子进程运行期间父进程相当于被阻塞。返回讯息与fork()函数一样。

void exit(int status);【stdlib.h】这个退出函数会深入内核注销进程的内核数据结构,并且释放进程的资源。

int exec(const char * pathname, const char * arg0, ……);Linux下使用该函数执行一个新函数,该函数在文件系统中搜索指定路径的文件;

int system(const char * cmdstring);【stdlib.h】参数cmdstring是需要执行的shell命令,该函数是一个库函数,其中封装了fork、exec、waitpid三个系统调用。

pid_t wait(int * statloc);调用wait()函数的进程会被阻塞,直到进程的任何一个子进程结束(在利用fork函数创建进程之后wait类似于vfork创建的子进程),该函数会返回最近结束的的子进程的进程ID。若是想要等待指定子进程结束,可以利用while(pid != wait(&status));

pid_t waitpid(pid_t pid, int * statloc, int options);等待指定的进程结束,第一个参数为指定的进程ID,第二个参数为和wait函数中相同,接收进程返回的执行结果,第三个参数是控制选项。

当父进程创建的子进程退出后,父进程没有回收子进程的的结束信息时,子进程就变成了一个僵尸进程。当父进程在子进程结束之前结束运行,这时该子进程就成为孤儿进程,Linux中由init进程负责领养所有的孤儿进程,作为系统的守护进程,init进程被设计为永远调用wait()函数,即init进程的子进程不会是僵尸进程,可以使用这种方法避免僵尸进程的产生。

time_t time(time * t);【time.h】Linux/Unix中由time函数提供时间,参数t为一个time_t类型的指针。

信号及信号处理

信号是一种进程通信的方式,又称为软中断。一个进程一旦受到信号就会打断原来的程序执行流程来处理信号,这种通信方式是异步的。使用 kill –l 命令可以查看当前系统所支持的信号列表即相应编号;

5种方式产生信号:

  1. 用户通过终端快捷键组合,Ctrl+c产生SIGINT,Ctrl+z产生SIGKILL;
  2. 硬件异常产生信号,有硬件检测通知内核,内核通过信号通知当前的进程;
  3. 一个进程调用kill(2)函数向另一个进程发送信号;
  4. 利用kill(1)命令发送某个信号给某个进程,kill(1)也是调用kill(2)实现的;
  5. 当内核检测到某种软件条件发生时,也可以通过信号通知进程;

处理信号

对于一个信号,Linux环境下的进程只有3种处理方式:

  1. 忽略此信号;
  2. 注册一个信号处理函数,并要求内核在接收到信号时切换到用户套调用该处理函数,这种方式称为捕捉到一个信号。用户程序需要对某些信号做一些自定义的处理;
  3. 执行默认动作,不同的信号有不同的系统默认动作,系统使用的默认动作只有两种:终结此信号或者忽略此信号;

在进程捕捉到信号之后,无论进程执行到何处都会跳到信号处理函数中执行,从信号处理函数返回后再恢复之前的代码位置继续执行。信号处理函数不判断此时进程执行到何处;

原子操作:不会被线程调度机制打断的操作。volatile是一个类型修饰符,volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

Linux允许用户自定义信号处理函数,还提供借口,共使用者产生一个信号。

设置信号处理函数

void (*signal (int signo, void (* func))) (int);【signal.h】 Linux允许用户提供自己的信号处理函数,使用signal()函数将处理函数加载,并且通知系统。该函数的第一个参数为需要加载的信号,第二个参数为信号处理函数的函数指针(函数名),该函数返回一个函数指针,指向上一次的信号处理程序。如果加载错误,返回SIG_ERR。常用语句“if(signal(SIGUSR1, handler) == SIG_ERR){}”

void handler(int signo);信号处理函数的原型;

int kill(pid_t pid, int signo);信号发送函数原型,发送成功返回0,否则返回-1;

发送信号需注意:

  1. 该进程有向另一个进程发送信号的权限;
  2. 系统进程不能接收信号;

int raise(int signo);向本进程发送信号的函数,相当于kill(getpid(), int signo)。该函数可以实现exit()函数功能使进程退出,所不同的是此用法不作任何善后处理。

设置Linux定时器

unsigned int alarm(unsigned int seconds);函数的参数表示设置的秒数,如果系统时间超过该时间后函数就会向调用它的进程发送一个SIGALRM信号,这个信号的默认动作是终止调用该函数的进程。当函数的参数是0的时候可以取消一个定时器。如果之前没有设置定时器,则设置定时器时成功则返回0,若之前设置过定时器且已经超时,则调用定时器函数时依然返回0,若之前设置了定时器尚未超时调用该定时器函数返回剩余的秒数;

int pause(void);运行、阻塞、就绪是进程的三个基本状态,就绪态的进程可以被调度,阻塞的进程由于不能运行所以不参与调度。有时候当一个进程的运行条件已经具备时仍需要进程阻塞(例如考虑到时间上的调度顺序,sleep()),这种由进程自愿进入阻塞的情况称为进程挂起,Linux下使用pause()函数挂起一个进程;pause()函数是调用该函数的进程进入到挂起状态,直到一个信号到来,并且执行一个信号处理函数从其返回后,pause函数才返回-1。

unsigned int sleep(unsigned int nsec);pause()函数使进程无时间限制的国企,若想使进程在一定时间后恢复运行则使用sleep函数。sleep()函数返回值分两种:

  1. 挂起的时间超过了指定的休眠时间,返回0;
  2. 挂起期间被信号唤醒,这时sleep函数返回挂起以来经过的时间。

信号集

进程所能够捕捉并处理的信号称为信号集,信号机的实现通常是一个位向量,其中的每一位对应产生一个Linux系统中的信号。其数据类型为sigset_t,信号集处理函数【signal.h】:

int sigemptyset(sigset_t *set);清空信号集,即所有的位置0;

int sigfillset(sigset_t *set);信号集全部填充,将所有的位置1;

int sigaddset(sigset_t *set, int signo);信号集添加某信号;

int sigdelset(sigset_t *set, int signo);信号集删除某信号;

int sigismember(sigset_t *set, int signo);检查某信号是否在信号集中,在返回1;

屏蔽信号

希望进程阻塞一些信号,及时接收到该信号,也不用做处理。阻塞一个信号称为信号屏蔽。每一个进程内部有一个信号屏蔽字,标记屏蔽信号。

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);第2个参数是一个信号集,该信号集中被设置的位表示需要被屏蔽的信号,第3个参数表示原来的屏蔽信号集,第1个参数指定三种关系:

  1. SIG_BLOCK将set集合中信号添加到屏蔽集;
  2. SIG_UNBLOCK将set集合中信号从屏蔽集中解除;
  3. SIG_SETMASK将屏蔽集重置为set;

如果第2个参数是NULL,表示忽略原来的信号屏蔽字。restrict为类型限定符,用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。

进程通信

IPC类型:

  1. 管道:半双(全双)匿名(FIFO)/命名管道
  2. System V/POSIX IPC:消息队列、信号量、共享存储
  3. 网络进程通信:Socket套接字(Streams)

管道在系统中相当于一个文件,来缓存所要传输的数据,某些方面与文件又不同,例如在数据读出后管道中就没有数据了。匿名半双工管道的特性:数据智能在一个方向上流动,只能在具有公共祖先的进程之间进行通信。

int pipe(int fd[2]);Linux下利用pipe()函数创建一个匿名半双工管道,参数int fd[2]是一个长度为2 的文件描述符数组,fd[0]是数据读出端,fd[1]则是数据写写入端,函数返回0代表管道创建成功。

与pipe()函数配套使用的函数close(fd[0])、close(fd[1])关闭管道两个端口,释放文件资源。

write(fd[1], char *str, size_t length);向管道中写入数据,数据长度可设置为BUSZ;

read(fd[0], char *str, size_t length);从管道中读出数据;

#define BUSZ PIPE_BUF;BUSZ为管道默认一次性读写的数据;

在使用管道进行数据传输的时候,要注意维护管道的顺序,当父进程创建了管道,只有子进程已经继承了管道之后,父进程才可以执行关闭管道的操作。

创建管道的标准库函数

FILE *popen( const char* command, const char *mode);【stdio.h】command是一个在shell中可运行的命令字符串指针,例如“cat in.txt”。参数mode是一个操作字符指针:r或者w,popen()函数返回值是一个读或者写文件指针。popen()函数先创建一个管道,调用/bin/bash –c来执行参数中的command命令字符串。然后函数返回一个标准的I/O文件指针。

int pclose( FILE *stream);该函数的参数stream是一个popen()函数打开的文件描述符,pclose函数即关闭管道。

Linux下进程通信知识点学习笔记(一)相关推荐

  1. Linux下进程通信的八种方法

    Linux下进程通信的八种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号量 ...

  2. linux通信管道破裂,Linux下进程通信之管道

    每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把 ...

  3. usb linux 内核,Linux下USB内核之学习笔记

    Linux下USB子系统软件结构为 USB 内核(USB驱动,USBD )处于系统的中心,对于它进行研究是能够进行USB驱动开发(包括客户驱动和主机驱动)的第一步.它为客户端驱动和主机控制器驱动提供了 ...

  4. Linux下进程通信---共享内存之:shm

    进程通信:进程与进程间的数据交换,称为进程通信.进程通讯的方式有:共享内存.信号量.管道.消息队列.socket等等. 共享内存:内核管理一片物理内存,允许不同的进程同时映射,多个进程可以映射同一块内 ...

  5. Linux 下 Influx 安装 初始化 学习笔记

    通过docker安装Influx 官网文档:https://docs.influxdata.com/influxdb/v2.1/install/?t=Docker 获取influxdb的Docker镜 ...

  6. linux下的进程创建,Linux下进程的创建

    这篇文章主要是讲解到Linux进程的控制,包括程序和进程.守护进程.守护进程的出错处理. 1.程序和进程 程序(program)是存放在磁盘文件中的可执行文件,程序的执行实例被称为进程(process ...

  7. linux下的加密解密学习

    linux下的加密解密学习 加密/解密:         加密协议:加密解密使用同一秘钥:3des,aes         公钥加密:公钥私钥对         数字签名,密钥交换          ...

  8. 《Linux高性能服务器编程》学习笔记

    <Linux高性能服务器编程>学习笔记 Linux高性能服务器编程 TCP/IP协议族 TCP/IP协议族体系结构以及主要协议 数据链路层 网络层 传输层 应用层 封装 分用 测试网络 A ...

  9. Linux之进程通信20160720

    好久没更新了,今天主要说一下Linux的进程通信,后续Linux方面的更新应该会变缓,因为最近在看Java和安卓方面的知识,后续会根据学习成果不断分享更新Java和安卓的方面的知识~ Linux进程通 ...

最新文章

  1. CentOS 5 下yum安装 Mono 2.4
  2. POJ-3264-Balanced Lineup-单点更新
  3. 代码示例:使用redis计数来控制单位时间内对某接口的访问量
  4. android listView的setOnScrollListener的使用
  5. div+css中常见的问题
  6. bzoj 4006 管道连接 —— 斯坦纳树+状压DP
  7. 自定义autograd function
  8. php报表开发韩顺平,韩顺平从Html基础到php实战开发视频教程非常全面的一套PHP开发教程...
  9. 力扣-1566 重复至少 K 次且长度为 M 的模式
  10. apktook 反编译错误
  11. 骨龄测试软件app_测测app下载-工具包-测测下载v1.0.11 安卓版-西西软件下载
  12. 如何完整的安装Cygwin
  13. CCF 201312-2 ISBN号码 C++语言实现
  14. 智能船舶概况(国内篇)
  15. java的resize函数_Java源码解析HashMap的resize函数
  16. 地理遥感专业属于计算机行业吗,遥感科学与技术专业怎么样?
  17. Labview 编写TCP/IP 客户端断线重连机制程序,亲测可用
  18. html pre 转义,html转义-HTML转义字的转换问题
  19. 青藤放飞“猎鹰”,主动防御又多一张牌
  20. android 仿 tv 菜单,Android TV 开发之仿泰捷视频最新 TV 版 Metro UI 效果

热门文章

  1. CUDA 8混合精度编程
  2. 摄像头Camera标定Calibration原理Theory
  3. 横竖屏切换时Activity的生命周期
  4. 客快物流大数据项目(十一):Docker应用部署
  5. Python数据挖掘2:pandas使用:Series一串数字和DataFrame数据框
  6. 绘制多边形_XDGE_RayMarchine 1- 利用Frag Shader绘制图形
  7. window 10系统更改默认下载的位置
  8. group by 分组后 返回的是一个同属性的集合
  9. 2017-2018-1 20155204 《信息安全系统设计基础》第十一周学习总结
  10. linux 下安装MySQL