您的位置:首页 > 其它

标准C的IO

2015-12-01 16:51 218 查看
Char* fgets(char*s,int size,FILE* stream);

Int printf(constchar* format,...);

Int fprintf(FILE*stream, const char* format,...);

Size_t fread(void*ptr,size_t size,size_t nmemb,FILE* stream );

Size_tfwrite(const void* ptr,size_t size,size_t nmemb,FILE* stream );

Typedeg structiobuf

{

char* ptr; //下一字符的位置

. . .

}

全缓存:

要求填满整个缓冲区才进行I/O调用。一般用于磁盘文件

Fwrite(“asdf”,1,4,pfile);

行缓存 printf();

涉及到一个终端时(例如标准输入输出)。

碰到换行符自动输出

行缓存满自动输出

无缓存:

标准错误流stderr不带缓存区,是错误尽快现实出来

STDIN_FILENO(0) STDOUT_FILENO(1) STDERR_FILENO(2)

文件描述符数量:0---OPRN_MAX

FILE* dopen(intfd,const char* mode); 文件描述符à 文件指针

Int fileno(FILE*stream); 文件指针->文件描述符

Open函数 打开或创建文件 失败返回-1,成功>0

#include<sysy/type.h> //类型

#include<sysy/stath> //状态

#include<fcntl.h> //控制

Int open(constchar* pathname,int flags);

Int open(constchar* pathname,int flags,mode_t mode); //兼容creat函数

Flag: O_RDONLY(只读)O_WRONLY(只写) 0_RDWR(读写)O_APPEND(追加模式,每次写在文件的尾端)

O_CREAT (如果文件不存在,按照mode指定的权限创建)

O_EXCL 测试文件是否存在

O_DIRECTORY 测试pathname是不是目录

O_TRUNC 如果文件存在,以只读或只写方式打开,并清零

O_NONBLOCK 设置本次I/O操作非阻塞

Mode:新建文件的访问权限,对于open函数而言,仅当创建文件时使用第三个参数

#include<sysy/type.h> //类型

#include<sysy/stath> //状态

#include<fcntl.h> //控制

Int creat(constchar* pathname,mode_t mode);//以只写创建文件 等价于open(pathname,O_WRONLY|O_CREAT|O_TRUNC,mode)

#include<unistd.h>

Int close(int fd);//关闭一个文件

当进程结束时,内核关闭所有文件

#include<unistd.h>

Size_t read(intfd,void* buff,size_t count);

一下情况返回值小于count

读普通文件,剩下的字节数不够

从终端设备读取数据时,一次最多读取一行

当从网络读取时,网络的缓冲机构可能会产生这种情况

面向记录的设备,一次最多返回一个记录,如磁带

进程间信号中断

#include<unistd.h>

Size_t write(intfd,const void* buff,size_t count);

返回值小于count的情况

磁盘一些满或者超过文件最大长度限制

Perror(“”);

Gcc –o obj/io.o –csrc/io.c –Include –Wall

#include<sysy/type.h> //类型

#include<unistd.h>

Off_t lseek(intfd,off_t offset,int whence)

Whence:定位位置

SEEK_SET:文件头 SEEK_CUR:当前位置 SEEK_END:文件尾

Lseek也可用来检测文件是否可以设置偏移量。如果文件描述符引用的是管道或匿名管道,lseek返回-1,并将error设置为EPIPE

Int ilen =Lsseek(fd,0,SEEK_END); 返回文件的长度

Int dup(ingoldfd);

Int dup2(ingoldfd,int newfd); newfd中原来的文件描述符被覆盖,如果newfd已经打开,就先自动关闭,再复制。

两个函数的功能:复制文件描述符,返回新的文件描述符

作用:进程间通信时改变进程中标准输入和标准输出设备STDIN_FILENO STDOUT_FILENO

cat cat 文件名 cat> 文件名

mycp

本课总结:

标准c的IO缓存类型

文件描述符

文件操作方式

文件IO系统调用 open close creat read write iseek

文件操作的内核数据结构

Dup和dup2函数

fcntl函数

Intfcntl(int fd,int cmd)

fflush

intflag = fcntl(STDIN_FILEIN,F_GETFL);

flag|=O_NONBLOCK;

fcntl(STDIN_FILENO,F_SETFL,flag);

文件和目录:

文件属性:stat结构体定义于:/usr/include/sys/stat.h

