Linux环境编程3(持续更新中)

时间:2020-8-26 作者:admin


基本概念
1、中断
当程序接收消息后中止当前正在执行的程序,转而执行其它任务,等其它任务执行完成后再返回,这种执行模式叫中断,分为硬件中断和软件中断。
2、信号
是一种软件中断,由操作系统发出,程序接收后会执行相应的操作。
3、常见信号
kill -l 显示所有信号
SIGINT Ctrl+c 终止
SIGQUIT Ctrl+\ 终止+core
SIGFPE 除0 终止+core
SIGSEGV 非常内存访问 终止+core
SIGKILL 终止信号 终止

4、不可靠信号和可靠信号
建立在早期的信号处理机制上(1~31) 是不可靠信号。
不支持排除,可能会丢失,同一个信号如果连续产生多次,进程可能只接收到一次。
建立在新信号处理机制上 (34~64) 是可靠信号,支持排除,不会丢失。
5、信号来源
硬件异常:除0、无效内存访问、未定义指令、总线错误、软件异常。
软件异常:通过一些命令、函数产生的信号。
6、信号的处理方式
1、忽略
2、终止进程
3、终止进程并产生core文件
4、捕获并处理(当信号发前,向内核注册一个函数,当信号发生时系统自动执行该函数)。
信号捕获:
typedef void (*sighandler_t)(int);
功能:信号处理函数的格式

sighandler_t signal(int signum, sighandler_t handler);
功能:向信号注册一个信号处理函数
signum:信号编号
handler:函数指针
SIG_IGN 忽略
SIG_DFL 按默认处理
返回值:
之前的信号处理方式

注意:有些系统通过signal注册的函数只执行一次,如果想持续有效,可以在信号处理函数中再注册一次。
注意:子进程会继承父进程的信号处理方式,但如果是通过exec系列函数创建的子进程,会恢复默认的信号处理式。
注意:信号处理完后会返回到产生信号的代码处理,如果我们捕获并处理段错误或算术异常可能会产生死循环,正确处理段错误和算术异常应该是备份数据并结束进程。

信号的发送:
键盘:
Ctrl+c
Ctrl+
Ctrl+z
错误:
除0
非法内存访问
硬件故障 总线错误
命令:
kill 信号 进程号
killall 信号 进程名
函数:
int kill(pid_t pid, int sig);
功能:向指定的进程发送信号

int raise(int sig);
功能:向进程自己发送信号

void abort(void);
功能:向进程自己发送SIGABRT信号

unsigned int alarm(unsigned int seconds);
功能:让内核在seconds后向进程发关这SIGALRM信号
返回值:上次alarm设置的剩余时间
注意:如果再次调用会覆盖之前的设置,而不会产生两次闹钟信号。

进程休眠与信号:
int pause(void);
功能:让调用者进入休眠状态,直接进程遇到信号(捕获处理、进程结束)
返回值:要么一直休眠不返回,要么返回-1。
相当于没有时间限制的sleep

unsigned int sleep(unsigned int seconds);
功能:让调用者进入休眠指定的秒数,当遇到信号时会提前返回。
返回值:剩余的休眠时间

信号集与信号阻塞:
信号集:是一种数据类型,可以存储多个信号。
sigset_t 128二进制,每一次都代码一个信号。
相关函数:
int sigemptyset(sigset_t *set);
功能:清空信号集

int sigfillset(sigset_t *set);
功能:填满信号集

int sigaddset(sigset_t *set, int signum);
功能:向信号集中添加信号

int sigdelset(sigset_t *set, int signum);
功能:从信号集中删除信号

int sigismember(const sigset_t *set, int signum);
功能:测试信号集中是否有某个信号
功能:
0 不存在
1 存在
-1 信号非法
信号阻塞:
当程序在执行一些特殊操作时是不适合处理信号的,此时可以让内核先屏蔽信号,等操作执行完成后再发送信号。
当信号产生时,内核会在其所维护信号表中为进程设置一个与该信号对应的标记,这个过程叫递送。
从信号产生到完成递送有个时间间隔,处于这个间隔的信号状态叫未决。
信号屏蔽就是让信号先处于未决状态,暂停递送,当屏蔽解除时再继续递送。
每个进程都有一个信号集用于存储要屏蔽的信号。

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:设置要屏蔽的信号,这些信号存储在信号集里面。
how:
SIG_BLOCK 把set中的信号添加到要屏蔽的信号集里。
SIG_UNBLOCK 从信号集删除set的信号。
SIG_SETMASK 用set替换之前的信号集。
set:准备好的信号集
oldset:获取的信号的集

