问题解答:
1.exit(状态码)返回状态码有什么意义?
返回值被系统得到.系统根据状态码进行日志记录.
返回值被调用者得到:system/wait.程序会根据返回状态码进行对应处理。
exit(状态码)=main函数中的return 状态码;
2.状态码的第二个字节才是exit()的返回值或者return值。
一.进程的基本控制
1.进程的常见控制函数
1.1.为什么需要控制进程?
1.2.pause/sleep/usleep
1.3.atexit on_exit
#include <stdio.h>
#include <stdlib.h>
void fun()
{
printf("over\n");
}
main()
{
atexit(fun); // 注册终止函数(即main执行结束后调用的函数)
printf("Process!\n");
}
在多进程下文件读写是共享的
问题:
怎么知道一个文件正在被另外进程读写?
解决方案:
文件锁。(建议锁)
API:
fcntl(文件锁受内核参数影响)
编程技巧:
对文件加锁
判定一个文件是否存在锁
函数说明:
int fcntl(
int fd,//被加锁的文件描述符号
int cmd,//锁的操作方式:F_SETLK(已经加锁,异常返回) F_UNLK F_SETLKW(已经加锁,则阻塞等待)
struct flock *lk);//锁的描述
返回值:
0:加锁成功
-1:加锁失败
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
main()
{
int fd;
struct flock lk;
int r;
// 打开一个文件
fd=open("a.txt",O_RDWR);
if(fd==-1) printf(":%m\n"),exit(-1);
// 描述锁
lk.l_type=F_WRLCK;
lk.l_whence=SEEK_SET;
lk.l_start=5;
lk.l_len=10;
// 加锁
r=fcntl(fd,F_SETLK,&lk);
if(r==0) printf("加锁成功!\n");
else printf("加锁失败!\n");
while(1);
}
写两个程序:
A:加锁
B:获取锁的信息
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
main()
{
int fd;
struct flock lk;
int r;
// 打开一个文件
fd=open("a.txt",O_RDWR);
if(fd==-1) printf(":%m\n"),exit(-1);
// 描述锁
lk.l_type=F_WRLCK;
lk.l_whence=SEEK_SET;
lk.l_start=5;
lk.l_len=3;
// 加锁
r=fcntl(fd,F_SETLK,&lk);
if(r==0) printf("加锁成功!\n");
else printf("加锁失败!\n");
while(1);
}
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
main()
{
int fd;
struct flock lk={0};
int r;
fd=open("a.txt",O_RDWR);
if(fd==-1) printf("::%m\n"),exit(-1);
r=fcntl(fd,F_GETLK,&lk);
if(r==0)
printf("得到锁成功!\n");
if(lk.l_type==F_WRLCK)
{
printf("写锁!\n");
}
printf("start:%d,len:%d\n",lk.l_start,lk.l_len);
printf("PID:%d\n",lk.l_pid);
}
二.信号
1.信号的作用
背景:
进程之间通信比较麻烦。
但进程之间有必须通信,比如父子进程之间。
作用:
通知其他进程响应。进程之间通信机制.
信号:
接受信号的进程马上停止.调用信号处理函数.
信号处理函数:
默认处理函数.
打印信号信息,退出进程.
用户处理函数.
中断:
软中断.
案例:
1.进程之中,默认信号处理
2.进程之中,用户信号处理
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void handle( int s)
{
printf("我是信号发生!\n");
}
main()
{
// signal(SIGWINCH,handle);
signal(35,handle);
while(1)
{
// printf("进程在执行:%d!\n",getpid());
// sleep(1);
}
}
kill -s 信号 进程ID
kill -信号 进程ID
信号:数字1-31 34-64
宏SIGINT=2
ctrl+d 发送信号2 SIGINT
kill -l察看所有信号
信号SIGKILL SIGSTOP不能被处理.
案例:
发送信号
int kill(pid_t pid,int s)
进程ID:
>0:发送信号到指定进程
=0:发送信号到该进程所在进程组的所有进程
-1:发送给所有进程,除init外
<0:发送给指定的进程组(组ID=绝对值)
#include <stdio.h>
#include <signal.h>
main()
{
int i;
// while(1)
for(i=0;i<5;i++)
{
kill(4601,35);
}
}
signal
kill
3.信号的应用
3.1.延时器timeout
SIGALRM
信号发出函数:alarm
3.2.定时器
// ITIMER_REAL 墙上时间,时钟时间
// ITIMER_VIRTUAL 系统空间时间+用户空间时间
//ITIMER_PROF ITIMER_VIRTUAL+休眠时间
const struct itimerval *val, // 定时器的时间参数
struct itimer *oldval); // 返回原来设置的定时器
// 如果=NULL,则不返回
{
struct timeval it_interval; // 间隔时间:定时器周期
struct timeval it_value; // 延时时间:过多久定时器开始生效
}
{
long tv_sec;//秒
long tv_usec;//微妙
}
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void deal( int s)
{
printf("起床!\n");
}
main()
{
struct itimerval v={0};
signal(SIGALRM,deal);
// v.it_value.tv_sec=3; // 程序启动3秒后触发,可以不设定tv_usec
v.it_value.tv_sec=0;
v.it_value.tv_usec=1; // 让程序一启动就触发。不能设为0
v.it_interval.tv_sec=1; // 间隔1秒
// alarm(5);
setitimer(ITIMER_REAL,&v,0);
while(1)
{
//

}
}
系统与应用程序之间
应用于应用程序之间
父子进程之间
案例1:
使用定时器信号,实现多任务.
实现:
实现7位随机数
使用中断实现时间的显示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <math.h>
#include <curses.h>
WINDOW *w;
int num;
int isstop=0;
void handle( int s)
{
if(s==SIGUSR1)
{
if(isstop==1)
isstop=0;
else
isstop=1;
}
}
main()
{
initscr();
curs_set(0); // 隐藏光标
noecho(); // 禁止输入回显
// keypad(stdscr,TRUE);
// keypad(w,TRUE);
// 创建子窗体
w=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
box(w,0,0); // 给子窗体加边框
refresh();
wrefresh(w);
if(fork())
{
// 显示7位数的随机数
signal(SIGUSR1,handle);
while(1){
if(isstop==1)
{
pause(); // pause会被信号中断停止 ***
}
num=rand()%10000000; // 产生7位随机数
mvwprintw(w,1,2,"%07d",num); // 显示随机数
refresh(); // 刷新屏幕。
wrefresh(w);
usleep(10000); // 暂停10毫秒
}
}
else
{
// 处理按键
while(1)
{
getch();
// if(ch==KEY_ENTER)
{
kill(getppid(),SIGUSR1);
}
}
}
endwin();
}
实现父子进程之间通信
控制进程.
sleep与pause函数被信号影响后,sleep不再继续sleep.
pause不再pause.
练习:
1.在curses显示7位随机数
其他信号函数
raise
4. 信号的可靠与不可靠以及信号的含义
信号有丢失.(信号不排队,压缩成一个)
由于历史的缘故:信号有压缩的需求.
可靠信号(实时信号)与不可靠信号(非实时信号).
早期信号 1-31 31个信号, 不可靠(与系统有关).
后期信号34-64 31个信号,可靠信号(用户信号)
这就是"不可靠信号"的来源。它的主要问题是:
- 进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
- 信号可能丢失,后面将对此详细阐述。
因此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失。
5.信号的操作
信号导致的问题
1.信号屏蔽
SIG_BLOCK
SIG_UNBLOCK
SIG_SETMASK
const sigset_t *sigs, // 操作的信号集合
sigset_t *oldsigs); // 返回原来操作的信号集合
sigset_t sigs;
2.加入屏蔽信号
一组信号集合维护函数:
2.1. 清空集合sigemptyset
2.2. 添加信号到集合sigaddset
2.3. 从集合删除信号sigdelset
2.4. 添加所有信号到集合sigfillset
2.5. 判定信号是否在集合sigismember
3.屏蔽信号
4.接触屏蔽
2.信号屏蔽的切换
int sigsuspend(sigset_t *sigs);
屏蔽新的信号,原来的信号失效.
sigsuspend是阻塞函数.对参数信号屏蔽.
对参数没有指定的信号不屏蔽,但当没有屏蔽信号处理函数调用完毕
sigsuspend返回条件:
1.信号发生,并且信号是非屏蔽信号
sigsuspend设置新的屏蔽信号,保存旧的屏蔽信号
而且当sigsuspend返回的时候,恢复旧的屏蔽信号.
#include <stdio.h>
#include <signal.h>
void h( int s)
{
printf("非屏蔽信号发生!\n");
}
main()
{
sigset_t sigs;
signal(SIGWINCH,h);
sigemptyset(&sigs);
sigaddset(&sigs,2);
printf("屏蔽开始!\n");
sigsuspend(&sigs);
printf("屏蔽结束!\n");
}
int sigpending(sigset_t *sets);
#include <stdio.h>
#include <signal.h>
void h( int s)
{
printf("抽空处理int信号\n");
}
main()
{
int sum=0;
int i;
// 1.
signal(SIGINT,h);
sigset_t sigs,sigp,sigq;
// 2.
sigemptyset(&sigs);
sigemptyset(&sigp);
sigemptyset(&sigq);
sigaddset(&sigs,SIGINT);
// 3.
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=1;i<=10;i++)
{
sum+=i;
sigpending(&sigp);
if(sigismember(&sigp,SIGINT))
{
printf("SIGINT在排队!\n");
sigsuspend(&sigq);
// 使原来屏蔽信号无效,开放原来信号
// 使新的信号屏蔽,
// 当某个信号处理函数处理完毕
// sigsuspend恢复原来屏蔽信号,返回
}
sleep(1);
}
printf("sum=%d\n",sum);
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("Over!\n");
}
回顾:
1.进程控制sleep pause
2.理解信号的中断流程
3.发射信号(Shell/code),处理信号
4.alarm与setitimer
5.信号应用:实现简单多任务与进程之间通信
6.使用信号+sleep/pause控制进程
7.信号的屏蔽
8.了解sigpending与 sigsuspend的使用.
作业:
1.写一个程序,创建两个子进程,分别计算1-5000与5001-1000素数,
通过信号让,父进程专门完成数据保存.
2.完成课堂上的作业:
显示7位随机数,同时使用定时器信号显示时间
在使用键盘控制7位随机数的停止与继续显示.
(建议美化)
3.完成如下作业:
在屏幕水平同时显示移动两个字符.
思考:
信号处理函数调用过程中,是否被其他信号影响.
明天:
1.信号与进程间数据传递
sigqueue=kill与sigaction=signal
2.IPC:
基于文件
无序文件:映射
有序文件:管道文件:有名/匿名
socket文件
基于内存
无序内存
内存共享
有序内存
共享队列