操作函数:

intstat(const char* path, struct stat *buf);

intfstat(int fd, struct stat *buf);

intlstat(const char* path, struct stat *buf);

1和2一样

1和3的却别:path是获取链接时,2获取链接指向的文件的属相,3获取链接本身的属性 ln –s(创建链接)

例子:

structstat buf={0};

stat(path,&buf); 或 l stat(path,&buf);

if(S_ISREG(buf.st_mode))

{ 普通文件}

if(S_ISDIR(buf.st_mode))

{ 目录文件}

if(S_ISLNK(buf.st_mode))

{ 链接文件}

2.文件权限

判断文件属性:

access(path,R_OK)

R_OK 可读

W_OK 可写

F_OK存在

X_OK

屏蔽文件权限

#include<sys/type.h>

#include<sys/stat.h>

Mode_tumask(mode_t mode)

功能:为进程设置文件方式创建屏蔽字,并返回以前的值

Man2 creat 查看

//创建文件 Int creat(constchar* path,mode_t mode); mode是权限

然后屏蔽权限

例子:

Umask(0);//都不屏蔽

Creat(argv[1],0777);//创建的文件有所有权限

Umask(0777);//屏蔽所有权限

Creat(argv[2],0777);//创建没有任何权限的文件

修改文件权限

Chmod(argv[i],权限)//修改权限

缩短文件

Inttruncate(const char* path,off_t length);

Intf truncate(int fd,off_t length);

//文件尾端截取一些数据,以缩短文件

给文件创建链接

IntLink(const char* exitfile,const char* newfile)

删除链接

Intunlink(const char* path)

解除链接

remove 对于目录 和rmdir一样 对于文件 和unlink一样

更换文件名

rename

symlink创建软连接

readlink//得到链接的指向的原文件

文件时间

Structutimbuf //时间的结构体

Utime(constchar* path,const struct utimbuf *buf) 更改文件的时间

命令 : Touch 文件名//恢复文件的时间

操作目录:

mkdir(constchar* path,mode_t mode);//创建目录

Rmdir(constchar* path);//删除目录(里面不能有文件且无其他进程打开此目录)

Opendir打开目录

readdir读取目录

rewinddir重新定位目录

intchdir(const char* path);指定新的当前目录

intfchdir(int fd); 指定新的当前目录

成功返回0,失败返回1

Char*getcwd(char* buf,size_t size);获取当前工作目录的绝对路径 成功返回buf,失败返回NULL 命令pwd

closedir关闭目录

第三章 进程

命令 ps 查看进程ID 号

Ps–u 查看进程状态

Ps–x

正常终止

1. 从main返回

2. 调用exit(标准c函数)

3. 调用_exit或_Exit(系统调用)

4. Int atexit(fun);2和3调用4实现,可以登记进程终止时收尾工作的函数,可以多次登记

5. 最后一个线程调用pthread_exit

异常终止

1. 调用abort

2. 接收信号终止

3. 最后一个线程对取消请求做处理响应

进程返回

1. 通常程序运行成功返回0,失败返回非0

2. 在shell中可以查看进程返回值(echo $?)

环境变量操作函数

char*getenv(const char* name);获取环境变量值

intputenv(cahr* str);形式为name=value的字符串,将其放到环境表中,如果name已经存在,则先删除其原来的定义

intsetenv(name,value,rewrite);略

intunsetenv(name);删除name的定义

setjmp 功能:设置非局部跳转的跳转点

longjmp功能:进行非局部跳转

C程序缺乏异常处理的语法,可以用非局部跳转处理C的程序异常

一.I/O处理的五种模型:

1.阻塞

非阻塞

I/O多路转换

信号驱动

异步

进程
命令:
Ps –aux 查看所有进程
USER 进程属主
PID进程ID
PPID 父进程
%CPU 进程占的CPU百分比
%MEM 占用内存百分比
NI 进程的NICE值,数值大表示较少占用CPU时间
VSZ 进程虚拟大小
RSS 驻留中页的数量
TTY 终端ID
WCHAN 正在等待的进程资源
START 启动进程的时间
TIME 进程小号的CPU时间
COMMAND 命令的名称和参数
进程常见状态:
运行状态 stat = R
等待状态 stat = S
停止状态 stat = T
僵尸状态 Z
Ctrl+z 暂停进程 fg 恢复暂停的进程
Pid_tgetpid(void) 获得当前进程ID

