#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
功能:用于创建当前正在调用进程的子进程,返回值以及可能出现的错误参考fork函数即可
vfork函数在创建子进程时,不会复制父进程中的内存空间信息
vfork函数不同于fork函数,会挂起父进程的执行,直到子进程终止或调用exec系列函数为止
vfork函数创建的子进程会共享所有内存空间和他父进程,包括栈区,该子进程在终止时不
+能从当前函数return或着调用exit()函数,可以直接调用_exit()函数
vfork函数保证了子进程先执行
exec系列函数
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char*arg, ...);
参 1:字符串形式的文件路径名
参 2:字符串形式的参数,一般给文件的执行方式
参 3:可变长参数,给NULL表示结尾
返回:只有出错时才有返回值,返回值为-1;
功能:主要用于执行参数指定的文件;
eg:execl("/bin/ls", "ls", "-l", NULL);
注意:vfork函数本身并没有太大的实际意义,一般和exec系列的函数搭配使用,主要用于
+子进程执行和父进程完全不同代码段的场合中;
fork函数也可以和exec系列函数搭配使用,只是fork函数可能会复制父进程中除了代码区
+之外的其它内存区域,因此执行效率比较低(写时拷贝技术)
#include <stdlib.h>
intsystem(const char *command);
返回:成功返回命令的状态信息,可以通过WEXITSTATUS进行解析, 失败 —— -1
功能:执行参数指定的shell命令
中断的概念和分类
中断就是停止当前正在执行的进程转而去执行新的进程或者处理意外情况的过程;
中断分为两种:硬件中断和软件中断
信号处理
信号本质就是一种软件中断,它既可以作为两个进程间的通信方式,也可以终止一个正常
+执行的程序,它更多地被用于处理意外情况;
a.信号是异步的,也就是进程并不知道信号何时到达
b.进程既可以发送信号,也可以处理信号
c.每个信号都有一个名字,并且用SIG开头
kill -l 表示查看当前系统所支持的所有信号
SIGINT 2 使用ctrl+c来产生信号 终止进程
SIGQUIT 3 使用ctrl+\来产生信号 终止进程
SIGKILL 9 使用kill -9来产生信号 终止进程
在当前教学系统,支持的信号范围是:1~64,
其中1~31之间的信号叫做不可靠信号,不支持排队信号可能会丢失,也叫做非实时信号;
其中34~64之间的信号叫做可靠信号,支持排队,信号不会丢失,也叫做实时信号;
信号处理的方式
1.默认处理,绝大多是信号的默认处理方式都是终止进程
2.忽略处理
3.自定义处理
#include <signal.h>
typedef void (*sighandler_t)(int); => typedef void(*)(int) sighandler_t;
sighandler_tsignal(int signum, sighandler_t handler);
=> signal是一个函数,有两个参数,一个是int类型,一个是函数指针类型返回值类型也是一个函数指针类型
函数功能解析:
参 1:信号值/信号名称(设置那个信号)
参 2:具体的处理方式(设置怎么处理)
a.SIG_IGN - 忽略处理
b.SIG_DFL - 默认处理
c.自定义函数的地址 - 自定义处理
返回:成功返回之前的处理方式,失败返回SIG_ERR
功能:主要用于设置指定信号处理的方式
父进程对信号处理方式的比较
1.对于fork函数创建的子进程来说,对信号处理方式完全照搬父进程,也就是父进程自定义子进程也自定义处理,父进程忽略处理,子进程也忽略处理,父进程默认处理,子进程也默认处理
2.对与vfork函数和execl函数启动了子进程来说,父进程忽略处理,子进程也忽略,父进程默认处理,子进程也默认处理;父进程自定义处理时,子进程采用默认处理;
发送信号的主要方式
1.采用键盘发送信号(只能发送部分特殊的信号)
SIGINT 2 => 使用ctrl+c发送信号
2.由程序出错发送信号(只能发送部分特殊的信号)
SIGSEGV 11 => 段错误发送信号
3.使用kill命令发送信号(发送所有信号)
kill -信号值/信号名称 进程号
4.采用系统函数发送信号(取决于函数的功能)
kill()/raise()/alarm()/sigqueue()
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参 1:进程的编号(给谁发信号)
>0 表示发送信号sig给进程号为pid的进程(重点)
=0 表示发送信号sig给和当前调用进程在同一个进程组的每一进程
=-1 表示发送信号sig给每一个当前前进程拥有发送信号权限的进程,除了进程inti(1)
<-1 表示发送信号sig给进程组ID 为-PID的每一个进程
参 2:信号值/信号名称(发送什么样的信号)
=0 表示不会发送信号,主要用于检查第一个参数进程/进程组是否存在
功能:主要用于给指定的进程发送指定的信号
int raise(int sig);
功能:主要用于给当前正在调用的进程或线程发送参数指定的信号,对与单线程的程序来说,等价于Kill(getpid(), sig)
返回:成功 —— 0, 失败 —— !0
unsigned int sleep(unsigned int seconds);
功能:用于让当前正在调用的线程睡眠参数指定的秒数,直到参数指定的秒数过去了或者一个不能忽略的信号到达时线程会被唤醒
返回:参数指定的秒数睡够了则返回0,否则返回没来的及睡眠的秒数/剩余秒数
unsigned int alarm(unsigned int seconds);
功能:主要用于经过参数指定的秒数后给当前正在调用的进程发送SIGALRM信号
每次使用alarm设置新的闹钟时,都会取消之前的闹钟,当参数为0时主要用于取消所有闹钟
返回:如果之前没有闹钟返回0,否则返回之前闹钟剩余的秒数
int sigqueue(pid_t pid, int sig, const union sigval value);
参 1:进程的编号
参 2:信号值/信号名称
参 3:联合类型的附加数据
功能:主要用于给参数指定的进程发送信号和附加数据
union sigval {
int sival_int;
void *sival_ptr; ==NULL
};
信号集的概念和操作
信号集本质就是由若干个信号组成的集合
信号集的数据类型:sigset_t类型
typedef struct
{
unsigned long int __val[ (1024 / (8 *sizeof (unsigned long int))) ];
}__sigset_t;
typedef __sigset_tsigset_t;
#include <signal.h>
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, intsignum); —— 判断是否存在
信号的屏蔽
在某些特殊程序的执行过程中,是不允许被信号打断的,这种情况下需要使用信号的屏蔽来实现
#include<signal.h>
int sigprocmask(int how, const sigset_t*set, sigset_t *oldset);
参 1:信号的屏蔽方式(怎么样屏蔽)
SIG_BLOCK - 当前屏蔽集合 + 参数 set 结合(ABC +CDE = ABCDE)
SIG_UNBLOCK - 当前屏蔽集合 - 参数 set 结合(ABC -CDE = AB)
SIG_SETMASK - 参数 set 集合 替换 当前屏蔽集合(ABC CDE => CDE)
参 2:信号集类型的指针,用于指定新的屏蔽集
参 3:信号集类型的指针,不为空时, 带出之前的信号屏蔽集
功能:主要用于检查和修改屏蔽的信号集
注意:信号屏蔽并不是删除信号,而是将信号屏蔽期间来过的信号,保存起来,等信号屏蔽解除之后还是会处理,
对于可靠信号来说,来多少保存多少,对于不可靠信号,只保存一份;
int sigpending(sigset_t *set);
功能:主要用于获取信号屏蔽期间产生但没有来的及处理的信号集,通过参数指针带出去
//sigprocmask函数的使用
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <signal.h>
8
9 void fa(int signum)
10 {
11 // printf("捕获到了信号%d\n", signum);
12 }
13
14 int main(void)
15 {
16 //1.打印当前进程的编号
17 printf("pid=%d\n", getpid());
18 //2.设置信号2,3,50进行自定义处理
19 if( SIG_ERR == signal(2, fa) )
20 {
21 perror("signal");
22 exit(1);
23 }
24 if( SIG_ERR == signal(3, fa) )
25 {
26 perror("signal");
27 exit(1);
28 }
29 if( SIG_ERR == signal(50, fa))
30 {
31 perror("signal");
32 exit(1);
33 }
34 //3.是的当前进程睡眠20秒等待信号到来
35
36 int res = sleep(20);
37 if( 0 == res )
38 {
39 printf("睡了一觉");
40 }
41 else
42 {
43 printf("没有信号的屏蔽,睡眠被打断,睡了%d秒\n", 20-res);
44 }
45
46 printf("---------------------------------\n");
47
48 printf("下面开始设置信号的屏蔽");
49
50 //定义两个信号集类型的变量
51 sigset_t set, old;
52
53 //清空两个信号集类型的变了
54 if( -1 == sigemptyset(&set) )
55 {
56 perror("sigemptyset");
57 exit(-1);
58 }
59
60 if( -1 == sigemptyset(&old))
61 {
62 perror("sigemptyset");
63 exit(-1);
64 }
65
66 //增加信号2,3, 50到信号集set中
67
68 if(-1 == sigaddset(&set, 2) )
69 {
70 perror("sigaddset");
71 exit(-1);
72 }
73 if(-1 == sigaddset(&set, 3) )
74 {
75 perror("sigaddset");
76 exit(-1);
77 }
78 if(-1 == sigaddset(&set, 50) )
79 {
80 perror("sigaddset");
81 exit(-1);
82 }
83
84 //设置信号的屏蔽
85 res = sigprocmask(SIG_SETMASK, &set, &old );
86 if(-1 == res )
87 {
88 perror("sigprocmask");
89 exit(-1);
90 }
91
92 printf("设置信号屏蔽成功,old = %d \n", old);
93
94 res = sleep(20);
95 if( 0 == res)
96 {
97 printf("信号屏蔽成功,休眠完20秒\n");
98 }
99 else
100 {
101 printf("产生了没有被屏蔽的信号\n");
102 }
103
104 //获取信号屏蔽期间来过的信号
105
106 sigset_t pend;
107
108 if( -1 == sigemptyset(&pend) )
109 {
110 perror("sigemptyset");
111 exit(-1);
112 }
113
114 if( -1 == sigpending(&pend) )
115 {
116 perror("sigpending");
117 exit(-1);
118 }
119
120 //判断信号2,3,50是否来过
121
122 if( sigismember(&pend,2) > 0 )
123 {
124 printf("信号2来过\n");
125 }
126
127 if( sigismember(&pend,3) > 0 )
128 {
129 printf("信号3来过\n");
130 }
131
132 if( sigismember(&pend,50) > 0 )
133 {
134 printf("信号50来过\n");
135 }
136
137
138 //恢复系统默认的信号屏蔽集
139
140 res = sigprocmask(SIG_SETMASK,&old, NULL);
141 if( -1 == res )
142 {
143 perror("sigprocmask");
144 exit(-1);
145 }
146
147 return 0;
148
149 }
int sigaction(int signum, const struct sigaction *act, struct sigaction*oldact);
参 1:信号值/信号名称
参 2:结构体指针,用于设置信号最新的处理方式
truct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void); //函数指针类型,是一个过时成员,不使用
};
成员1:函数指针类型,用于设置对信号处理的方式,取值可以是:SIG_IGN/SIG_DFL/自定义函数地址
成员2:功能与成员1相同,但取决于成员4取值为(SA_SIGINFO)
参1:
参2: siginfo_t {
……
pid_t si_pid; //发送方的进程号
sigval_t si_value; //伴随信号到来的附加数据
……
}
参3:不使用NULL
成员3:用于设置在信号处理函数执行期间需要屏蔽的信号,自动屏蔽触发信号处理函数的信号,
如果成员4值是SA_NODEFER,则不会自动屏蔽触发信号处理函数的信号
成员4:SA_SIGINFO表示选择成员2为信号的处理方式
SA_NODEFER表示解除对触发信号处理函数的信号的屏蔽(如成员3)
参 3:结构体指针,用于带出信号之前的处理方式
功能:主要用于检查和修改指定信号的处理方式
1 //使用sigaction函数设置对信号的处理方式
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <sys/types.h>
7#include <signal.h>
8
9void fa ( int signum )
10 {
11 printf("正在处理信号,请稍后...\n");
12 sleep(10);
13 printf("信号处理完毕\n");
14
15 }
16
17 int main(void)
18 {
19
20 //准备结构体变量,并初始化
21 struct sigaction action = {};
22 action.sa_handler = fa;
23
24 //在执行fa函数期间屏蔽函数3
25
26 if(-1 == sigemptyset( &action.sa_mask ) )
27 {
28 perror("sigemptyset");
29 exit(-1);
30 }
31
32 if(-1 == sigaddset(&action.sa_mask, 3) )
33 {
34 perror("sigaddset");
35 exit(-1);
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <sys/types.h>
5#include <signal.h>
6
7void fa ( int signum, siginfo_t * info, void * pv)
8 {
9 printf("进程%d发来了信号%d\n",info->si_pid, signum);
10 }
11
12 int main(void)
13 {
14 //打印当前进程的进程号
15 printf( "pid = %d\n",getpid() );
16
17 //准备结构体变量,并初始化
18 struct sigaction action = {};
19
20 //设置自定义处理
21 action.sa_sigaction = fa;
22 //选择第二个函数指针
23 action.sa_flags = SA_SIGINFO;
24
25 //使用sigaction设置对2进行自定义处理
26
27 int res = sigaction(2, &action, NULL );
28 if(-1 == res )
29 {
30 perror("sigaction");
31 exit(1);
32 }
33
34 while(1);
35
36 return 0;
37 }
计时器
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const structitimerval *new_value, struct itimerval *old_value);
参 1:计时器的类型
ITIMER_REAL - 真实计时器,统计进程消耗的真实时间,定时发送SIGALRM信号
ITIME_VIRTUAL - 虚拟计时器,统计进程在用户态下消耗的时间
ITIME_PROF - 实用计时器,统计进程在用户态和内核态消耗的总时间,定时发送SIGPROF信号
参 2:结构体指针,用于设置计时器的新值
struct itimerval {
struct timevalit_interval; /* 间隔时间 */
struct timevalit_value; /* 启动时间 */
};
struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微秒 1 sec = 10^6 user */
};
参 3:结构体指针,用于带出计时器的旧值
功能:主要用于获取/设置计数器的数值