一,进程
1,进程的概念
程序
存放在磁盘上的指令和数据的有序集合(文件)静态的。
进程
指行一个程序所分配的资源的总称
进程是程序的一次执行过程
动态的,包含创建,调度,执行和消亡
2,进程包含的内容
进程控制块,CPU寄存器值,堆栈
进程控制块(pcb)
- 进程标识(PID)
- 进程用户
- 进程状态,优先级
- 文件描述符表
进程类型
交互进程:在shell下启动,可以在前台运行,也可以在后台运行。
批处理进程:和终端无关,被提交一个作业队列中以便顺序执行。
守护进程:和终端无关,一直在后台运行。
3,进程的状态
运行态:进程正在运行,或者准备运行
等待态:进程正在等待一个事件的发生或某种资源
可中断/不可中断
停止态:进程被终止,收到信号后可以继续运行。
死亡态:已终止的进程,但pcb没有被释放。
查看进程的信息
命令 | 描述 |
---|---|
ps | 查看系统进程快照 |
top | 查看进程动态信息 |
/proc | 查看进程详细信息 |
nice | 按用户指定的优先级运行进程 |
renice | 改变正在运行进程的优先级 |
jobs | 查看后台进程 |
bg | 将挂起的进程在后台运行 |
fg | 把后台运行的程序放到前台运行 |
4,创建子进程
进程创建---fork
#include<unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1;
成功时父进程返回子进程的进程号,子进程返回0
通过fork的返回值来区分父子进程
pid_t pid;
if(pid=fork()<0){
perror("fork");
return -1;
}
else if(pid==0){
printf("child process:my pid is %d\n",getpid());
}else{
printf("parent process:my pid is %d\n",getpid());
}
5,父子进程
1,子进程继承了父进程的内容
2,父进程有独立的地址空间,互不影响
3,若父进程先结束
1)子进程成为孤儿进程,被init进程收养
2)子进程变成孤儿进程
4,若子进程先结束
1)父进程如果没有及时回收,子进程变成僵尸进程。
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<2;i++){
pid=fork();
if(pid<0){
perror("fork");
exit(-1);
}else if(pid>0){
printf("this is parent\n");
}else if(pid==0){
printf("this is child\n");
}
}
6,结束进程
#include<stdlib.h>
#include<unistd.h>
void exit(int status);
void _exit(int status);
结束当前的进程并将status返回
exit结束进程时会刷新(流)缓冲区
7,进程回收–wait,waitpid
#include<unistd.h>
pid_t wait(int *status);
成功时返回回收的子进程的进程号,失败时返回EOF
若子进程没有结束,父进程一直阻塞
若有多个进程,哪个先结束就先回收
status指定保存了子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB,不接收返回值。
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pit_t pid;
int i;
int status;
for(i=0;i<10;i++){
pid=fork();
if(pid<0){
perror("fork");
exit(-1);
}else if(pid>0){
printf("this is parent\n");
wait(&status);
printf("child is dead\n");
while(1);
}else if(pid==0){
printf("this is child\n");
sleep(2);
exit(1);
}
}
}
#include<unistd.h>
pid_t waitpid(pid_t pid,int *status,int option);
成功时返回回收的子进程的pid或0,失败时返回EOF
pid可用于回收哪个子进程或任意子进程
status指定用于保存子进程返回值和结束方式抵制
option指定了回收方式,0或WNOHANG
waitpid(pid,&status,0);
waitpid(pid,&status,WNOHANG);
waitpid(-1,&status,0);
waitpid(-1,&status,WNOHANG);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
pid_t pid
int i,status;
for(i=0;i<10;i++){
pid=fork();
if(pid<0){
perror("fork");
exit(-1);
}else if(pid>0){
printf("this is parent\n");
//wait(&status);
waitpid(pid,&status,0);
printf("child is dead ,ret=%d\n",WEXITSTATUS(status));
while(1);
}else if(pid==0){
printf("this is child\n");
sleep(2);
exit(1);
}
}
}
8,进程的返回方式
子进程通过exit/_exit/return 返回某个值(0-255)
父进程调用wait(&status)回收
状态 | 描述 |
---|---|
WIFEXITED(&status) | 判断子进程是否正常结束 |
WEXITSTATUS(status) | 获取子进程返回值 |
WIFSIGNALED(status) | 判断子进程是否被信号结束 |
WTERMSIG(status) | 获取结束子进程的信号类型 |
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
pit_t pid;
int i,ret;
int status;
for(i=0;i<10;i++){
pid=fork();
if(pid<0){
perror("fork");
exit(-1);
}else if(pid>0){
printf("this is parent\n");
wait(&status);
printf("child is dead ,ret=%d\n",WEXITSTATUS(status));
while(1);
}else if(pid==0){
printf("this is child\n");
sleep(2);
exit(1);
}
}
}
进程–exec函数族
- 进程调用exec函数族来执行某个程序
- 进程当前的内容被指定的程序替换
- 实现让父子进程执行不同的程序
1>父进程创建子进程
2>子进程调用exec函数族
3>父进程不受影响
进程–execl/execlp
#include<unistd.h>
int execl(const char *path,const char *arg,…);
int execl(const char *file,const char *arg,…);
成功时返回指定的程序,失败时返回EOF
path:执行的程序名称,包含路径
arg:传递给执行程序的参数列表
file:执行的程序的名称,在PATH中查找
#include<stdio.h>
#include<unistd.h>
int main()
{
/*if(execl("/bin/ls","ls","-a","-l","./",NULL)<0){
perror("execl");
}
*/
if(execlp("ls","ls","-a","-l","./",NULL)<0){
perror("execlp");
}
printf("i ls current dictionary\n");
}
进程–execv/execvp
#include<unistd.h>
int execv(const char *path,char *const argv[]);
int execvp(const cahr *file , char *const argv[]);
成功时执行指定的程序,失败时返回EOF
arg…封装成指针数组的形式
//执行ls命令,显示/etc目录下所有文件的详细信息
char *arg[]={"ls","-a","-l","/etc",NULL};
if(execv("/bin/ls,arg)<0){
perror("execv");
}
if(execvp("ls",arg)<0){
perror("execvp");
}
进程–system
#include<stdio.h>
int system(const char* command);
成功时返回命令command的返回值;失败时返回EOF;
当前进程等待command执行结束后才继续执行;
守护进程
守护进程是Linux三种进程类型之一;
通常在系统启动时运行,系统关闭时结束;
Linux系统中大量使用,很多服务程序以守护进程方式运行;
守护进程的特点
始终在后台运行
独立与任何终端
周期性的执行某种任务或等待处理特定事件
守护进程–会话,控制终端
Linux以会话,进程组的方式管理进程。
每个进程属于一个进程组
会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会会创建一个会话,所有通过该终端运行的进程都属于这个会话。
终端关闭时,所有相关进程都会被结束。
守护进程的创建
- 创建子进程,父进程退出
if(fork()>0){
exit(0);
}
子进程变成孤儿进程,被init进程收养
子进程在后台运行 - 子进程创建新会话
if(setsid()<0){
exit(-1);
}
子进程称为新的会话组组长
子进程脱离原先的终端 - 更改当前的工作目录
chdir("/");
chdir("/tmp");
守护进程一直在后台运行,其工作目录不能被卸载
重新设定当前的工作目录cwd - 重设文件权限
if(umask(0)<0){
exit(-1);
}
文件权限掩码设置为0
只影响当前进程 - 关闭打开的文件描述符
int i;
for(i=0;i<getdtablesize();i++){
close(i);
}
关闭所有从父进程继续的打开文件
已脱离终端,stdin/stdout/stderr无法再使用
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
pid_t pid;
pid=fork();
if(pid<0){
perror("fork");
return -1;
}else if(pid>0){
exit(0);
}
pid=setsid();
if(pid==-1){
perror("setsid");
return -1;
}
chdir("/");
umask(0);
int i;
for(i=0;i<2;i++){
close(i);
}
FILE *fp;
time_t ctm;
fp=fopen("1.log","w");
while(1){
ctm=time(NULL);
fputs(ctime(&ctm),fp);
fflush(fp);
sleep(1);
}
}
二,线程
线程概念
- 进程在切换时系统开销大
- 很多操作系统引入轻量级进程LWP
- 同一进程中的线程共享相同的地址空间
- Linux下不区分进程和线程
线程特点
- 通常线程指的是共享相同地址空间的多个任务task_struct
- 使用多线程的好处:
1>大大提高任务切换的效率
2>避免了额外的TLB&cache的刷新
线程共享资源
一个进程中多个线程共享一下资源:
- 可执行的指令
- 静态数据
- 进程中打开的文件描述符
- 当前工作目录
- 用户ID
- 用户组ID
线程私有资源
每个线程私有的数据包括:
- 线程ID(TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号
- 优先级
- 执行状态和属性
Linux线程库
pthread线程库中提供了如下操作:
1>创建线程
2>回收线程
3>结束线程
同步和互斥机制
1>信号量
2>互斥锁
线程创建–pthread_create
#include<pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(routine)(void *),void *arg);
成功返回0,失败时返回错误码
thread:线程对象
attr:线程属性,NULL代表默认属性
routine:线程执行的函数
arg:传递给routine的参数,参数是void *,注意传递参数的数据格式
pthread_t pthread_self(void) :查看自己的TID
- 创建一个线程,查看线程的tid和pid
- 创建多个线程,查看线程的tid和pid ,立即线程和进程PCB关系。
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void *function1(void *arg){
printf("this is thread function1\n");
sleep(10);
}
void *function2(void *arg){
printf("this is thread function2\n");
while(1) sleep(1);
}
int main()
{
pthread_t tid;
int err;
err=pthread_create(&tid,NULL,function1,void *arg);
if(err!=0){
printf("create thread:%s",strerror(err));
return -1;
}
sleep(1);
err=pthread_create(&tid,NULL,function2,void *arg);
if(err!=0){
printf("create thread:%s",strerror(err));
return -1;
}
sleep(1);
}
线程回收–pthread_join
#include<pthread.h>
int pthread_join(pthread_t thread,void **retval);
成功返回0,失败时返回错误码
thread:要回收的线程对象
调用线程阻塞直到thread结束
*retval接收线程thread的返回值
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void *function1(void *arg){
printf("this is thread function1\n");
sleep(10);
}
int main()
{
pthread_t tid[100];
int err,i;
for(i=0;i<100;i++){
err=pthread_create(&tid[i],NULL,function1,NULL);
if(err!=0){
printf("create pthread:%s\n",strerror(err));
return -1;
}
}
sleep(5);
for(i=0;i<100;i++){
err=pthread_join(tid[i],NULL);
if(err!=0){
printf("join pthread:%s\n",strerror(err));
return -1;
}
}
while(1) sleep(10);
}
线程结束—pthread_exit
#include<pthread.h>
void pthread_exit(void **retval);
结束当前线程
retval可被其他的线程通过pthread_join获取
线程私有资源被释放
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
void *funct(void *arg)
{
int ret;
printf("this is funct pthread\n");
sleep(1);
ret=5
pthread_exit((void *)ret);
}
int main()
{
int ret i;
pthread_t tid;
ret=pthread_create(&tid,NULL,funct,(void *)i);
void *retval;
pthread_join(&tid,&retval);
printf("retval=%s\n",(char *)retval);
}
编译:gcc -o test test.c -lpthread
pthread_detach: 使用了detach就不用join
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
void *funct(void *arg)
{
int ret;
printf("this is funct pthread %d\n",(void *)arg);
sleep(20);
// ret=5
// pthread_exit((void *)ret);
}
int main()
{
int ret i;
pthread_t tid[NUM];
for(i=0;i<NUM;i++){
ret=pthread_create(&tid[i],NULL,funct,(void *)i);
pthread_detach(tid[i]);
}
/*
re=pthread_join(&tid,&retval);
if(re!=0){
printf("join error %s\n",sterror(re));
}
printf("retval=%s\n",(char *)retval);
*/
}
取消一个线程
- int pthread_cancel(pthread_t thread);
- void pthread_testcancel(void);
- int pthread_setcancelstate(int state,int *oldstate);
state:1>PTHREAD_CANCEL_ENABLE
2>PTHREAD_CANCEL_DISABLE - int pthread_setcanceltype(int type, int *oldstate);
type: 1> PTHREAD_CANCEL_DEFERRED
2>PTHREAD_CANCEL_ASYNCHRONOUS
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
void *funct(void *arg)
{
int a=0;
a=(int)arg;
int td=pthread_self();
pthread_detach(pthread_self());
printf("this is thread a=%d\n",n);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
while(1){
//sleep(1);
//pthread_testcancel();
}
}
int main()
{
int ret i;
pthread_t tid
ret=pthread_create(&tid,NULL,funct,(void *)i);
pthread_detach(tid);
if(ret!=0){
printf("pthread_create:%s\n",strerror(ret));
exit(0);
}
sleep(5);
//pthread_cancel();
while(1){
sleep(1);
}
}
线程间通信
- 线程共享同一进程的地址空间
- 优点:线程间通信通过全局变量交换数据
- 缺点:多个线程访问共享数据时需同步或互斥机制
线程间的同步
同步机制(PV操作)
是指多个任务按照约定的先后次序完成配合完成一件事情,由信号量来决定线程是继续运行还是阻塞等待。
信号量
信号量代表某一资源,其值代表系统中该资源的数量。信号量是一个受保护的变量,只能通过三种方式来访问:初始化,P操作(申请资源),V操作(释放资源)。POSIX定义了两类信号量:无名信号量(基于内存的信号量),有名信号量。
pthread常用的信号量操作函数如下:int sem_init(sem_t *sem,int pshared,usigned int value); //信号量初始化 sem向要初始化的信号量对象,pshared指0-线程间,1-进程间,value:信号量初值。
int sem_wait(sem_t *sem);//p操作
int sem_post(sem_t *sem); //v操作
P操作
V操作
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<sempthore.h>
char buf[100];
sem_t sem;
void *write(void *arg){
sem_t
int a=0;
a=(int)arg;
int td=pthread_self();
pthread_detach(pthread_self());
while(1){
fgets(buf,100,stdin);
sem_post(&sem); //申请资源,写线程,写完后释放资源,v操作
}
}
void *read(void *arg){
int a=0;
a=(int)arg;
int td=pthread_self();
pthread_detach(pthread_self());
while(1){
memset(buf,0,100);
sem_wait(&sem);
printf("buf :%s\n",buf); //消耗资源,读线程,等待写线程的资源,使用P操作
}
}
int main(){
int ret,i=0;
pthread_t tid1,tid2;
sem_init(&sem,0,0);
re=pthread_create(&tid1,NULL,read,(void *)i);
pthread_detach(tid);
if(ret!=0){
printf("pthread_create:%s\n",strerror(ret));
exit(0);
}
ret=pthread_create(&tid2,NULL,write,(void *)i);
pthread_detach(tid);
if(ret!=0){
printf("pthread_create :%s\n",strerror(ret));
exit(0);
}
while(1){
sleep(1);
}
}
线程间的互斥
临界资源,互斥机制,互斥锁
临界资源:一次只允许一个任务(进程,线程)访问的共性资源
临界区:访问临界区的代码
互斥机制:mutex互斥锁,任何访问临界资源前申请锁,访问完后释放锁。
互斥锁初始化—pthread_mutex_init
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
成功时返回0,失败时返回错误码EOF
mutex指向要初始化的互斥锁对象
attr:互斥锁属性,NULL表示缺省默认属性
man函数出现NO manual entry for pthread)mutex_xxx 使用apt-get install manpages-posix-dev
申请锁–pthread_mutex_block
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex:指向要初始化的互斥锁对象
如果无法获得锁,任务阻塞
释放锁–pthread_mutex_unlock
#include<pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功时返回0,失败时返回错误码
mutex指向要初始化的互斥锁对象
执行完临界区要及时释放锁
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<sempthore.h>
char buf[100];
FILE *fp;
pthread_mutex_t mutex;
void *write1(void *arg){
char *c1="hello world";
int len,i;
char *c2;
int a=0;
a=(int)arg;
len=strlen(c1);
int td=pthread_self();
pthread_detach(pthread_self());
c2=c1;
while(1){
pthread_mutex_lock(&mutex);
for(i=0;i<len;i++){
fputc(*c1,fp);
fflush(fp);
c1++;
usleep(10000);
}
pthread_mutex_unlock(&mutex);
c1=c2;
sleep(1);
}
}
void *write2(void *arg){
char *c2="how are you"
int len,i;
char *c2;
int a=0;
a=(int)arg;
len=strlen(c1);
int td=pthread_self();
pthread_detach(pthread_self());
while(1){
pthread_mutex_lock(&mutex);
for(i=0;i<len;i++){
fputc(*c1,fp);
fflush(fp);
c1++;
usleep(10000);
}
pthread_mutex_unlock(&mutex);
c1=c2;
sleep(1);
}
}
int main(){
int ret,i=0;
pthread_t tid1,tid2;
fp=fopen("1.txt","w");
if(!fp){
perror("fopen");
return -1;
}
pthread_mutex_init(&mutex,MULL)
re=pthread_create(&tid1,NULL,write1,(void *)i);
pthread_detach(tid);
if(ret!=0){
printf("pthread_create:%s\n",strerror(ret));
exit(0);
}
ret=pthread_create(&tid2,NULL,write2,(void *)i);
pthread_detach(tid);
if(ret!=0){
printf("pthread_create :%s\n",strerror(ret));
exit(0);
}
while(1){
sleep(1);
}
}
进程间通信
早期unix进程间的通信方式:无名管道(pipe),有名管道(fifo),信号(signal)。
System V IPC :共享内存,消息队列,信号灯集(进程间同步),套接字。
1>无名管道的特点:
只能用于具有亲缘关系的进程之间的通信,单工的通信方式,具有固定的读写端。无名管道创建时会返回两个文件描述符,分别用于读写管道。
无名管道的创建–pipe
#include<unistd.h>
int pipe(int pfd[2]);
成功时返回0,失败时返回EOF
pfd包含两个元素的整型数组,用来保存文件描述符
pfd[0]用于读管道;pfd[1]用于写管道;
#include<unistd.h>
#include<stdio.h>
#include<string.h>
int main()
{
int pfd[2];
int re;
re=pipe(pfd);
if(re==-1){
perror("pipe");
return -1;
}
pid=fork();
if(pid<0){
perror("fork");
return -1;
}else if(pid==0){
char buf[64]="haha,i am child.pipe message!\n";
while(1){
write(pfd[1],buf,strlen(buf));
sleep(1);
}
}else{
char buf[64];
while(1){
memset(buf,0,64);
read(pfd[0],buf,64);
if(re>0){
printf(" message form child %s\n",buf);
}else{
break;
}
}
}
无名管道读特性:读无名管道:写端存在:1)有数据 2)无数据(阻塞)
写端不存在:1)有数据(不存在) 2)无数据(立即返回)
写无名管道特性:写无名管道:读端存在: 1)有空间 2)无空间(阻塞)
读端不存在:管道破裂
如何获取无名管道的大小?
#include<unistd.h>
#include<stdio.h>
#include<string.h>
int main()
{
int pfd[2];
pid_t pid;
int re,i;
re=pipe(pfd);
if(re==-1){
perror("pipe");
return -1;
}
for(i=0;i<10000000;i++){
write(pfd[1],"a",1);
printf("i=%d\n".i);
}
}
2>有名管道特点:
- 对应管道文件,可用于任意进程之间进行通信
- 打开管道时可指定读写方式
- 通过文件IO操作,内容存放咋内存中
有名管道创建–mkfifo
#include<unistd.h>
#include<fcntl.h>
int mkfifo(const char *path,mode_t mode);
成功时返回0,失败时返回EOF
path创建的管道文件路径
mode 管道文件的权限,如0666
fifowrite.c
#include<sys/types.h>
#inlcude<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
int main()
{
int re;
int fd;
char buf[64];
unlink("/myfifo");
re=mkfifo("/myfifo","0666);
fi(re==-1){
perror("mkfifo");
return -1;
}
fd=open("/myfifo",O_WRONLY);
if(fd<0){
perror("open");
return -1;
}
strcpy(buf,"fifo write test");
while(1){
write(fd,buf,strlen(buf));
sleep(1);
}
}
fiforead.c
#include<sys/types.h>
#inlcude<sys/stat.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
int main()
{
int re;
int fd;
char buf[64];
fd=open("/myfifo",O_RDONLY);
if(fd<0){
perror("open");
return -1;
}
while(1){
memset(buf,0.64);
read(fd,buf,64);
printf("%s\n",buf);
sleep(1);
}
}
3>信号
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式,Linux内核通过信号通知用户进程,不同的信号代表不同的事件。
Linux对早期的unix信号机制进行扩展
进程对信号有不同的相应方式:缺省方式,忽略信号,捕捉信号
Linux下 kill -l 命令查看信号
信号相关命令kill/killall
kill [-signal] pid
默认发送SIGTERM -signal :可指定信号,pid:指定发送对象
killall [-u user|prog]
prog:指定进程名 user:指定用户名
信号发送–kill/raise
#include<unistd.h>
#include<signal.h>
int kill(pid_t pid,int sig);
int raise(int sig) //给自己发信号
成功时返回0,失败时返回EOF
pid接受进程的进程号
0代表同组进程,-1代表所有进程
sig信号类型
int alarm(unsigned int seconds);
成功时返回上个定时器的剩余时间,失败时返回EOF
seconds定时器的时间
一个进程中只能设定一个定时器,时间到时产生SIGALARM
int pause(void);
进程一直阻塞,直到被信号中断
被信号终端返回-1
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
alarm(3);
pause();
printf("hello wold\n");
return 0;
}
设置信号响应方式–signal
#include<unistd.h>
#include<signal.h>
void (*signal(int signo,void *(handler)(int)))(int);
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
成功时返回原先的信号处理函数,失败时返回SIG_ERR
signo要设置的信号类型、
handler指定的信号处理函数,SIG_DFL代表缺省方式,SIG_IGN代表忽略信号
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
void handler (int signo){
if(signo==SIGINT){
printf("i have got SIGINT\n");
}
if(signo==SIGQUIT){
printf("I have got SIGQUIE\n");
}
}
int main()
{
signal(SIGINT,handler);
signal(SIGQUIT,handler);
while(1){
sleep(1);
}
return 0;
}
//按两次CTRL+C后,中止程序
#include<stdio.h>
#include<unistd.h>
typedef void (*sight)(int)
sight oldhandle;
int count=0;
void funch(int sig)
{
count++;
printf("i catch ctrl+c!\n");
if(count==2){
signal(SIGINT,oldhandle);
}
}
int main()
{
oldhandle=signal(SIGINT,funch);
while(1) sleep(1);
}
//回收子进程僵尸
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void functh(int sig){
wait(NULL);
}
int main()
{
pid_t pid;
signal(SIGCHLD,functh);
if(pid<0){
perror("fork");
return -1;
}else if(pid==0){
printf("this is child\n");
sleep(10);
exit(0);
}
while(1){
printf("i am working\n");
sleep(1);
}
}
4>System V IPC
Linux下使用ipcs命令可看到
- IPC对象包含:共享内存,消息队列和信号灯集
- 每个IPC对象有唯一的ID
- IPC对象创建后一直存在,直到被显示的删除
- 每个IPC对象有一个关联的key
- ipcs/ipcrm
System V IPC–ftok
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char *path,int proj_id);
成功时返回合法的key值,失败时返回EOF
path存在且可访问的文件路径
proj_id用于生成key的数字,范围1-255
共享内存
- 共享内存是一种最为高效的进程间通信的方式,进程可以直接读写内存,而不需要任何数据的拷贝
- 共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
- 由于多个进程可同时访问内存空间,因此需要同步和互斥机制配合使用。
共享内存使用步骤:
- 创建/打开共享内存
- 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
- 读写共享内存
- 撤销共享内存对象
- 删除共享内存对象
共享内存创建–shmget
#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key,int size,int shmflg);
成功时返回共享内存的id,失败时返回EOF
key和共享内存关联的key,IPC_PROVATE或ftok生成
shmflg共享内存标志位IPC_CREAT|0666
共享内存映射–shmat
#include<sys/ipc.h>
#include<sys/shm.h>
void *shmat(int shmid,const void *shmaddr,int shmflg);
成功时返回映射后的地址,失败时返回EOF
shmid:要映射的共享内存id
shmaddr:映射后的地址,NULL表示由系统自动映射
shmflg: 标志位 0:表示可读写,IPC_RDONLY表示只读
共享内存撤销映射–shmdt
#include<sys/ipc.h>
#include<sys/shm.h>
int shmdt<void *shmaddr);
成功时返回0,失败时返回EOF
不使用共享内存时因撤销隐射
进程结束时自动撤销
共享内存控制–shmctl
#include<sys/ipc.h>
#include<sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
成功时返回0,失败时返回EOF
shmid :要操作的共享内存的id
cmd:要执行的操作 IPC_STAT IPC_RMID
buf:保存成设置共享内存属性的地址
write sharememory.c
#include<stdio.h>
#include<sys/ipc.h>
#include<string.h>
int main()
{
char *addr;
int shmid;
key_t key;
key=ftok(".",23);
if(key==-1){
perror("ftok");
return -1;
}
shmid=shmget(key,1024,IPC_CREATE|0666);
if(shmid==-1){
perror("shmget");
return -1;
}
addr=shmat(shmid,NULL,0);
strcpy(addr,"this my share memory");
shmdt(addr);
//执行后,用ipcs命令查看是否创建了共享内存
}
read sharememory.c
#include<stdio.h>
#include<sys/ipc.h>
#include<string.h>
int main()
{
char *addr;
int shmid;
key_t key;
key=ftok(".",23);
if(key==-1){
perror("ftok");
return -1;
}
shmid=shmget(key,1024,IPC_CREATE|0666);
if(shmid==-1){
perror("shmget");
return -1;
}
addr=shmat(shmid,NULL,0);
printf("get share =%s\n",addr);
shmdt(addr);
shmctl(IPC_RMID,NULL);
4>消息队列
- 消息对列是System V IPC对象的一种
- 消息队列由消息队列ID来唯一标识
- 消息队列就是一个消息的列表。用户可以在消息队列中添加消息,读取消息等
- 消息队列可以按照类型来发送/接受消息
消息队列使用步骤
- 打开/创建消息队列,msgget
- 向消息队列发送消息,msgsnd
- 从消息队列接受消息,msgrcv
- 控制消息队列,msgctl
消息队列创建/打开–msgget
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key,int msgflg);
成功时返回消息队列的id,失败时返回EOF
key和消息队列关联的key IPC_PRIVATE和ftok
msgflg标志位IPC_CREAT|0666
消息发送–msgsnd
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msgid,const void *msgp,size_t size,int msgflg);
成功时返回0,失败时返回-1;
msgid:消息队列id
msgp:消息缓冲区地址
msgp消息结构体指针:struct msgbuf
{
long mtype; //消息类型
char metx; //消息正文
}
size:消息正文长度
msgflg:标志位0或IPC_NOWAIT
消息接受–msgrcv
#include<sys/ipc.h>
#include<sys/msg.h>
int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int msgflg);
成功时返回收到的消息长度,失败时返回-1;
msgid :消息队列id
msgp:消息缓冲区地址
size:指定接收的消息长度
msgtype:指定接收的消息类型
0:接收队列中第一个消息
大于0:接收消息队列中第一个类型为msgtype的消息
小于0: 接受消息队列中第一个类型值不小于msgtype绝对值类型第一个消息
msgflg:标志位 0或IPC_NOWAIT
控制消息队列–msgctl
#include<sys/ipc.h>
#include<sys/msg.h>
int msgctl(int msgid,int cmd,struct msgid_ds *buf);
成功时返回0,失败时返回-1;
msgid:消息队列id
cmd:要执行的操作 IPC_STAT/IPC_SET/IPC_RMID
buf:存放消息队列属性的地址
msgsnd.c
#include<sys/ipc.h>
#include<stdio.h>
#include<sys/msg.h>
#define LEN sizeof(MSG)-sizeof(long)
int main()
{
typedef struct {
long type;
char txt[64];
}MSG;
key_t ipkey;
int msgid;
MSG msg_t;
ipkey=ftok(".",23);
if(ipkey==-1{
printf("ftok");
return -1;
}
msgid=msgget(ipkey,IPC_CREAT|0666);
if(msgid==-1){
perror("msgget");
return -1;
}
msg_t.type=1;
strcpy(msg_t.txt,"msg type one");
msgsnd(msgid,&msg_t ,LEN,0);
msg_t.type=2;
strcpy(msg_t.txt,"msg type two");
msgsnd(msgid,&msg_t ,LEN,0);
msg_t.type=3;
strcpy(msg_t.txt,"msg type three");
msgsnd(msgid,&msg_t ,LEN,0);
msg_t.type=4;
strcpy(msg_t.txt,"msg type four");
msgsnd(msgid,&msg_t ,LEN,0);
msg_t.type=5;
strcpy(msg_t.txt,"msg type five");
msgsnd(msgid,&msg_t ,LEN,0);
}
msgrcv.c
#include<sys/ipc.h>
#include<stdio.h>
#include<sys/msg.h>
#include<stdlib.h>
#define LEN sizeof(MSG)-sizeof(long)
void rmmsg(int sig)
{
msgctl(msgid,IPC_RMID,NULL);
exit(0);
}
int main()
{
typedef struct {
long type;
char txt[64];
}MSG;
key_t ipkey;
int msgid;
MSG msg_t;
ipkey=ftok(".",23);
if(ipkey==-1{
printf("ftok");
return -1;
}
msgid=msgget(ipkey,IPC_CREAT|0666);
if(msgid==-1){
perror("msgget");
return -1;
}
signal(SIGINT,rmmsg);
while(1){
re=msgrcv(msgid,&msg_t,LEN,3,EXCEPT); //只接收三以外的其他消息
printf("receive msg :type=%d,txt=%s\n",msg_t.type,msg_t.txt);
if(re<0){
break;
}
}
}
5>System V IPC-信号灯
信号灯也叫信号量,用于进程/线程同步或互斥的机制
信号灯的类型
- posix无名信号灯
- posix有名信号灯
- systme V 信号灯
信号灯的含义
计数信号灯
System V IPC–信号灯特点:
system v 信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁
System v信号灯使用步骤
打开/创建信号灯 semget
信号灯初始化 semctl
P/V操作 semop
删除信号灯 semctl
信号灯打开/创建--semget
#include<sys/ipc.h>
#include<sys/sem.h>
int semget(key_t key,int nsems,int semflg);
成功时返回信号灯的id,失败时返回-1;
key和信号队列关联key IPC_PRIVATE或ftok
nsems 集合中包含的计数信号灯个数
semflg标志位IPC_CREAT|0666 IPC_EXCL
信号灯初始化--semctl
#include<sys/ipc.h>
#include<sys/sem.h>
int semctl(int semid,int semnum,int cmd,....);
成功时返回0,失败时返回EOF
semid 要操作的信号灯集id
semnum 要操作的集合中的信号灯编号
cmd 执行的操作 SETVAL IPC_RMID
union semun 取决于cmd
信号灯P/V操作--semop
#include<sys/ipc.h>
#include<sys/sem.h>
int semop(int semid,struct sembuf *sops,int nsops);
成功时返回0,失败时返回-1;
semid:要操作的信号灯集id
sops:描述对信号灯操作的结构体(数组)
nsops:要操作的信号灯的个数
信号灯操作--sembuf
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
};
semnum :信号灯编号
sem_op: -1:P操作,1:V操作
sem_flg: 0/IPC_NOWAIT
#include<stdio.h>
#include<stdlib.h>
#include<sys/type.h>
#include<sys/ipc.h>
#include<errno.h>
#include<string.h>
#define SEM_READ 0
#define SEM_WRITE 1
union semun{
int val;
}
poperation(int index,int semid)
{
struct sembuf sop;
sop.sem_num=index;
sop.sem_op=-1;
sop.em_flg=0;
semop(semid,&sop,1);
}
voperation(int index)
{
struct sembuf sop;
sop.sem_num=index;
sop.sem_op=1;
sop.em_flg=0;
semop(semid,&sop,1);
}
int main()
{
int semid;
key_t key;
int shmid;
char *shmaddr;
pid_t pid;
key=ftok(".",123);
//创建信号灯
semid=semget(key,2,IPC_CREAT|0666);
if(semid<0){
perror("semget");
return -1;
}
//创建共享内存
shmid=shmget(key,256,IPC_CREAT|0666);
if(shmid<0){
perror("shmget");
return -1;
}
//初始化2个信号灯
nuion semun myun;
//初始化读信号灯
myun.val=0;
semctl(semid,SEM_READ,SETVAL,myun);
//初始化写信号灯
myun.val=1;
semctl(semid,SEM_WRITE,SETVAL,myun);
pid=fork();
if(pid<0){
perror("fork");
return -1;
}else if(pid==0){
shmaddr=(char *)shmat(shmid,NULL,0);
while(1){
poperation(SEM_READ,semid);
printf("getshm:%s\n",shmaddr);
voperation(SEM_WRITE,semid);
}
}else{
shmaddr=(char *)shmat(shmid,NULL,0);
while(1){
poperation(SEM_WRITE,semid);
printf("please input char to shm:");
fgets(shmaddr,32,stdin);
voperation(SEM_READ,semid);
}
}
}