Pid_tgetuid(void) 获得当前进程的实际用户ID

uid_tgeteuid(void) 获得当前进程的有效用户ID

Pid_tgetgid(void) 获得当前进程的用户组ID

Pid_tgetppid(void) 获得当前进程的父进程ID

Pid_tgetpid(void) 获得当前进程ID

Pid_tgetpgrp(void) 获得当前进程所在进程组ID

Pid_tgetpgid(void) 获得进程ID为pid的进程所在的进程组ID

Pstree命令

Pid fork(void) 创建进程

Pid vfork(coid) 同上

#define typedef_STD_TPYE

孤儿进程:父进程结束 子进程没结束,子进程由1号进程结束

僵尸进程:子进程结束但没完全释放内存(内存中的task_struct没有释放)。

僵尸进程的父进程结束后就会被Init进程领养,最终被回收

避免僵尸进程:wait(int*status);

Waitpid(pid_t pid,int*status,int opration);

Exec族函数:通过exec函数执行其他的程序,exec之后exec打开的进程覆盖当前程中进程

Int execl(constchar* path,const char* arg0,....);

Int execv(constchar* path,const char* argv[ ],....);

Int execle(constchar* path,const char* arg0,....);

Int execve(constchar* path,const char* argv[],const char* envp[]);

Int execl(constchar* path,const char* arg0,....);

Int execvp(constchar* path,const char* argv[],....);

Int main(void)

{

Pid_t pid=0;

Pid=fork();

If(pid>0)

{

Exitlp(“\bin\ls”,”mingling”,NULL); //如果有这句 以后的都不会执行

Wait(NULL);

Printf(“创建子进程成功”);

Printf(“子进程ID=%d,她的父进程ID=%d\n”, getpid(),getppid());

}

Else if(pid == 0)

{

Printf(“这里是父进程\n”);

}

Else

{

Printf(“创建子进程失败”);

}

}

System(const char*comment);创建一个子进程并在子进程中执行exec

等同于/bin/bash/ -c “cmd”或exec(“bash”,”-c”,”cmd”);

Cat命令

信号

1.基本概念;进程间通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程发生变化

Kill –l 查看linux系统的信号,共64个。1---31最重要 unix的信号 没有优先级, 非实时 不支持排队

34----64 linux自加的信号 实时的信号 支持排队

Cd /user/include

Find –name signum.h

Cd ./i386-linux-gnu/bits/signum.h

Vi signum.h 通过这些命令可以找到前31个信号

Kill -9 信号编码 关掉一个信号

.信号软件中断

.信号异步事件

不可预见

信号有自己的名称和编码

.信号发送的来源

硬件来源:键盘或其他硬件,信号是由硬件驱动产生的

软件来源:函数kill()相当于命令kill -9, raise()自己发给自己, alarm(), setitimer()定时器

2.信号处理

A.忽略信号:

SIGKILL和SIGSTOP永远不能忽略。

忽略硬件异常

进程启动时SIGUSR1和SIGUER2两个信号被忽略

B.执行默认操作

每个信号都有默认操作,大部分信号默认动作是终止进程。SIGUSR1和SIGUER2没有默认处理

C.捕获信号

告诉内核出现信号时调用自己的处理函数

SIGKILL和SIGSTOP不能被捕获

3.Signal函数

Void( *signal( int signo, void(*func)(int)
)) (int);

参数: signo 要登记的信号值

Func 信号处理函数指针

SIG_IG 忽略信号

SIG_DFL 采用系统默认的方式处理信号,执行默认操作

例子:

Void sig_handler(inxint signo)

{

Printf(“%d,%doccuren\n”,getpid(),signo);

}

Int main(int argc,char* argv[])

{

//捕获ctrl+c

If( SIG_ERR == signal(SIGINT,sig_ handler))//向内核登记信号处理函数以及信号值

{ perror(“登记失败\n”);}

If( SIG_ERR == signal(SIGKILL,sig_ handler)) //捕获KILL -9信号

{ perror(“登记失败\n”);}

If( SIG_ERR == signal(SIGSTOP,sig_ handler)) //捕获STOP信号

{ perror(“登记失败\n”);}

If( SIG_ERR == signal(SIGUSR1,sig_ handler)) //捕获用户注册信号

{ perror(“登记失败\n”);}

While(1);

}

