day08

来源:互联网 发布:nba2k15mc捏脸数据 编辑:程序博客网 时间:2024/05/16 00:51
今天:
进程结束 
-fork() /退出进程/父进程等待子进程
vfork() 
Unix/Linux 信号(信号处理)


break退循环,return 退函数,exit()退进程


程序员退出进程的方式:
正常退出:
1.在主函数中执行了return语句(特殊方式)
2.exit()退进程(通用方式)
    3 _exit()或_EXIT()函数退出进程(立即退出)
    4最后一个线程结束


非正常退出
1.被信号干掉,比如:ctrl+c
2.最后一个线程被取消




exit(int)/_exit(int)/_EXIT(int)


_exit(int)/_EXIT(int)在底层是一样的,_EXIT()调用了_exit()


exit()函数会退出进程,但不一定是马上退出,允许通过atexit()函数注册一些其他的函数,在退出前会执行注册过的函数


_exit()函数立即退出,只做三件事:
1.关闭文件描述符
2.让所有子进程变成孤儿进程
3.发退出信号给父进程
如果没有特殊需求,退出进程exit()即可


wait()和waittpid()
wait()和waitpid()可以让父进程等待子进程的结束,并且取得子进程结束的方式(正常退出还是非正常退出)和退出码


wait()必须等待任意一个子进程结束,只要有子进程结束wait()返回,如果没有子进程结束父进程继续等待
waitpid()可以等待多种方式的子进程结束,也可以一个都不等,因此更灵活。
wait()和waitpid()能回收僵尸子进程的资源,戏称为殓尸工。


pid_t wait(int* status)
功能:等待任意一个子进程的结束,并取得退出状态和退出码:
参数:status是传出参数,返回子进程的退出状态和退出码
返回值:返回结束子进程的pid.
宏函数被用于判断子进程的退出状态和获取退出码:
WIFEXITED()判断是否正常退出
WEXITSTATUS()获取退出码(只有正常退出才有效)




pid_t waitpid(pid_t pid,int* status,int option)
功能:等待子进程的结束,但比wait()更灵活(可以不等)
参数:pid是等待哪个/那些子进程,包括:
-1 等待任意一个子进程
>0 等待特定的一个子进程(进程ID=pid)
0  等待和父进程一个进程组的子进程(本组)
<-1 等待指定进程组的子进程(进程组ID=|pid|)
status和wait()中的一样
option可以设置等待或不等待,默认0为等待,宏WNOHANG可以代表不等待。
返回:有子进程结束返回结束子进程的PID,如果不等待且没有子进程结束返回0,失败返回-1.


vfork() + excel()创建子进程
 
 vfork()函数从语法上和fork()没有任何区别,区别在于vfork()不会复制父进程的任何资源。
 子进程会占用父进程的资源继续运行,而父进程会阻塞,停止运行,父进程的阻塞有两种方法可以解除:
 1. 子进程运行结束,把资源还给父进程。
 2. 子进程调用了execl(),启动了一个全新的程序,也把原来的资源还给父进程。
 第二种方法更常用,第一种方法没有太大的实际意义。
 
 vfork()会创建一个新的子进程,可以确保子进程先运行。
 vfork()创建的子进程必须使用exit()退出,return 语句退出会有问题


 vfork()函数能创建子进程,但不能提供代码和数据;execl函数不能创建子进程,但可以提供进程运行的代码和数据


execl()函数不会创建新的进程,进程PID不会改变,用一个新的程序替换掉当前进程执行的程序。
int execl(char* path,char* cmd,……)
功能:启动一个全新的程序,当前程序将会被替换,但不会建立新进程。
参数:path 就是新程序的路径,包括文件名,不能出错
cmd 就是运行程序的命令,比如:“a.out”
 ……可以包括命令的参数/命令的选项,最后以NULL结束
 返回:成功则启动新程序,没有任何的返回值
 失败就无法启动新程序,返回-1.


使用vfork()+excel()函数的方式实现如下功能:
在子进程中打印execl()前后的pid,对比是否一样。
写两个程序,一个是刚才的程序结构,还有一个负责打印pid.


关于进程必须会写代码:
fork()
vfork()+excel()


Unix/Linux的信号处理


信号(singnal)是Unix/Linux系统最常见的一种软件中断的方式。中断就是程序中止当前正在执行的代码,转而执行其他代码的过程。中断分为软件中断和硬件中断,软件中断的主要方式就是信号。
信号本质就是一个非负的整数,不同的值可以代表不同的情况。信号都有一个宏名称,以SIG开头,比如:信号2 就是ctrl+c的名字就叫SIGINT,宏名称POSIX规范中,而值不保证一致。因此编程时,信号都是使用宏名称而不是值,否则可能出现不一致。信号在Unix和Linux中是不同的,Unix是1-48,Linux 1-64,但中间不保证连续,信号0有特殊用途,没有实际意义。




信号分为可靠信号和不可靠信号,不可靠信号在Linux系统中1-31,特点是不支持排队,因此多个相同信号同时到来时可能出现信号的丢失,可靠信号43-64 ,特点是,支持排队。因此不会丢失
命令 kill -l可以查看系统都有哪些信号,每种信号都有来源和处理方式。
信号是无法确定何时到来,在程序中无法确定信号的到来时间。


信号的处理方式:
1 默认处理,每个信号都有默认处理方式,如果不改变信号的处理方式,就采用默认处理,默认处理大多数就是退出进程。
2 忽略信号,不做任何处理,就像信号没有来过
3 自定义处理,程序员需要写一个信号处理函数,然后信号的处理方式改为信号处理函数。
注:有些信号无法自定义或忽略,比如:信号9。
信号可以在进程之间互发,但信号的发送有权限限制,当前的用户只能给自己的进程发信号,root用户可以给所有进程发信号。(安全)


如何设置信号的处理方式:
函数signal()函数sigaction()
sigaction()增强版的signal(),但在应用中,signal()足够了,因此sigaction()介绍一下


void(*func)(int)     signal(int signal,
void(*func)(int))
功能:设置某个信号的处理方式为默认/忽略/自定义函数
参数:第一个参数就是设置哪个信号的处理方式
第二个参数就是函数指针,可以是:
SIG_DFL/SIG_IGN /自定义的函数名
返回:成功返回之前的处理方式(一般用不上)
失败返回 SIG_ERR(重点)


信号处理步骤:
1.#include<signal.h>一个头文件
2.写一个信号处理函数,格式:
void func(int signo)//函数名可变,参数是信号值
3在主函数中调:用signal()改变信号的处理方式。


父子进程直接的信号处理方式
如果是fork()创建的子进程,子进程完全复制进程信号处理方式,父子进程的信号处理方式一样
如何是vfork()+execl创建的子进程,父进程默认,子进程默认;父进程忽略,子进程也忽略,父进程自定义处理函数,子进程改为默认处理。

















































0 0
原创粉丝点击