一.进程的基本控制
1.进程的常见控制函数1.1.为什么需要控制进程?
1.2.pause/sleep/usleep
1.3.atexit on_exit
atexic.c
#include <stdio.h>
#include <stdlib.h>
void fun()
{
printf("over\n");
}
main()
{
atexit(fun);//进程执行完毕后 在退出之前调用fun函数
printf("Process!\n");
}
在多进程下文件读写是共享的
问题:
怎么知道一个文件正在被另外进程读写?
解决方案:
文件锁。(建议锁) 其实就是告诉我们一个文件的状态
API:
fcntl(文件锁受内核参数影响)
编程技巧:
对文件加锁
判定一个文件是否存在锁
函数说明:
int fcntl(
int fd,//被加锁的文件描述符号
int cmd,//操作方式:F_SETLK(已经加锁,异常返回) F_UNLK F_SETLKW(已经加锁,则阻塞)
struct flock *lk);//锁的描述
返回值:
0:加锁成功
-1:加锁失败
案例:
写两个程序:
A:加锁
setlock.c
#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;
//lk.l_pid不用设置 进程自动完成
//加锁
r=fcntl(fd,F_SETLK,&lk);//另一个进程如果同时对该文件加锁 位置不冲突就行
if(r==0) printf("加锁成功!\n");
else printf("加锁失败!\n");
while(1);
}
B:获取锁的信息
getlock.c
#include <stdio.h>
#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.进程之中,默认信号处理(向进程发送没有注册的信号)
signaldeal1.c
#include <unistd.h>
#include <stdio.h>
main()
{
while(1)
{
printf("jin cheng zhi xing:%d!\n",getpid());
sleep(1);
}
}
编译执行 在另个终端向该进程发送信号
2.进程之中,用户信号处理
signaldeal2.c
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void handle(int s)
{
printf("xin hao ¡\n");
}//只有处理函数执行完毕后 主函数才继续执行(中断)
main()
{
//signal(SIGWINCH,handle);
signal(35,handle);
while(1)
{
printf("jin cheng zhi xing:%d!\n",getpid());
sleep(1);
}
}
如果发送的信号跟注册的信号不一致,则执行默认信号处理
3.中断
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=绝对值)
2.信号发送与安装
signal
kill
kill.c
#include <stdio.h>
#include <signal.h>
main()
{
int i;
//while(1)
for(i=0;i<5;i++)
{
kill(4601,35);
}
}
3.1.延时器timeout 到指定延时时间后发送一个信号SIGALRM;
SIGALRM
信号发出函数:alarm() 指定多长时间后向本进程发送一个SIGALRM信号
alarm.c
#include <stdio.h>
#include <signal.h>
void deal(int s)
{
printf("起床!\n");
//alerm(5);每隔多长时间后做什么事情 类似定时器
}
main()
{
signal(SIGALRM,deal);
alarm(5);
while(1)
{
//.....
}
}
3.2.定时器int setitimer(int which,//计时方式 一般使用ITIMER_REAL
//ITIMER_REAL / ITIMER_VIRTUAL /ITIMER_PROF
const struct itimerval *val,//定时器的时间参数
struct itimer *oldval);//返回原来设置的定时器
//如果=NULL,则不返回
struct itimerval
{
struct timeval it_interval;//间隔时间
struct timeval it_value;//延时时间
}
struct timeval
{
long tv_sec;
long tv_usec;
}
alarm2.c
#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;
v.it_value.tv_usec=1;//如果全设置为0 无穷时间后才触发 一般设为1
v.it_interval.tv_sec=1;
setitimer(ITIMER_REAL,&v,0);
while(1)
{
//.....
}
}
信号应用:
系统与应用程序之间
应用于应用程序之间
父子进程之间
案例1:
使用定时器信号,实现多任务.
实现:
实现7位随机数
使用中断实现时间的显示
demo1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <curses.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
WINDOW *wtime,*wnumb;
int num,i;
void showtime(int s)
{
time_t tt;
struct tm *t;
if(s==SIGALRM)
{
tt=time(0);
t=localtime(&tt);
mvwprintw(wtime,1,1,"%02d:%02d:%02d",t->tm_hour,t->tm_min,t->tm_sec);
refresh();
wrefresh(wtime);
wrefresh(wnumb);//可选
}
}
main()
{
struct itimerval val;
initscr();
curs_set(0);
signal(SIGALRM,showtime);
bzero(&val,sizeof(struct itimerval));
val.it_value.tv_sec=0;
val.it_value.tv_usec=1;
val.it_interval.tv_sec=1;
wnumb=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
wtime=derwin(stdscr,3,10,0,COLS-10);
box(wnumb,0,0);
box(wtime,0,0);
setitimer(ITIMER_REAL,&val,0);
refresh();
wrefresh(wtime);
wrefresh(wnumb);
while(1)
{
num=0;
for(i=0;i<7;i++)
{
num=num*10+rand()%10;
}
mvwprintw(wnumb,1,2,"%07d",num);
refresh();
wrefresh(wnumb);
wrefresh(wtime);
usleep(100000);
}
endwin();
}
案例2: 屏幕显示随机数 按下任意键就会停止 再按下继续显示
实现父子进程之间通信
控制进程.
sleep与pause函数被信号影响后,sleep不再继续sleep;pause不再pause.
demo2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <curses.h>
#include <math.h>
WINDOW *w;
int num,i;
int isstop=0;
void handle(int s)
{
if(s==SIGUSR1)
{
if(isstop==0){
isstop=1;
}
else{
isstop=0;
}
}
}
main()
{
initscr();
curs_set(0);
w=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
keypad(stdscr,TRUE);//支持功能键
keypad(w,TRUE);
noecho();
box(w,0,0);
refresh();
wrefresh(w);
if(fork())
{
signal(SIGUSR1,handle);
while(1)
{
if(isstop==1)
{
pause();//阻塞 直到信号发送
}
num=0;
for(i=0;i<7;i++)
{
num=num*10+rand()%10;
}
mvwprintw(w,1,2,"%07d",num);
refresh();
wrefresh(w);
usleep(10000);
}
}
else//child
{
//输入
int ch;
while(1)
{
ch=getch();
kill(getppid(),SIGUSR1);
}
}
endwin();
}
其他信号函数
raise(int sig) 向自己发送信号
4.信号的可靠与不可靠以及信号的含义
信号有丢失.(信号压缩) 向指定进程多次发送信号 进程收到的次数有丢失的现象
由于历史的缘故:信号有压缩的需求.
可靠信号(实时信号)与不可靠信号(非实时信号).
早期信号 1-31 31个信号, 不可靠(与系统有关).
后期信号34-64 31个信号,可靠信号(用户信号) 进程收到的次数不会丢失
5.信号的操作
信号导致的问题 想在执行某段代码的时候不被信号中断
int sigprocmask(int how,//操作方式SIG_BLOCK /SIG_UNBLOCK / SIG_SETMASK
const sigset_t *sigs,//操作的信号集合
sigset_t *oldsigs);//返回原来操作的信号集合
1.声明信号集合
sigset_t sigs;
2.加入屏蔽信号
一组信号集合维护函数:
2.1. 清空集合sigemptyset
2.2. 添加信号到集合sigaddset
2.3. 从集合删除信号sigdelset
2.4. 添加所有信号到集合sigfillset
2.5. 判定信号是否在集合sigismember
3.屏蔽信号
4.解除屏蔽
aa.c
#include <stdio.h>
#include <signal.h>
main()
{
int sum=0;
int i;
//1.
sigset_t sigs;
//2.加入屏蔽
sigemptyset(&sigs);
sigaddset(&sigs,SIGINT);
//3.设置屏蔽
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=1;i<=5;i++)
{
sum+=i;
sleep(1);
}
printf("sum=%d\n",sum);
//4.解除屏蔽
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("Over!\n");//如果在信号屏蔽后的进程中有信号产生
//就会排队等待 解除屏蔽后会先执行信号的操作
}
2.信号屏蔽的切换
int sigsuspend(sigset_t *sigs);
屏蔽新的信号,原来的信号失效.
sigsuspend是阻塞函数.对参数信号屏蔽.
对参数没有指定的信号不屏蔽,但当没有屏蔽信号处理函数调用完毕
sigsuspend返回条件:
1.信号发生,并且信号是非屏蔽信号
2.信号必须要处理,而且处理函数返回后,sigsuspend才返回.
sigsuspend设置新的屏蔽信号,保存旧的屏蔽信号
而且当sigsuspend返回的时候,恢复旧的屏蔽信号.
3.查询被屏蔽的信号
int sigpending(sigset_t *sets);
mask.c
#include <stdio.h>
#include <signal.h>
void h(int s)
{
printf("抽空处理SIGINT信号\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");
}
demo1.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle(int s)
{
printf("外部用户中断处理...!\n");
sleep(3);
printf("外部用户中断处理完毕!\n");
}
main()
{
int sum=0;
int i;
sigset_t sigs,sigt,sigu;
sigemptyset(&sigs);
sigemptyset(&sigt);
sigemptyset(&sigu);
sigaddset(&sigs,SIGINT);
//sigfillset(&sigs);
signal(SIGINT,handle);
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=0;i<10;i++)
{
printf("正在拷贝电影<%d>!\n",i);
sleep(5);//模拟业务处理时间比较长
printf("正在拷贝电影<%d>完毕!\n",i);
sigpending(&sigu);
if(sigismember(&sigu,SIGINT))
{
sigsuspend(&sigt);
}
}
printf("所有电影拷贝完毕\n",sum);
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("over!\n");
}
6.最新版本的信号发送与处理
sigqueue/sigaction
1.思考:信号中断函数调用中是否被其他信号中断.
信号函数调用中只屏蔽本身信号,不屏蔽其他信号.
sighandle.c
#include <stdio.h>
#include <signal.h>
void handle(int s)
{
printf("Hello!start\n");
sleep(10);
printf("Hello!end\n");
}
main()
{
signal(SIGINT,handle);
while(1);
}
以上代码在执行过程中多次按下Ctrl+C 信号排队(有丢失 每次执行一个)
2.怎么保证函数调用中屏蔽指定的信号呢?
sigaction可以指定处理函数调用的时候屏蔽信号;在处理信号的时候,接受数据.功能类似signal
sigqueue发送信号的时候,可以发送数据.
sigaction/sigqueue是signal/kill的增强版本
3.函数说明
使用sigaction(相当与signal)/sigqueue(相当与kill)有两个理由.
3.1.稳定
3.2.增强功能
int sigaction(
int sig,//被处理信号
const struct sigaction*action,//处理函数及其参数
struct sigaction*oldact//返回原来的处理函数结构体
)
返回:
0:成功
-1:失败
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int,siginfo_t*,void*);
sigset_t *mask;//屏蔽信号
int flags;//SA_SIGINFO 信号处理两个函数靠着个参数指定 如果指定先执行下面的那个
void**//保留成员.
}
案例:
1.使用sigaction处理信号,使用kill发送信号
sigaction.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handle(int s)
{
printf("OOOK!\n");
sleep(5);
printf("K000!\n");
}
main()
{
struct sigaction act={0};
act.sa_handler=handle;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGINT);//函数不被SIGINT干扰(先排队 结束后影响)
//(函数本身就不会被SIGUSR1干扰)
act.sa_flags=0;
sigaction(SIGUSR1,&act,0);
while(1);
}
2.使用sigaction处理信号,使用sigqueue发送信号
sigaction2.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handle(int s,siginfo_t* info,void *d)
{
printf("OOOK:%d\n",info->si_int);
sleep(5);
printf("K000!\n");
}
main()
{
struct sigaction act={0};
act.sa_sigaction=handle;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGINT);
act.sa_flags=SA_SIGINFO;
sigaction(SIGUSR1,&act,0);
while(1);
}
sigqueu.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
main()
{
union sigval val;
val.sival_int=8888;
sigqueue(3972,SIGUSR1,val);//用他来向指定进程发送信号
}
3.发送信号的同时处理数据
回顾:
1.进程控制sleep pause2.理解信号的中断流程
3.发射信号(Shell/code),处理信号
4.alarm与setitimer
5.信号应用:实现简单多任务与进程之间通信
6.使用信号+sleep/pause控制进程
7.信号的屏蔽
8.了解sigpending与 sigsuspend的使用.
任务:
1.写一个程序,创建两个子进程,分别计算1-5000与5001-1000素数,
通过信号让,父进程专门完成数据保存.
2.完成如下作业:
在屏幕水平同时显示移动两个字符.
思考:
信号处理函数调用过程中,是否被其他信号影响.
明天:
1.信号与进程间数据传递
sigqueue=kill与sigaction=signal
2.IPC:
基于文件
无序文件:映射
有序文件:管道文件:有名/匿名
socket文件
基于内存
无序内存
内存共享
有序内存
共享队列