4. SIGCHLD信号:子进程状态发生变化(子进程结束),父进程需要调用wait来等待子进程结束并回收它,避免僵尸进程

Void sig_handler(int signo) //当父进程捕获到signo信号后要调用

{

Printf(“%d,%doccuren\n”,getpid(),signo);

Wait(null);// wait回收子进程,否则子进程会成为僵尸进程

}

Void out(int n)

{

Int i=0;

For(;i<n;i++)

{

printf(“%d,OUT %d\n”,getpid(),i); //获得当前进程ID

sleep(2);

}

}

Int main(int argc,char* argv[])

{

If( SIG_ERR == signal(SIGCHLD,sig_ handler) )//向内核登记信号处理函数以及信号值SIGHLID(子进程结束信号)

{ perror(“登记失败\n”);}

Pid_t pid = fiork();

If(pid<0)

{

perror(“ fork error”);

exit(1);

}

Else if(pid>0) //父进程

{ Out(100); }

Else //子进程

{ out(10); }

Return 0;

}

5. 信号发送 kill raise

除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号

一般的进程只能向具有相同uid和gid的进程发信号,或者向相同进程组中的其他进程发信号

常用的发信号函数有kill(), raise(), setitmer(), abort()

Int kill(pid_t pid, int signo); 0为空信号,用来检测特定进程是否存在

成功返回0,失败返回1. 向指定进程发出一个信号

Int raise(int signo)

成功返回0,失败返回1. 向进程本身发出一个信号,相当于kill(getpid(),sig);

Pid>0 将信号发给进程ID为pid的进程

Pid=0 将信号发给于发送进程统一进程组的所有进程

Pid<0 将信号发给进程组ID等于pid的绝对值

Pid==-1 将信号发给发送进程有权限向他们发送信号的所有进程

定时器

6.定时器 Unsignedint alarm(Unsigned int seconds);

设置定时器,当超时,产生SIGALRM信号

信号由内核产生,在指定的seconds秒后,给进程本身发一个SIGALM信号

参数为0,取消以前设置的定时器

Void sig_handler(int signo) //当父进程捕获到signo信号后要调用

{

If(signo == SIGALRM)

{

Printf(“CLOSCTIME OUT\n”);

Alarm(5);

}

}

Void Out_data()

{

Int i=1;

While(i<=20)

{ double d = drand48();

Printf(“%d-10d:%1f\n”,i++,d);

If(i==16)

{

Alarm(0); //取消之前定义的定时器

}

Sleep(1);

}

}

Int main(int argc,char* argv[])

{

If( SIG_ERR == signal(SIGALRM,sig_ handler) )//向内核登记信号处理函数以及信号值SIGALRM (超时信号)

{ perror(“登记失败\n”);}

Alarm(5);//设置定时器

Printf(“开始运行main函数”);

Out_data();

Printf(“停止运行main”);

Return 0;

}

函数的可重入性

Int a;

Void handle(int signo) //a的值受外界影响,不定,就是不可重入函数

{ printf(“a=%d\n”,a);}

Int main()

{

Signal(SIGINT,handle);

While(1)

{

a=3;

a=4;

a=5;

a=6;

}

}

信号集函数:信号集是一个或多个信号的几何,信号集函数用来屏蔽信号

Int sigemptyset(sigset_t *set) 将信号集清空,将所有信号屏蔽字置0

Int sigfillset(sigset_t *set) 将所有信号加入到信号集中,所有信号屏蔽字置1

Int sigaddset(sigset_t *set) 将某个信号加入到信号集中,所有信号屏蔽字置1

Int sigdelset(sigset_t *set) 将某个信号从信号集中中删除,对应信号屏蔽字置0

以上函数成功返回0,出错返回-1

Int sigismember(const siget_t *set,intsigno);测试信号集中是否包含某个信号,判断信号屏蔽字某位是否置1

真返回1,假返回0,出错返回-1

信号未决:信号从产生道递达之间的状态称为信号未决(pending)执行信号的处理动作称为信号递达。

进程可以选择阻塞(block)某个信号,被阻塞的信号产生时就会保持在未决状态,直到解除阻塞才能执行递达

SIGHUP 0

SIGINT 1

SIGQUIT 1

1

0

Task_struct block peding handler

产生 阻塞 未决 递达

Viod handle(int signo)