int sigpending(sigset_t *set);
功能:获取未决状态的信号

带附加的信号处理:
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
功能:向内核注册一个信号处理方式
signum:要捕获的信号
act:信号处理函数
oldact:获取旧的信号处理式

struct sigaction {
void (*sa_handler)(int); // 不带附加数据信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *); //带附加数据信号处理函数
sigset_t sa_mask; // 信号屏蔽信
在信号函数函数执行过程中,默认屏蔽当前信号,如果想屏蔽其它信号可以向sa_mask中添加。
int sa_flags; // 信号处理标志
SA_NOCLDSTOP 如果signum是SIGCHLD,则子进程停止时不要向我发送SIGCHLD。
SA_NOCLDWAIT 如果signum是SIGCHLD,则子进程结束时不要产生僵尸进程。
SA_NODEFER 在信号处理过程中不要屏蔽当前信号。
SA_RESETHAND 该信号处理方式执行完成后,还原成默认的处理方式。
SA_RESTART 系统调用一旦被信号打断,自动重启。
SA_SIGINFO 使用函数指针2,处理信号
void (*sa_restorer)(void); // 保留,NULL
};

siginfo_t {
int si_signo; /* Signal number /
int si_errno; /
An errno value /
int si_code; /
Signal code /
int si_trapno; /
Trap number that caused
hardware-generated signal
(unused on most architectures) /
pid_t si_pid; /
Sending process ID /
uid_t si_uid; /
Real user ID of sending process /
int si_status; /
Exit value or signal /
clock_t si_utime; /
User time consumed /
clock_t si_stime; /
System time consumed /
sigval_t si_value; /
Signal value /
int si_int; /
POSIX.1b signal */
void si_ptr; / POSIX.1b signal /
int si_overrun; /
Timer overrun count; POSIX.1b timers /
int si_timerid; /
Timer ID; POSIX.1b timers */
void si_addr; / Memory location which caused fault /
long si_band; /
Band event (was int in
glibc 2.3.2 and earlier) /
int si_fd; /
File descriptor /
short si_addr_lsb; /
Least significant bit of address
(since kernel 2.6.32) */

int sigqueue(pid_t pid, int sig, const union sigval value);
功能:向指定的进程发送信号并附加信号

union sigval {
int sival_int; // 整数
void *sival_ptr; // 指针
};

定时器:
int getitimer(int which, struct itimerval *curr_value);
功能:获取当前的定时方案
which:
ITIMER_REAL 真实计时器,程序总的运行时间 SIGALRM
ITIMER_VIRTUAL 虚拟计时器 用户态的运行时间 SIGALRM
ITIMER_PROF 实际计时器 用户态+内核态的运行时间 SIGPROF
真实计时器 = 实际计时器 + 休眠时间

int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
功能:设置新的定义方案

struct itimerval {
struct timeval it_interval; /* next value /
每次时钟信号产生和间隔时间
struct timeval it_value; /
current value */
第一次时钟信号产生和时间
};

struct timeval {
long tv_sec; /* seconds /
long tv_usec; /
microseconds */
};

进程基本概念:
1、进程与程序
程序就是存储在磁盘上的可执行文件,程序被加载到内存中开始运行叫进程。一个程序可以被多次加载生成多个进程,进程就是处于活动状态的计算机程序。
2、进程的分类
进程一般分为三种类型:交互进程、批处理进程、守护进程。
守护进程一般都处于活跃状态,运行在后台,由于系统在开机时通过启动脚本自动创建的。
3、查看进程
简单形式:ps 显示当前用户有控制终端的进程信息。
列表形式:ps auxw 显示进程详细信息
a 所有用户的有控制终端的进程
x 无终端控制的进程
u 显示进程的详细信息
w 以更大列宽显示
USER 进程的属主
PID 进程的编号
%CPU CPU使用率
%MEM 内核使用率
VSZ 虚拟内存使用的字节数
RSS 物理内存使用的字节数
TTY 终端设备号,?表示无终端控制
STAT 进程的状态
O 就绪,等待被调度
R 运行,Linux系统没有O,就绪也用R表示
S 可被唤醒的睡眠,如系统中断、获得资源、收到信号都可以唤醒它转入运行状态。
D 不可被唤醒的睡眠,只能被系统唤醒。
T 暂停状态,收到SIGSTOP信号进程暂停状态,收到SIGCONT信号转入运行状态。
W 等待内存分页(2.6内核以后被废弃)
X 死亡状态
Z 僵尸状态
< 高优级
N 低优先级
l 多线程的进程
s 会话首进程
L 有内存被锁进内存分页
+ 在前台进程级中
START 进程启动的时间
TIME 进程运行的时间
COMMAND 启动进程的命令
4、父进程、子进程、孤儿进程、僵尸进程
一个进程可以被另一个进程创建,创建者叫父进程,被创建者叫子进程,子进程被父进程启动后在操作系统的调用同时运行。
当子进程先于父进程结束,子进程会向父进程发送SIGCHLD信号,此时父进程应该去回收子进程的相关资源,如果没有收回子进程就会进入僵尸状态。
僵尸进程:该进程已经死亡,但它的父进程没有立即回收它的相关资源,该进程就进入僵尸状态。
孤儿进程:父进程先于进程结束,子进程就变成了孤儿进程,孤儿进程会被孤儿院(init守护进程)领养,init就是孤儿进程的父进程。

5、进程标识符
每个进程都有一个以非负整数表示的唯一标识,即进程ID/PID。
进程ID在任意时刻都是唯一的,但可以重用,进程一旦结束它的进程ID就会被回收,过一段时间后再重新分配给其它新创建的进程(延时重用)。
pid_t getpid(void);
功能:获取进程ID
pid_t getppid(void);
功能:获取父进程ID
init的进程ID永远是1。

创建进程:
int system(const char *command);
功能:执行一个可以执行文件,这样就创建一个子进程。
返回值:子进程结束后才返回。
该函数的实现调用了fork和waitpid函数,其实是当进程创建一个子进程,子进程又加载了command可执行文件。

pid_t fork(void);
返回值:一次调用两次返回,子进程返回0,父进程返回子进程ID,当进程的数量超过系统的限制会创建进程失败,返回-1。
可以根据返回值的不同让父子进程进入不同的分支,执行不同的代码。

该函数调用后父子进程各自独立运行,谁先返回不确定,但可以通过睡眠确定让哪个进程先执行。

通过fork创建的子进程会拷贝父进程的(数据段、bss段、堆、栈、I/O流缓冲区)等数据同,与父进程共享代码段,子进程会继承父进程的信号处理方式。

通过fork创建的子进程可以共享父进程的文件描述符。

pid_t vfork(void);
功能:以加载可以执行文件方式创建子进程。
返回值:子进程返回0,父进程返回子进程的PID。

子进程先返回,此时子进程并没有创建成功,需要加载一个可以执行文件来替换当前子进程的所有资源,替换完成子进程才算创建成功,此时父进程才返回。

使用exec系列函数加载可执行文件:
int execl(const char *path, const char *arg, …);
path:可执行文件的路径,
arg:命令行参数,一般每个是可执行文件的名字,至少一个,NULL结尾。

int execlp(const char *file, const char *arg, …);
file:可执行文件名字,会根据PATH环境变量的查找可执行文件;
arg:命令行参数

int execle(const char *path, const char *arg, …, char * const envp[]);
path:可执行文件的路径
arg:命令行参数
envp:环境变量表,父进程可以在加载子进程时把环境变量传递给子进程,这样父子进程共用一个环境变量表。

int execv(const char *path, char *const argv[]);
path:可执行文件的路径
argv:指针数组,由传递给子进程的字符串组成,以NULL结尾

int execvp(const char *file, char *const argv[]);
file:可执行文件名字,会根据PATH环境变量的查找可执行文件。
argv:指针数组

int execvpe(const char *file, char *const argv[],char *const envp[]);
file:可执行文件名字
argv:指针数组
envp:环境变量表

exec系列函数正常是不会返回会,当加载子可执行文件失败时返回-1。
以excl系列函数创建出的子进程不会继承父进程的信号处理函数,但能继承父进程的信号屏蔽。

进程的正常退出:
1、在main函数中执行 return n,该返回值可以被父进程接收到,它与exit几乎等价。
2、进程调用了exit函数,该函数是标准库函数:
void exit(int status);
功能:在任何时候调用此函数都可以结束进程。
status:结束状态码(EXIT_SUCCESS/EXIT_FAILURE),与main函数中return的返回值效果一样,
返回值:该函数不会返回

进程退出前要完成:
int atexit(void (*function)(void));
功能:注册一个进程结束时执行的函数
int on_exit(void (*function)(int , void *), void *arg);
功能:注册一个进程结束时执行的函数
int: return 的值或exit函数的参数
arg:会在进行结束时自动传递给function函数。
1、先调用事先通过at_exit/on_exit注册过的函数,如果都注册了执行顺序与注册顺序相反。
2、冲刷并关闭所有打开状态的标准IO流。
3、该函数的实现调用了_Exit/_exit。
3、调用_exit/_Exit函数
void _exit(int status);
功能:结束进程,由系统提供
void _Exit(int status);
功能:结束进程,由标准库提供
1、它们的参数会被父进程获取到。
2、进程结束前会关闭所有处理打开状态的文件描述符。
3、会向父进程发送SIGCHLD.
4、该函数不会返回。
4、进程的最后一个线程执行的返回语句。
5、进程的最后一个结束调用pthread_exit函数。

进程的异常终止:
1、进程调用了abort函数,产生了SIGABRT信号(自杀)。
2、进程接收到了某些信号,可以是其它进程发送的,也可能自己的错误操作造成的(他杀,作死)。
3、进程的最一个线程收到了”取消”操作,并作出了响应。
这三种结束方式父进程无法猎取结束状态码,所以叫异常终止。

注意:不管进程是如何结束的它们都会执行同一段代码,关闭所有打开的文件描述符,释放所有的内存。

子进程回收:
对于任何结束方式,都希望父进程能够知道,通过wait、waitpid函数可以知道子进程是如何结束的以及结束状态码。

pid_t wait(int *status);
功能:等待子进程结束,并获取结束状态。
返回值:子进程的ID
1、如果所有子进程都在运行,则阻塞。
2、如果有一个子进程结束,立即返回该进程结束状态和PID。
3、如果没有子进程则返回-1。

WIFEXITED(status) 判断进程是否是正常结束,如果是则返回真。
WEXITSTATUS(status) 如果进程是正常结束的,可以获取到正确的结束状态码,只获取低8位。
WIFSIGNALED(status) 判断进程是否是异常结束,如果是则返回值。
WTERMSIG(status) 如果进程是异常结束的,可以获取到杀死进程的信号。

注意:由于wait函数可能会阻塞,因此不适合在业务逻辑中调用些函数,可以为SIGCHLD信号注册一个处理函数,在处理函数中调用wait函数。

pid_t waitpid(pid_t pid, int *status, int options);
功能:指定收回某个或某些进程
pid:
<-1 等待abs(pid) 进程组中的进程结束。
-1 等待任意子进程结束,功能与wiat等价。
0 等待同组的任意进程结束。
>0 等待该进程结束。
status:结束状态,与wiat中的等价。
options:
WNOHANG 非阻塞模式,如果没有进程结束,立即返回0。
WUNTRACED 如果有进程处理暂停状态,返回该进程的状态。
WCONTINUED 如果有进程由暂停转为继续运行,返回该进程的状态。

WIFSTOPPED(status) 判断进程是否处理暂停状态,是则返回真。
WSTOPSIG(status) 获取到导致该进程暂停的信号。
WIFCONTINUED(status) 判断该进程是否由暂停转为继续运行,是则返回真。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。