Linux 学习记录27(进程线程篇)
本文目录
一、信号
需要添加头文件:#include <signal.h>
1. 概念
所谓信号,就是软件模拟的中断的功能,信号是软件实现的,而中断是硬件实现的。
中断属于异步通讯的一种同步通信:进程在要执行前一直阻塞等待其他进程结束
异步通信:进程在要执行前不需要一直阻塞等待其他进程结束关于处理信号有三种情况:默认、忽略、捕获
2. 信号的种类及功能
可以使用kill -l查看所有信号
以下为常见的信号
信号名 | 含义 | 默认操作 |
---|---|---|
SIGHUP | 该信号在用户终端关闭时产生,通常是发给和该终端关联的会话内的所有进程 | 终止 |
SIGINT | 该信号在用户键入ITR字符(Ctrl-C)时产生,内核发送此信号送到当前终端的所有前台进程 | 终止 |
SIGQUIT | 该信号和SIGIT类似,但由QUIT字符(通常是Ctrl-)来产生 | 终止 |
SIGIL | 该信号在一个进程企图执行一条非法指令时产生 | 终止 |
SIGSEV | 该信号在非法访问内存时产生,如野指针、缓冲区溢出 | 终止 |
SIGPIPE | 当进程往一个没有读端的管道中写入时产生,代表“管道断裂” | 终止 |
SIGKILL | 该信号用来结束进程,并且不能被捕捉和忽略 | 终止 |
SIGSTOP | 该信号用于暂停进程,并且不能被捕捉和忽略 | 暂停进程 |
SIGTSTP | 该信号用于暂停进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号 | 暂停进程 |
SIGCONT | 该信号让进程进入运行态 | 继续运行 |
SIGALRM | 该信号用于通知进程定时器时间已到 | 终止 |
SIGUSR1/2 | 该信号保留给用户程序使用 | 终止 |
3. 信号API
信号处理函数:typedef void (*sighandler_t)(int);
类型为函数指针,参数为整形的返回值为void类型的的信号处理函数,该参数就是要处理信号
返回值:
==============================================================
函数原型:sighandler t signal(int signum, sighandler t handler);
参数1:要处理的信号号
参数2:要处理的方式
1. SIG_IGN:该信号被进程忽略
2. SIG_DFL:该信号被默认处理,一般为终止进程
3. 捕获信号,并执行信号处理函数
返回值:成功返回信号处理函数的首地址,失败返回SIG ERR,并置位错误码
示例:
/*信号处理函数*/
void handler(int signo)
{
printf("获取的信号为: Ctrl+C\r\n");
}
int main(int argc, char const *argv[])
{
/*忽略*/
signal(SIGINT,SIG_IGN);
/*默认*/
signal(SIGINT,SIG_DFL);
/*捕获*/
signal(SIGINT,handler);
while(1);
return 0;
}
4. 使用信号以非阻塞方式回收子进程资源
当子进程结束时,父进程会拿到哟个SIGCHLD的信号,我们可以将该信号进行捕获,在该信号对应的信号处理函数中手动回收子进程的资源
/*信号处理函数*/
void handler(int signo)
{
/*处理内容*/
waitpid(-1,NULL,WNOHANG);
printf("父进程以非阻塞状态回收子进程\r\n");
}
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("Error");
return -1;
}else if(pid == 0)
{//子进程
printf("子进程\r\n");
sleep(3);
printf("子进程结束\r\n");
exit(EXIT_SUCCESS);
}else
{//父进程
printf("父进程\r\n");
/*捕获*/
if(signal(SIGHUP,handler) == SIG_ERR)
{
perror("signal");
}
while(1);
exit(EXIT_SUCCESS);
}
// /*忽略*/
// signal(SIGINT,SIG_IGN);
// /*默认*/
// signal(SIGINT,SIG_DFL);
/*捕获*/
// signal(SIGINT,handler);
return 0;
}
5. 发信号相关的API(kill/raise)
(1. kill
函数原型:int kill(pid_t pid, int sig);
功能:给指定的进程发送信号
参数1:指定的进程
1. 大于0:给指定的进程发送信号
2. 等于0:给当前进程所在的组的所有进程发送信号
3. 等于-1:给所有进程发送信号,须注意权限
4. 小于-1:给pid的绝对值的进程所在的组下的所有进程发送信号
参数2:需要发送的信号
返回值:成功返回0,失败返回-1并置位错误码
(2. raise
函数原型:int raise(int sig);
功能:给自己发送信号
参数1:要发送的信号
返回值:成功返回0,失败返回非0错误码
6. 闹钟函数API
函数原型:unsigned int alarm(unsigned int seconds);
功能:在secnods秒后发送一个信号
参数1:毫秒数
返回值:该函数返回上次调用alarm函数余的毫秒数,如果之前没有调用过alarm函数或者,
上一次alar函数用钟已经处理完毕,则返回0
当第二次调用alarm时,如果更新了秒数,则SIGALRM信号发射,根据新更改的秒数确定
二、system VIPC进程间通信
系统5又提供了三种进程间通讯
消息队列
共享内存
信号灯集相关命令集
- ipcs:查看全部信息
- ipcs -q:只查看消息队列
- ipcs -m:只查看共享内存
- ipcs -s:只查看信号量
- ipcs -想要删除的信息 信息ID:删除指定的信息
三、消息队列
1. ftok 获取key值
函数原型:key_t ftok(const char *pathname, int proj_id);
功能:获取打开消息队列的键值
参数1:已存在的文件路径
参数2:给定任意的值
返回值:成功返回key值,失败返回-1
2. msgget 创建IPC对象
函数原型:int msgget(key_t key, int msgflg);
功能:创建消息队列
参数1:IPC PRIVATE 或 ftok
参数2:创建共享内存的标志位
IPC_CREAT|666:如果消息队列不存在就创建,如果存在就返回消息队列的ID
IPC_CREAT|IPC_EXCL|666:如果消息队列不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1
注意:
如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID
3. msgsnd 发消息
函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:发消息
参数1:消息队列ID
参数2:需要发送的消息存放的地址
参数3:消息正文的大小
参数4:发消息的标志位
0:阻塞的方式发送
IPC NOWAIT:非阻塞方式调用
返回值:成功返回@,失败返回-1 (队列的大小限制MSGMNB 16384)
==============================================================================
消息结构体定义:
typedef structf
{
long msg_type;//消息类型必须在第一个位置
char mtxt[1024];
......
}
正文大小:sizeof(msg_t) - sizeof(long)
4. msgrcv 接收消息
函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:接收消息
参数1:消息队列ID
参数2:存放接收到的消息
参数3:正文大小
参数4:消息类型
大于0:总是从消息队列中提取第一个消息
小于0:指向消息的类型接收消息,如果类型不匹配,详细依然在消息队列中存放
等于0:对消息的类型取绝对值
参数5:接收方式
0:阻塞的方式接收
IPC_NOWAIT:非阻塞方式
返回值:成功返回 接收消息正文大小,失败返回-1
5. msgctl 消息队列控制
函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:消息队列控制
参数1:消息队列ID
参数2:控制方式
IPC_RMID(删除消息队列)
IPC_SET(设置消息队列的属性信息)
IPC_STAT(获取消息队列属性信息)
参数3:存放消息队列属性(如果cmd填写为IPC RMID这个参数缺省)
返回值:成功返回0,失败返回-1
===============================================================
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
===============================================================
struct ipc_perm {
key_t __key; /* Key supplied to msgget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
使用示例:
四、共享内存
所需头文件:#include <sys/shm.h>
2. 共享内存API
(1. 创建共享内存
函数原型:int shmget(key_t key, size_t size, int shmflg);
功能:功能:获取共享内存段的ID
参数1:IPC_PRIVATE 或 ftok()
参数2:申请的共享内存段大小 [4k的倍数]
参数3:创建消息队列的标志位
IPC_CREAT|0666:如果共享内存不存在就创建,如果存在就返回共享内存的ID
IPC_CREAT|IPC_EXCL|0666:如果共享内存不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1
(2. 映射内存
函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射内存
参数1:共享内存段ID
参数2:映射到的地址
NULL:系统自动完成映射
参数3:共享内存操作的标志位
SHM_RDONLY:只读
0:读写
返回值:成功返回映射后的地址,失败返回(void *)-1
(3. 撤销映射
注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。
只有当共享内存映射次数为0,才会删除共享内存对象
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:根据命令控制共享内存
参数1:共享内存的ID
参数2:控制方式
IPC_STAT [获取属性],
IPC_SET [设置属性],
IPC_RMID [删除IPC对象]
参数3:存放消息队列属性 (如果cmd填写为IPC_RMID这个参数缺省)
返回值:成功返回0,失败返回 -1
思维导图
练习
1.
#define MSG_MAX_SIZE (sizeof(msg_t)-sizeof(long))
/*定义一个存放到队列中的结构体*/
typedef struct
{
long msg_type;//消息类型
char msg_txt[1024];//数据域
}msg_t;
int main(int argc, char const *argv[])
{
key_t key;//定义钥匙
int msg_id;//定义消息队列ID
msg_t msg={.msg_type = 100};//定义消息结构体
/*获取键值*/
if((key = ftok("./",'h')) == -1)
{
perror("ftok");
return -1;
}
/*创建消息队列*/
if((msg_id = msgget("./",'h')) == -1)
{
perror("msgget");
return -1;
}
/*向消息队列子存放数据*/
while (1)
{
/*提示输入消息正文*/
printf("请输入消息体\n");
fgets(msg.msg_txt, sizeof(msg.msg_txt), stdin);
if(strcmp(msg.msg_txt, "over") == 0) break;
msg.msg_txt[strlen(msg.msg_txt) - 1] = '\0';//消除回车
/*将消息放入消息队列(阻塞输入)*/
if(msgsnd(msg_id,&msg,MSG_MAX_SIZE,0) == -1)
{
perror("msgsnd");
return -1;
}
}
return 0;
}