{

Switch(signo)

{

Case SIGINT:

Printf(“a=%d\n”,a); break;

Case SIGQUIT:

Sigset_t bset;

Sigemptyset(&bset); // 清空信号集

Sigaddset(&bset,SIGINT);

Sigprocmask(SIG_UNBLOCK,&bset,NULL);

Break;

Defult:

Break;

}

}

Void printfsigset(sigset_t* pset)

{

Int i=0;

For(;i<NSIG;i++)

{

If( sigismember(pset,i) == 1 )

Putchar(“1”);

Else if(sigismember(pset,i) == 0 )

Putchar(“0”);

Else

Perror(“”);

}

Printf(“\n”);

}

Int main()

{

Signal(SIGINT,handl);

Signal(SIGQUIT,handl);

Sigset_t bset; //用来设置信号集

Sigset_t bset2;//存放信号未决的信号集

Sigemptyset(&bset);// 清空信号集

Sigaddset(&bset,SIGINT);把SIGINT信号添加到信号集中

//SIG_BLOCK SIG_UNBLOCK SIG_SETMASK

Sigprocmask(SIG_BLOCK,&bset,NULL);//第三个参数是就得block集

While(1)

{

Sigpending(&bset2);//查看

printfsigset(&bset2);

sleep(1);

}

Return 0;

}

作业 完成mshell项目

进程是资源管理的做小单位,线程是程序执行的最小单位

每个进程有自己的数据段,代码段,堆栈段,线程包含独立的栈和cpu寄存状态。

创建线程:

Int pthread_creat(pthread_t *restricttidp,const pthread_attr_t *restrictattr, void*(start_rtn)(void*), void*restrictarg );

Void* pthread_mian(void* arg)

{

Struct teacher *tea = arg;

Whiel(1)

{

Printf(“age=%d,sex=%c\n”, tea->age,tea->sex);

Sleep(2);

}

Pthread_exit((void*)100);

Return (void*)100;

}

Struct teacher

{

Int age;

Char sex;

}

Int main()

{

Struct teacher tea;

T ea.age=40;

Tea.sex=’M’;

Pthread_attr_t attr;//线程属性

Pthread_attr_int(&attr); //初始化结构体

pthread_attr_setdetachstat(&attr,PTHREAD_CREATE_DETACHED);

Pthread_t pthreadid=0;

pthread_creat(&pthreadid, &attr, pthread_mian,&tea);

//while(1)

//{sleep(2);}

For(int i=0;i<10;i++)

{ sleep(2); }

Printf(“主线程ID=%d,子线程ID=%d”,pthread_self(),pthreadid);

Pthread_cancel( pthreadid );

Printf(“主线程ID=%d,子线程ID=%d”,pthread_self(),pthreadid);

Sleep(5);// Pthread_jion会阻塞等待子线程退出,不需要sleep

Int res=0;

//Pthread_jion( pthreadid,(void**)&res);//取退出的线程的返回值,并清理子线程的资源 上面设置里分离属性

Printf(“%d\n”,res);

Pthread_attr_destroy(&attr);//线程属性注销

return 0;

}

编译:gcc .... –lpthread

Gcc ......–std=c99(int定义到for循环里)

线程退出

Intpthread_exit(void* retval); 自己关闭自己

Intphread_cancel(pthread_t id); 被统一进程中的其他线程取消

Int pthread_jion(pthread_th th, void**thread_return); 取退出的线程的返回值,并清理子线程的资源

设置和获取分离属性

结合适创建的线程,线程结束时需要用pthread_jion来回收子线程的资源

如果是分离式创建线程,不需要调用pthread_jion来回收子线程的资源,子线程会自己去回收资源,但是无法获得线程结束的返回值

Intpthread_attr_getdetachstat(const pthread_attr_t* restrict attr, int*detachstate);

Intpthread_attr_setdetachstat(const pthread_attr_t* attr, int detachstate);

线程属性初始化和注销

Intpthread_attr_init(pthread_attr_t* attr);

Intpthread_attr_destory(pthread_addr_t* attr);

成功返回0,否则返回错误编码

阶段回顾:

线程的概念

Linux中线程的实现

线程创建和终止

线程清理和控制函数

线程的状态

线程属性初始化

分离属性

管道:

内容概述:

进程间通信概述

管道通信

消息队列

共享内存

信号量

进程间通信概述:

数据传输、

共享数据、

通知事件、

资源共享(锁和同步机制)、

进程控制

