一
1.1 进程基础
1.1.1 程序和进程
-
程序:是静态的,没有任何执行的概念。
- 程序 = 代码 + 数据
-
进程:是程序的一次动态执行过程,进程是程序执行和资源管理的最小单元,进程是一个独立的可以调度的任务
-
进程 = 代码 + 数据 + 系统资源[从外部角度看]
-
进程 = task_struct + 4G(虚拟内存)[从内核角度看]
-
task_struct是PCB(进程管理块)的一种,是Linux系统中描述进程的一个结构体
-
内容
1 标识符:pid/tid 2 状态 3 优先级 4 程序计数器 5 内存指针 ......
-
-
-
1.1.2 进程的标志
-
pid:进程号,是唯一标识进程的符号,是task_struct中的一项成员
-
获取方法
getpid();
-
-
ppid:父进程号,是标识当前进程的父进程的符号
-
获取方法
getppid();
-
1.1.3 进程的类型
- 交互进程:给指令就有回应的进程。如ls、ps
- 批处理进程:是一个进程集合,维护着一个进程的队列,负责按照时间先后顺序启动队列中的进程。如shutdown -h time
- 守护进程:周期性执行某项任务或等待某个事件发生的进程,它的运行不依赖shell终端,它的生存周期较长,从开机开始运行直到关机结束
1.1.4 进程相关的shell操作指令
ps -aux#查看系统中的进程
ps -axj#详细查看系统中进程
top#实时查看linux系统进程
pstree#查看进程树
kill#关闭一个进程。如kill -9 进程号
nice#以指定优先级运行进程。如nice -20 ./test
renice#改变正在运行的进程的优先级。如renice 19 进程号
1.1.5 进程的状态
- 运行态:running(R)
- R 运行状态(running):并不意味着进程一定在运行中,也可以在运行队列中
- 等待态:正在等待被运行,sleep
- S 睡眠状态(sleeping):进程在等待事件完成(浅度睡眠,可以被唤醒)
- D 磁盘睡眠状态(disk sleep):不可中断睡眠(深度睡眠,不可以被唤醒,通常在磁盘写入时发生)
- 停止态:stopT/Z/X (kill -STOP pid)
- T 停止状态(stopped):可以通过发送stop信号给进程来停止进程,可以发送SIGCONT信号让进程继续运行。
- Z 僵尸状态(zombie):子进程退出,父进程还在运行,但是父进程没有读到子进程的退出状态值
- X 死亡状态(dead):该状态为返回状态,在任务列表中看不到
1.2 进程相关的函数
1.2.1 fork
-
pid_t fork(void)
-
函数功能:在当前进程下(当前进程是父进程),创建一个子进程
-
需要的头文件:
#include <sys/types.h>
#include <unistd.h>
-
函数名:fork
-
函数参数:void
-
函数返回值:
0:表示子进程
大于0的整数(子进程的pid):表示父进程
-1:出错
1.2.2 exec函数族
execl函数
-
int execl(const char *path, const char *arg, …)
-
函数功能:用path路径下的进程替换当前进程
-
需要的头文件:
#include <unistd.h>
-
函数名:execl
-
函数参数:
path:带路径的可执行文件名
arg:执行可执行文件需要的指令
…:以列表形式传参,以NULL结束
-
函数返回值:
成功:不会产生返回值
出错:-1
示例
execle函数
-
int execle(const char *path, const char *arg, …, char *const envp[])
-
函数功能:用path路径下的进程替换当前进程
-
需要的头文件:
#include <unistd.h>
-
函数名:execle
-
函数参数:
path:带路径的可执行文件名
arg:执行可执行文件需要的指令
…:以列表形式传参,以NULL结束
envp:预先定义的存放自定义的环境变量的数组,以NULL结束
-
函数返回值:
成功:不会产生返回值
出错:-1
execlp函数
-
int execlp(const char *file, const char *arg, …)
-
函数功能:使用环境变量下的可执行文件替换当前进程
-
需要的头文件:
#include <unistd.h>
-
函数名:execlp
-
函数参数:
file:环境变量下的可执行文件名,不用加路径
arg:执行可执行文件需要的指令
…:以列表形式传参,以NULL结束
-
函数返回值:
成功:不会产生返回值
出错:-1
execv函数
-
int execv(const char *path, char *const argv[])
-
函数功能:用path路径下的进程替换当前进程
-
需要的头文件:
#include <unistd.h>
-
函数名:execv
-
函数参数:
path:带路径的可执行文件名
argv:预先定义的执行可执行文件需要的指令的数组,以NULL结束
-
函数返回值:
成功:不会产生返回值
出错:-1
execve函数
-
int execve(const char *path, char *const argv[], char *const envp[])
-
函数功能:用path路径下的进程替换当前进程
-
需要的头文件:
#include <unistd.h>
-
函数名:execve
-
函数参数:
path:带路径的可执行文件名
argv:预先定义的执行可执行文件需要的指令的数组,以NULL结束
envp:预先定义的存放自定义的环境变量的数组,以NULL结束
-
函数返回值:
成功:不会产生返回值
出错:-1
execvp函数
-
int execvp(const char *file, char *const argv[])
-
函数功能:使用环境变量下的可执行文件替换当前进程
-
需要的头文件:
#include <unistd.h>
-
函数名:execvp
-
函数参数:
file:环境变量下的可执行文件名,不用加路径
argv:预先定义的执行可执行文件需要的指令的数组,以NULL结束
-
函数返回值:
成功:不会产生返回值
出错:-1
1.2.3 exit/_exit
exit函数
-
void exit(int status)
-
函数功能:退出当前进程并刷新IO缓存区
-
需要的头文件:
#include <stdlib.h>
-
函数名:exit
-
函数参数:
status:0表示正常结束;其他数值表示非正常结束
-
函数返回值:void
_exit函数
-
void _exit(int status)
-
函数功能:退出当前进程,不刷新IO缓存
-
需要的头文件:
#include <unistd.h>
-
函数名:_exit
-
函数参数:
status:0表示正常结束;其他数值表示非正常结束
-
函数返回值:void
1.2.4 wait/waitpid
wait函数
-
pid_t wait(int *status)
-
函数功能:等待任意子进程的退出状态(第一个退出的子进程)
-
需要的头文件:
#include <sys/types.h>
#include <sys/wait.h> -
函数名:wait
-
函数参数:
status:指向子进程退出状态值的指针
-
函数返回值:
成功:返回等到的子进程的PID
失败:返回-1
waitpid函数
-
pid_t waitpid(pid_t pid, int *status, int options)
-
函数功能:等待指定子进程的退出状态
-
需要的头文件:
#include <sys/types.h>
#include <sys/wait.h> -
函数名:waitpid
-
函数参数:
pid:子进程的PID;
status:指向子进程退出状态值的指针
options:模式选择
0:将waitpid函数变成阻塞函数,若本次调用没有等到结果,则阻塞当前进程;若等到结果,则返回子进程的PID。
WNOHANG:将waitpid函数变成非阻塞函数,若本次调用没有等到子进程则退出,waitpid返回0;若等到结果,waitpid返 回子进程的pid。 -
函数返回值:
>0:已经结束运行的子进程的进程号
0:使用选项WNOHANG且子进程没有退出
-1:出错
二
2.1 守护进程
2.1.1 守护进程的定义
- 周期性的执行某项任务或等待某个事件发生的进程,不依赖shell终端,生存周期较长,从开机开始运行,直到关机结束
- 通常用于服务器,TTY为一个问号"?"
2.1.2 创建一个守护进程的步骤
-
创建一个子进程,父进程退出(此时子进程是孤儿进程)
pid=fork(); if(pid>0) { exit(0); }
-
创建新的会话期(使子进程脱离shell终端),使子进程从孤儿进程变为守护进程 (会话期相当于一个终端 一个班级,进程组相当于进程的集合,相当于一个班里的几个组)
setsid();
-
改变子进程的工作目录为根目录
chdir("/");
-
取消文件掩码
umask(0);
-
关闭所有父进程中可能打开的文件描述符(使用gettablesize得到文件描述符的数量)
close(i);//从i = 3开始关闭,0,1,2是大部分文件可能的文件描述符,可能影响标准输入输出函数的使用
创建一个守护进程示例
2.2 线程基础
2.2.1 线程定义
- 线程是一种轻量级的进程
- 其与创建它的进程共享一块内存空间
- 线程和进程一样统一参与操作系统的调度
2.2.2 引入线程的原因
- 进程间通信系统开销大,需要跨越地址空间
- 同一进程创建的两个线程间通信系统开销小,不用跨越地址空间
2.2.3 创建线程
使用pthread_create函数
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
2.2.4 创建线程涉及的函数
pthread_create函数
-
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg)//在进程中使用
-
函数功能:创建一个线程
-
需要的头文件:
#include <pthread.h>
-
函数名:pthread_create
-
函数参数:
thread:指向线程id的指针
attr:指向线程属性的指针,通常给NULL
(*start_routine) (void *):指向线程执行函数的指针
arg:传给线程执行函数的入参
-
函数返回值:
0:成功
非0:失败
pthread_join函数
-
int pthread_join(pthread_t thread, void **value_ptr)//在进程中使用
-
函数功能:阻塞等待线程退出,回收线程退出状态值
-
需要的头文件:
-
函数名:pthread_join
-
函数参数:
thread:线程ID
value_ptr:指向线程退出状态值的指针
-
函数返回值:
0:成功
非0:失败
pthread_exit函数
-
int pthread_exit(void *value_ptr)//在线程中使用
-
函数功能:退出当前线程
-
需要的头文件:
-
函数名:pthread_exit
-
函数参数:
value_ptr:指向线程退出状态的指针
-
函数返回值:
0:成功
非0:失败
pthread_cancel函数
-
int pthread_cancel(pthread_t thead)//在进程中使用
-
函数功能:取消线程/关闭线程
-
需要的头文件:
-
函数名:pthread_cancel
-
函数参数:
thead:线程ID
-
函数返回值:
0:成功
非0:失败
示例
2.3 多线程引入的问题
2.3.1 线程间通信
2.3.1.1 线程间通信的定义
- 线程间数据交换
2.3.1.2 线程间通信的方法
- 使用全局变量(在当前文件中定义)
2.3.2 线程同步
2.3.2.1 同步的定义
- 是指两个对象/事物/任务按照约定好的顺序相互配合完成一件事情
2.3.3.2 线程同步的定义
- 两个线程按照约定好的顺序相互配合完成一件事情
2.3.3.3 线程同步的实现方法
-
1 使用无名信号量(二值信号量)
sem_t sema;//信号量 sem_t semb;//信号量
-
2 使用P操作和V操作函数控制无名信号量
- P操作
- sem_wait(&sema)
- 用法:
- 当sema > 0时,函数返回,sema–
- 当sema = 0时,阻塞当前线程
- 返回值:
- 成功返回0
- 出错返回-1,且置errno
- V操作
- sem_post(&semb)
- 用法:
- 给信号量为0的信号量加一,非阻塞
- 返回值:
- 成功返回0
- 出错返回-1,且置errno
- P操作
2.3.3.4 线程同步涉及到的函数
sem_init函数
-
int sem_init(sem_t *sem, int pshared, unsigned int value)
-
函数功能:初始化信号量
-
需要的头文件:
#include <semaphore.h>
-
函数名:sem_init
-
函数参数:
sem:信号量对象
pshared:决定信号量能否在进程间共享,一般为0
value:信号量初始化值
-
函数返回值:
0:初始化成功
-1:初始化失败
sem_wait函数
-
int sem_wait(sem_t *sem)
-
函数功能:获取信号量,若不成功则阻塞。当sem > 0时,函数返回,sem–;当sem=0时,阻塞当前线程
-
需要的头文件:
#include <semaphore.h>
-
函数名:sem_wait
-
函数参数:
sem:信号量对象
-
函数返回值:
0:表示成功
-1:表示出现错误,信号量的值保持不变
sem_post函数
-
int sem_post(sem_t *sem)
-
函数功能:释放信号量。给信号量为0的信号量+1,sem++
-
需要的头文件:
#include <semaphore.h>
-
函数名:sem_post
-
函数参数:
sem:信号量对象
-
函数返回值:
0:表示成功
-1:表示出现错误,信号量的值保持不变
//sem_wait和sem_post两个函数配合使用示例
while(1)
{
sem_wait(&sem_printf);//消耗一个资源,sem_printf--
printf("printf_msgbuf :***********\n");
printf("printf_msgbuf :%s\n",msg->buf);
sem_post(&sem_printf);//得到一个资源,sem_printf++
}
2.3.3 线程互斥
2.3.3.1 引入
- 两个线程可以使用同一个线程执行函数,若在该线程执行函数中定义了一个公共的buf,两个线程同时使用公共的buf,就会出现资源的竞争访问问题,会引起数据存放混乱。
2.3.3.2 解决竞态的方法
- 加互斥锁
2.3.3.3 线程互斥涉及到的函数
pthread_mutex_init函数
-
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr)
-
函数功能:初始化互斥锁
-
需要的头文件:#include <pthread.h>
-
函数名:pthread_mutex_init
-
函数参数:
mutex:指向互斥锁对象的指针
attr:互斥锁属性,一般给NULL
-
函数返回值:
0:初始化成功
非0:初始化失败
pthread_mutex_lock函数
-
int pthread_mutex_lock(pthread_mutex_t *mutex)
-
函数功能:给公共资源加锁
-
需要的头文件:#include <pthread.h>
-
函数名:pthread_mutex_lock
-
函数参数:
mutex:指向互斥锁对象的指针
-
函数返回值:
0:加锁成功
非0:加锁失败
pthread_mutex_unlock函数
-
int pthread_mutex_unlock(pthread_mutex_t *mutex)
-
函数功能:给公共资源解锁
-
需要的头文件:#include <pthread.h>
-
函数名:pthread_mutex_unlock
-
函数参数:
mutex:指向互斥锁对象的指针
-
函数返回值:
0:解锁成功
非0:解锁失败
示例
三
3.1 进程间通信(一台主机间通信)
- 通信领域(应用层)概念:
- 单工通信:传输方向单向
- 半双工通信:传输方向双向,但同一时刻只能一个方向
- 全双工通信:传输方向双向,可以双向同时传送
3.1.1 无名管道
3.1.1.1 无名管道的定义
- 无名管道是一种管道文件
- 用于有亲缘关系的进程间的通信
- 有固定的读端和写端,通常当单工使用(本质上是半双工,但通信时刻不好把握),需要一个管道文件
- 无法通过“ls -la”查看该文件
- 父进程创建无名管道,子进程继承无名管道,所以父子进程可操作同一个管道
3.1.1.2 无名管道的创建方法
- 使用pipe函数创建无名管道
注意:
- 只有在管道读端存在时,向管道写入数据才有意义
3.1.1.3 使用有名管道实现通信涉及到的函数
pipe函数
-
int pipe(int fd[2])
-
函数功能:创建无名管道并打开管道,返回fd[0]和fd[1]
-
需要的头文件:
#include <unistd.h>
-
函数名:pipe
-
函数参数:
fd:读写操纵数组。(fd[0]:操作管道固定的读端;fd[1]:操作管道固定的写端)
-
函数返回值:
0:创建无名管道成功
-1:创建无名管道失败
3.1.1.4 使用无名管道实现通信示例
#include<stdio.h>
#include <unistd.h>
#include <string.h>
#define BUF_SIZE 20
int main()
{
//pipe
int fd[2]={0};
if(0!=pipe(fd))
{
printf("create pipe error!----------------\r\n");
return -1;
}
printf("1:create pipe ok!---------------\r\n");
//fork
pid_t pid=fork();
if(-1==pid)
{
printf("fork error!--------------\r\n");
return -1;
}
char buf[BUF_SIZE]={0};
//pid==0
if(0==pid)
{
//child process
close(fd[0]);
printf("child process!-------------\r\n");
strcpy(buf,"child to parent");
write(fd[1],buf,BUF_SIZE);
}
else //pid>0
{
//parent process
printf("parent process!------------\r\n");
close(fd[1]);
read(fd[0],buf,BUF_SIZE);
printf("read data:%s\r\n",buf);
}
return 0;
}
3.1.2 有名管道
3.1.2.1 有名管道的定义
- 有名管道是一种管道文件
- 用于任意数量进程间的通信
- 没有固定的读端和写端,可以用于单工通信和双工通信,用于单工通信需要一个管道文件、两个进程;用于双工通信需要两个管道文件、两个进程
- 可以通过“ls -la”查看该文件;但文件数据大小为0,因为管道中的数据只存在于内存中,只有存放在硬盘上的文件可以看到具体的文件数据大小
3.1.2.2 有名管道的创建方法
- 使用mkfifo函数创建有名管道
注意:
- 只有在管道读端存在时,向管道写入数据才有意义
3.1.2.3 使用有名管道实现通信涉及到的函数
mkfifo函数
-
int mkfifo(const char *filename,mode_t mode)
-
函数功能:创建有名管道
-
需要的头文件:
#include <sys/types.h>
#include <sys/stat.h> -
函数名:mkfifo
-
函数参数:
filename:有名管道文件的名称,通常为字符串形式
mode:文件权限(参见文件IO中的文件权限介绍)
-
函数返回值:
0:创建有名管道成功
-1:创建有名管道失败
fgets函数
-
char *fgets(char *s, int size, FILE *stream)
-
函数功能:从文件流指针获取字符串
-
需要的头文件:
#include <stdio.h> -
函数名:fgets
-
函数参数:
s:存储字符的内存首地址
size:获取字符的的数量
stream:文件流指针
-
函数返回值:
s:获取成功返回存储字符的内存首地址
NULL:当发生错误或者没有字符可获取时返回NULL
3.1.2.4 使用有名管道实现通信示例
/*
有名管道实现单工通信,需要一个有名管道文件(A---->B)
A进程:
1 mkfifo--->创建一个有名管道文件fifo
2 open----->打开有名管道
3 write
4 close
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 30
int main()
{
//mkfifo
int ret = -1;
ret = mkfifo("fifo",0666);
if(0 > ret && errno != EEXIST)//创建文件失败并且系统中没有fifo文件
{
printf("create fifp error\r\n");
return -1;
}
printf("1 mkfifioo------k\r\n");
//open
int fd = open("fifo",O_WRONLY,0666);//只能使用系统调用函数open打开fifo文件,不能使用标准库函数fopen
if(-1 == fd)
{
return -1;
}
printf("2 openfifo ok!---\r\n ");
//write
char buf[SIZE] = {0};
strcpy(buf,"hellowoeld");
write(fd,buf,SIZE);
//close
close(fd);
return 0;
}
/*
B进程:
1 mkfifo--->找到管道文件fifo(若已知已经创建过管道文件,该步可省略)
2 open
3 read
4 close
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 30
int main()
{
//mkfifo
int ret = -1;
ret = mkfifo("fifo",0666);
if(0 > ret && errno != EEXIST)//创建文件失败并且系统中没有fifo文件
{
printf("create fifp error\r\n");
return -1;
}
printf("1 mkfifioo------k\r\n");
//open
int fd = open("fifo",O_RDONLY,0666);
if(-1 == fd)
{
printf("open fifp error\r\n");
return -1;
}
//read
char buf[SIZE] = {0};
read(fd,buf,SIZE);
printf("b read data:%s\r\n",buf);
//close
close(fd);
return 0;
}
/*
有名管道实现双工通信,需要两个有名管道文件(A<-->B)
A进程
1 mkfifo-->fifoa
2 mkfifo-->fifob
3 open---->fifoa-->write
4 open---->fifob-->read
5 while
{
//write
write-->fifoa
//read
read--->fifob
}
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 30
int main()
{
//1 mkfifo fifoa
int ret = -1;
ret = mkfifo("fifoa",0666);
if(0 > ret && errno != EEXIST)//创建文件失败并且系统中没有fifo文件
{
printf("create fifo a error\r\n");
return -1;
}
printf("1 mkfifio a----o--k\r\n");
//2 mkfifob
ret = mkfifo("fifob",0666);
if(0 > ret && errno != EEXIST)//创建文件失败并且系统中没有fifo文件
{
printf("create fifo b error\r\n");
return -1;
}
printf("2 mkfifio b----o--k\r\n");
//3 open fifoa
int fdw = open("fifoa",O_WRONLY,0666);
if(-1 == fdw)
{
return -1;
}
printf("3 open fifoaok-------\r\n");
//4 open fifob
int fdr = open("fifob",O_RDONLY,0666);
if(-1 == fdr)
{
return -1;
}
printf("4 open fifobok-------\r\n");
//5 6 write read
char buf[SIZE] = {0};
while(1)
{
//write
memset(buf,0,SIZE);
fgets(buf,SIZE,stdin);
write(fdw,buf,SIZE);
//read
memset(buf,0,SIZE);
read(fdr,buf,SIZE);
printf("b--->a:%s\r\n",buf);
}
//close
close(fdw);
close(fdr);
return 0;
}
/*
B进程
1 mkfifo-->fifoa(若已知已经创建过管道文件,该步可省略)
2 mkfifo-->fifob
3 open---->fifoa-->writeread
4 open---->fifob-->write
5 while
{
//read
read--->fifob
//write
write-->fifoa
}
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 30
int main()
{
//1 mkfifo fifoa
int ret = -1;
ret = mkfifo("fifoa",0666);
if(0 > ret && errno != EEXIST)//创建文件失败并且系统中没有fifo文件
{
printf("create fifo a error\r\n");
return -1;
}
printf("1 mkfifio a----o--k\r\n");
//2 mkfifob
ret = mkfifo("fifob",0666);
if(0 > ret && errno != EEXIST)//创建文件失败并且系统中没有fifo文件
{
printf("create fifo b error\r\n");
return -1;
}
printf("2 mkfifio b----o--k\r\n");
//3 open fifoa
int fdr = open("fifoa",O_RDONLY,0666);
if(-1 == fdr)
{
return -1;
}
printf("3 open fifoaok-------\r\n");
//4 open fifob
int fdw = open("fifob",O_WRONLY,0666);
if(-1 == fdw)
{
return -1;
}
printf("4 open fifobok-------\r\n");
//5 6 write read
char buf[SIZE] = {0};
while(1)
{
//read
memset(buf,0,SIZE);
read(fdr,buf,SIZE);
printf("a--->b:%s\r\n",buf);
//write
memset(buf,0,SIZE);
fgets(buf,SIZE,stdin);
write(fdw,buf,SIZE);
}
//close
close(fdw);
close(fdr);
return 0;
}
3.1.3 信号通信
-
异步通信:通信中接收方并不知道信号(数据)何时会到达,当前进程一直准备接收发送方发送的数据,同时也在做自己的事情,一旦信号(数据)到达,马上接收处理,所以是非阻塞通信模式,
-
同步通信:发送方发送数据,接收方同步接收数据,双方需要在很短的时间内完成数据交换,否则会造成一方阻塞,所以是阻塞通信模式,
-
中断:指在程序的执行过程中插入了另外一段程序的执行过程,该段程序执行结束后,CPU回到中断点继续执行原来的程序
3.1.3.1 信号通信的定义
- 信号通信是在软件层面上对中断机制的一种模拟,是一种异步通信方式,使用信号通信可以实现用户进程和内核进程之间的通信
3.1.3.2 信号来源
-
Linux内核提供了64个信号,使用“kill -l”shell命令可以查看Linux内核中提供的64个信号,每一个信号都对应一个默认处理动作
例如:
- 用户点击关闭终端按钮:SIGHUP-------------->终止终端运行的进程
- 用户键入ctrl+c:SIGINT--------------------------->终止当前进程
- 用户键入shell命令“kill -9 1000”:SIGKILL—>终止进程1000
- 用户对SIGKILL和SIGSTOP这两个信号不能忽略
部分常用信号:
信号名称 默认处理动作 SIGABORT 进程停止运行 SIGALRM 警告钟 SIGFPE 浮点运算例外 SIGHUP 系统挂断 SIGILL 非法指令 SIGINT 终端中断 SIGKILL 停止进程(此信号不能被忽略或捕获) SIGPIPE 向没有读者的管道 SIGSEGV 无效内存段访问 SIGQUIT 终端退出ctrl+| SIGTERM 正常终止 SIGUSR1 用户定义信号1 SIGUSR2 用户定义信号2 SIGCHLD 子进程已经停止或退出 SIGCONT 如果被停止则继续执行 SIGSTOP 停止执行 SIGTSTP 终端停止信号 SIGTOUT 后台进程请求进行写操作 SIGTTIN 后台进程请求进行读操作
3.1.3.3 使用信号实现通信涉及到的函数
kill函数
-
int kill(pid_t pid, int sig)
-
函数功能:给进程或进程组发送信号
-
需要的头文件:
#include <sys/types.h>
#include <signal.h>
-
函数名:kill
-
函数参数:
pid:
- 正数:发送信号给进程号为pid的进程
- 0:信号被发送到所有和当前进程在同一个进程组的进程
- -1:信号发给所有的进程表中的进程(除了进程号最大的进程外)
- <-1:信号发送给进程组号为-pid的每一个进程
sig:信号类型
-
函数返回值:
0:发送成功
-1:发送出错
raise函数
-
int raise(int sig)
-
函数功能:让进程向自身发送信号
-
需要的头文件:
#include <sys/types.h>
#include <signal.h>
-
函数名:raise
-
函数参数:
sig:信号类型
-
函数返回值:
0:发送成功
-1:发送出错
pause函数
-
int pause(void)
-
函数功能:挂起当前进程(避免该进程执行剩余代码段,导致该进程结束,对方进程无法与该进程通信),并等待一个信号的到来,可以捕获信号
-
需要的头文件:
#include <unistd.h>
-
函数名:pause
-
函数参数:
void
-
函数返回值:
-1:pause函数只有等到一个信号的到来才会返回,并且返回值始终为-1,若未等到信号的到来,阻塞
alarm函数
-
unsigned int alarm(unsigned int seconds)
-
函数功能:该函数是一个闹铃函数,它可以在进程中设置一个定时器,当定时时间到时,内核就向当前进程发送SIGALRM(终止当前进程)信号。(默认动作可修改)
-
需要的头文件:
#include <unistd.h> -
函数名:alarm
-
函数参数:
seconds:秒数
-
函数返回值:
0:若参数为0,则返回值为0;若该定时器前没有其他定时器,则返回值也为0
大于0的整数:若该定时器前还有别的定时器,返回值是上一个定时器被打断计时后剩余的秒数(若有两个定时器,在没有sleep函数打断的情况下,后一个定时器会把第一个定时器打断,定时时间由第二个定时器的秒数决定,此时被打断的定时器返回值为0,第二个定时器的返回值是第一个定时器被打断计时后剩余的秒数)
signal函数
-
sighandler_t signal(int signum, sighandler_t handler)
-
函数功能:修改信号对应的默认处理动作,signum会自动给handler传送信号编号
-
需要的头文件:
#include <signal.h>
-
函数名:signal
-
函数参数:
signum:信号代码,它可以取除了SIGKILL和SIGSTOP外的任何一种信号
handler:
typedef void (*sighandler_t)(int):入参int,返回值void
1. 信号对应的新的处理动作函数 2. SIG_IGN:忽略该信号,使该信号失效 3. SIG_DFL:恢复对信号的系统默认处理,使该信号保持系统默认的作用
-
函数返回值:
成功:返回以前的信号处理函数
出错:-1
atoi函数
-
int atoi(const char *nptr)
-
函数功能:将指向存储字符串首地址的指针指向的字符型数据转换为整型数据
-
需要的头文件:
#include <stdlib.h>
-
函数名:atoi
-
函数参数:
nptr:指向字符串首地址的指针
-
函数返回值:
>0的整数:当转换成功时,返回转换后的整数
0:当转换失败时,返回0
3.1.3.4 使用信号实现通信示例
-
在Linux系统中,进程对信号的处理方式:
-
捕获信号
-
采用默认处理动作(可更改)
-
忽略信号
-
/*
通过信号实现A进程和B进程的通信
条件:
1 信号:SIGUSR1 SIGUSR2
2 信号的发送方发送信号的方式:使用kill或者raise函数
3 信号的接收方捕获信号的方式:使用pause函数
A进程:发送信号
1 使用main函数传参,从而获取对方进程的进程号pid
2 使用kill(pid,SIGUSR1)发送SIGUSR1信号
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char *argv[])
{
if(2 != argc || NULL == argv[1])
{
return -1;
}
int pid = atoi(argv[1]);
if(0 >= pid)
{
return -1;
}
kill(pid,SIGUSR2);
return 0;
}
/*
B进程:接收信号并处理
1 输出进程号
2 使用signal(SIGUSR1,Func)修改信号的默认处理动作
3 使用pause挂起B进程,延长B进程的生存周期,从而可以捕获到一个信号
*/
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void Func(int signo)
{
if(signo == SIGUSR1)
{
printf("user1 signal-------\r\n");
}
else
{
printf("other-----------\r\n");
}
}
int main()
{
printf("pid = %d\r\n",getpid());
signal(SIGUSR1,Func);
signal(SIGUSR2,Func);
while(1)
{
pause();
}
return 0;
}
四
- 进程间通信
4.1 共享内存
ftok
shmget
shmat
shmdt
shmctl
4.2 消息队列
ftok
msgget
msgsnd
msgrcv
msgctl
4.3 信号灯
sem_open