1.子进程创建
getpid 获取本进程号 getppid 获取父进程 当前的bash命令 getpgid获取进程组号进程号和进程组号一样 进程创建新进程,新进程也就是子进程,子进程创建子进程,形成树结构pid_t fork(void)用于一个已存在的进程创建一个新进程,新进程为子进程,原进程为父进程 创建成功返回子进程返回0,父进程返回子进程ID
子进程继承父进程的地址空间:包括进程上下文,进程堆栈,打开文件描述符,信号控制设定,进程优先级,进程组号等。子进程独有进程号,计时器。fork()写时拷贝,读时共享。
创建把原来的进程的所有值都复制到新的新进程中。
fork函数在子进程中返回0 在父进程中返回子进程的pid
2.结束进程
全局变量在数据段中
堆区分配内存空间
valgrind ./a.out
void exit(int status)结束调用此函数的进程
_exit()全部结束,没有后续清理工作 exit(0)等价于return 0 结束一个进程
wait 和waitpid功能相似 wait函数会阻塞,waitpid可以设置不阻塞,waitpid()可以指定等待某个子进程结束 调用一次只能清理一个子进程,清理多个子进程应循环使用
pid_t wait(status)等待任意一个子进程结束,如果任意子进程结束,此函数回收该子进程的资源。wait回收已经结束的子进程的资源
kill -18 继续 kill -19暂停 挂起 kill -9强制杀死进程
pid_t waitpid(pid_t pid ,int *status,int options);
ret=waitpid(-1,&status,0)= =ret=wait(&status)
不阻塞使用WNOHANG最后一个参数改
3.孤儿进程和僵尸进程
孤儿进程 :父进程运行结束,子进程仍继续运行,子进程 为孤儿进程内核将孤儿进程的父进程设置init
僵尸进程:子进程结束,父进程没有回收其资源,其实就是没用wait 回收资源的过程,
4.进程替换和进程通讯
进程替换:exec函数实现
进程数据全被替换之后,又会从主函数开始执行execlp 第一个参数是可执行文件,execl第一个参数是路径。
进程间通讯:
进程是独立的资源分配单元,不同进程之间资源是独立的,没有关联,不能在一个进程中直接访问一个进程的资源,不同进程需要信息交换与状态传递,进程通讯IPC 目的:数据传输,通知事件,资源共享(加锁),进程控制。
linux系统进程间通信:同一主机进程通讯:分为unix进程通信其中分为三种:无名管道,有名管道,信号。之后又分为两种system V进程间通信和POSIX进程间通信其中这两个分为三种:消息队列,共享内存,信号量。不同主机进程间通信:socket。
5. 无名管道pipe
无名管道:半双工:数据在同一时刻只能在一个方向流动 (全双工:数据即可读又可写 )
数据只能管道一端写入 另一端读出。先入先出,管道传输数据无格式,先约定数据格式,字节数决定,管道不是普通文件,存在内存中,数据一次性操作,读走就抛弃,无名管道只能在亲缘关系之间使用。fd[0]读,fd[1]写。
int pipe(int pipefd[2])有俩管道pipefd[0],pipefd[1]
父子进程使用无名管道通信:首先创建管道,在创建fork子进程 有血缘关系,有公共祖先,遗传文件描述符,
管道读写特性:四种情况1.如果写端没有关闭,管道中没有数据,这个时候读管道进程去读管道会阻塞。如果写端没有关闭,管道中有数据,这个时候读管道进程会将管道进程数据读出,下一次读没有数据去读管道会阻塞。2.管道所有写端关闭,读进程去读管道的内容,读取全部内容,最后返回0.3.所有读端没有关闭,如果管道被写满了,写管道进程写管道会被阻塞。4.所有读端被关闭,写管道进程写管道会收到一个信息,然后退出。
查看缓冲区大小ulimit -a设置非阻塞o_nonblock如果没有数据,直接返回-1.
6. 有名管道fifo
有名管道:FIFO管道,FIFO创建进程不存在亲缘关系进程,访问该路径,就能通过FIFO进行通信,FIFO不相关的进程也能交换数据FIFO中的内容放在内存中,mkfifo 管道名
int mkfifo(const char *pathname,mode_t mode);有名管道的创建
使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可用于fifo。如:close、read、write、unlink等。
FIFO遵循先进先出从开始处返回数据,数据添加至末尾。
一个只读而打开的一个管道进程会阻塞直到另外一个进程为只写打开该管道
一个为只写而打开一个管道的进程会阻塞直到另外一个进程为只读打开该管道
7.设置非阻塞方法
int flags = fcntl(fd[0],F_GETFL);
flag | = O_NONBLOCK;
fcntl(fd[0],F_SETFL,flags);
8.共享存储映射
共享存储映射:磁盘文件与存储空间的一个缓冲区相映射。(通讯最快的方式)
当从缓冲区中取数据,就相当于读文件中的相应字节。将数据存入缓冲区,则相应的字节就自动写入文件。就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。
存储映射函数mmap函数一个文件或者其他对象映射内存mmap函数总结 第一个参数NULL,第二个参数要映射文件大小,第三个参数 PROT_READ PROT_WRITE(读或写) 第四个参数 MAP_SHARED MAP_PRIVATE(共享或者匿名) 第五个参数 打开文件对应的描述符,第六个参数4K的整数倍,通常为0。
munmap函数释放内存映射区
注:创建映射区的过程中,隐含着一次对映射文件的读操作。munmap传入的地址一定是mmap的返回地址。坚决杜绝指针++操作。mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。
9.信号
信号是linux进程通信最古老的方式,软件中断,信号发出中断,信号也有编号posix.1信号的标准1-31常规信号,34-64实施信号,驱动编程与硬件相关,
信号四要素:编号,名称,事件,处理工作。SIGKILL信号和SIGSTOP信号,不允许忽略和捕捉,只能执行默认动作,甚至不能设置为阻塞。
阻塞信号集(黑名单)可以读写,未决信号集,通过位图机制来实现,信号产生没有相应未决态,未决可以读,不可以设置内核自动设置,
int kill(pid_t,int sig)给指定进程发送指定信号,
int raise(int sig);给当前进程发送指定信号(自己给自己发),等于kill(getpid(),sig)
void abort(void)给自己发送异常终止6(SIGABRT),产生core文件,等价于kill(getpid(),SIGABRT)
alarm函数(闹钟)unisiged int alarm(unsigned int seconds)设置定时器,内核会给当前进程发送14SIGALAM信号,每个进程有且只有唯一一个定时器。不会阻塞
int setitimer(int which,const struct *new_value,struct *old_value)设置定时器,定时器适用于异步处理,
10.信号集和信号阻塞集
信号集sigset_t set 集合,有添加删除的操作。
int sigemptyset(sigset_t *set); //将set集合置空
int sigfillset(sigset_t *set); //将所有信号加入set集合
int sigaddset(sigset_t *set, int signo); //将signo信号加入到set集合
int sigdelset(sigset_t *set, int signo); //从set集合中移除signo信
int sigismember(const sigset_t *set, int signo); //判断信号是否存在
信号阻塞集也称信号屏蔽集、信号掩码,每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集,信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞。所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);功能:检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存。
信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。
int sigpending(sigset_t *set);功能:读取当前进程的未决信号集
总结:
通过fork创建出来的子进程,getpid获取本进程号和getppid父进程号,子进程其实将父进程的东西复制一遍,堆和栈也复制过去,还有文件描述符,单独有的进程号和定时器,fork写时拷贝读时共享
exit函数用来结束进程,wait和waitpid用来释放进程资源,防止变成僵尸进程,wait是阻塞函数,waitpid可以设置非阻塞,kill -9按照信号的方式来杀死进程。
孤儿进程是父进程结束,子进程仍在继续执行,僵尸进程是父进程结束,没有回收子进程资源。
进程间不能直接资源交换,需要通讯,同一主机下:unix系统:通信方式:有名、无名管道,信号,还有三个共享内存映射,消息队列,信号量,不同主机:socket通信。
无名管道通信:半双工,还有亲缘关系,pipe创建无名管道,ulimit -a查看缓冲区大小
有名管道通讯:不需要亲缘关系,创建mkfifo有名管道,使用open,close ,write,read函数就可以操作
设置非阻塞方式,定义一个标志,用fcntl获取之后再设置标志位再设置回去
共享内存映射函数要知道每个参数设置,以及其根本原理
信号,四要素:编号,名称,事件,处理工作,SIGKILL和SIGSTOP信号不可以捕获和忽略,非阻塞,信号阻塞集是黑名单,未决信号集没有执行的,知道kill函数发送信号,raise函数自己给自己发,abort自己给自己发异常,alarm函数和setitimer函数定时
信号阻塞集不是删除信号,暂缓信号执行,阻塞一些信号。