进程间通信方式:

1.管道(pipe)和命名管道(FIFO)

2.信号(signal)

3.消息队列

4.共享内存

5.信号量

6.套接字(socket)

1.管道

1.1匿名管道:

在关系进程中才能用

由pipe系统调用,由父进程创建

位于内核空间,其实是一块缓存

1.2命名管道(FIFO)

两个没有任何关系的进程间通信

本质是内核中的一块缓存,在文件系统中以一个特殊的设备文件(管道文件)存在

通过系统调用mkfifo创建

管道创建: int pipe(int fd[2]); fd[0]读 fd[1]写

例1 Int main()

{

Int fd[2];

If( pipi(fd)<0 )

{

Perror(“ creat error\n”); return-1;

}

Else

{

Printf(“succes\n”);

}

Close(fd[0]);

Clsoe(fd[1]);

Return 0;

}

例2 父进程通过管道传输2个数据给子进程,子进程从管道中读取数据

首先创建一个管道,再通过fork创建一个子进程

Int main(int argc,char* argv[] )

{

Int fd[2]; //0读1写

If( pipi(fd)<0 ) //成功返回, 失败返回-1

{

Perror(“ creat error\n”); return-1;

}

Else

{

Pid_tpid;

If( (Pid = fork()) <0 )

{ perror(“创建进程失败”); exit(1); }

Elseif(pid>0) //父进程

{

Close( fd[0] );//关闭读

Int start = 1;

Int end = 100;

If( write(fd[1],&start, sizeof(int)) != sizeof(int) )

{ perror(“写入失败\n”); exit(1); }

If( write(fd[1], &stend, sizeof(int)) !=sizeof(int) )

{ perror(“写入失败\n”); exit(1); }

Close(fd[1]);

Wait(0); //回收子进程,防止产生孤儿进程

}

Else if(pid == 0)//子进程

{

Close( fd[1] );//关闭写

Int start = 0;

Int end = 0;

If( read(fd[0], &start, sizeof(int)) <0 ) //阻塞

{ perror(“读取失败\n”); exit(1); }

If( read(fd[0], &stend,sizeof(int)) <0 ) //阻塞

{ perror(“读取失败\n”); exit(1); }

Close(fd[0]);

Printf(“子进程读的start=%d, end=%d”, start,end);

}

}

Close(fd[0]);

Clsoe(fd[1]);

Return 0;

}

通过打开两个管道来创建一个双向管道

管道默认是阻塞的,当读取时,若没有数据,进程会阻塞

读端不读,写端一直写,管道放满数据就会报错

不完整管道:当读一个写端已关闭的管道时,在所有数据被读完后,read返回0,表示到达文件尾部。 当写一个读端已被关闭的管道,则产生信号SIGPIPE,如果忽略该信号,或者捕捉该信号并从处理程序返回,则write返回-1,同时error设置为EPIPE.

//不完整管道: 读取一个写端已被关闭的管道

Int main(int argc,char* argv[])

{

Int fd[2];

If( pipe(fd) <0 )

{ perror(“创建管道失败\n”); exit(1);}

Pie_t pid;

If( (pid = fork()) < 0 )

{ perror(“创建子线程失败\n”); exit(1);}

else if(pid>0 )//父

{

Sleep(5);

Close(fd[1]);

While(1)

{

Charc;

If(read(fd[0],&c,1) == 0)

{ printf(“写端已经关闭\n”) break;}

Else

{ printf(“%c\n”,c); }

}

Close(fd[0]);

Wait(0);

}

Else //子进程

{

Clsose(fd[0]);

Char* s=”1234”;

Write(fd[1],s,strlen(s));

Close(fd[1]);

}

Return 0;

}

//不完整管道: 写入一个读端已被关闭的管道

Void sig_handler(int signo)

{

If( signo == SIGPIPE)

{ printf(“SIGPIPE发生了 \N”); }

}

Int main(int argc,char* argv[])

{

Int fd[2];

If( pipe(fd) <0 )

{ perror(“创建管道失败\n”); exit(1);}

Pie_t pid;

If( (pid = fork()) < 0 )

{ perror(“创建子线程失败\n”); exit(1);}

else if(pid>0 )//父

{

Sleep(5);

Close(fd[0]);

If(signal(SIGPIPE, sig_handler)==SIG_ERR )

{ perror(“signal sigpipe error\n”) ; exit(1); }

Char*s=”1234”;

If(write(fd[1],s,strlen(s))!= strlen(s) )

{

Fprintf(stderr,“%s%s\n”, strerror(errno),(errno==EPIPE)?”EPIPE”:”UNKOWN”);

}

Close( fd[1] );

Wait(0);

Else

{ close(fd[0]); close(fd[1]); }

Return 0;

}

标准库中管道操作

FILE* popen(const char* cmdstring, const char*type);

成功返回文件指针,出错返回NULL.

Int pclose(FILE*fp);返回值cmdstring的终止状态,出错返回-1

Int main(int argc,char* agrv[])

{

FILE* fp;

Fp = popen(“cat /etc/passwd”,”r”);

Char buff[512];

Memset(buff,0,sizeof(buff));

While(fgets(buff,sizeof(buff),fp) != NULL )

{

Printf(“%s\n”,buff);

}

Pclose(fp);

Printf(“------------------\n”);

Fp = fopen(“wc -l”,”w”);

Fprintf(fp,”1\n2\n3\n”);

Pclose(fp);

Return 0;

}

命名管道(FIFO)

只要对FIFO有适当访问权限,FIFO可用在两个没有关系的进程间通信

本质是内核中的一块缓存,在文件系统中以一个特殊的设备文件(管道文件)存在

在文件系统中只有一个索引快存放文件爱你的路径,没有数据块,所有的数据存放在内核中

命名管道必须读和写同时打开,否则会阻塞

命令mkfifo创建命名管道(命令内部调用mkfifo函数)

对FIFO的操作与操作普通函数一样

用mkfifo创建一个FIFO后,一般的文件I/O函数都可以用于FIFO. Open close read write unlink.....

FIFO出错的信息

EACCES(无存取权限)

.....

........

.....

有名管道的操作

读取

Int main(int argc,char* agrv[])

{

If(argc<2)

{ perror(“参数错误\n”); exit(1);}

Printf(“openfifo for read\n”);

Int fd = open( argv[1],O_RDONLY); //阻塞

或 intfd = open(argv[1],O_RDONLY | O_NONBLOCK);//不阻塞

If(fd<0)

{ perror(“打开错误\n”); exit(1); }

Else

{ perror(“打开成功:%d\n”,fd ); }

Char buff[512];

Memset(buff,0,sizeof(buff));

While(read(fd,buff,sizeof(buff)) < 0 )

{

perror(“打读取失败\n”);

}

Printf(“%s\n”,buff);

close(fd);

return 0;

}

写入

Int main(int argc,char* agrv[])

{

If(argc<2)

{ perror(“参数错误\n”); exit(1);}

Printf(“openfifo for write\n”);

Int fd = open( argv[1],O_WRONLY);

If(fd<0)

{ perror(“打开错误\n”); exit(1); }

Else

{ perror(“打开成功:%d\n”,fd ); }

Char* s = “1234567890”;

Int isize = strlen(s);

If(write(fd,s,isize)!=isize )

{ perror(“write error\n”);}

close(fd);

return 0;

}

匿名管道和命名管道读写相同点

默认都是阻塞

都是用于socket通信

阻塞不完整管道(有一端关闭):当读一个写端已关闭的管道时,在所有数据被读完后,read返回0,表示到达文件尾部。 当写一个读端已被关闭的管道,则产生信号SIGPIPE,如果忽略该信号,或者捕捉该信号并从处理程序返回,则write返回-1,同时error设置为EPIPE.

阻塞完整通道(两端口开启)

单纯读时,要么阻塞,要么读到数据

单纯写时,写到管道满报错

非阻塞不完整管道(有一端关闭):

单纯读时直接报错

单纯写时,产生SIGPIPE,如果忽略该信号或捕捉该信号并从处理程序返回,则write返回-1,同时errno设置为EPIPE

非阻塞完整通道(两端口开启)

单纯读时直接报错

单纯写时,管道满是出错

打开方式不一致

Pipe通过fcntl系统调用来设置O_NONBLOCK来设置非阻塞读写

FIFO通过fcntl系统调用或者open函数来设置非阻塞读写

阶段回顾

进程间通信概述 :管道信号system

管道通信

管道分类:匿名 有名

匿名(pipe)管道特点,创建于读写

管道读写特性

标准库中的管道操作

命名管道FIFO的特点和创建

匿名管道和命名管道的读写区别
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: