无名管道由pipe( )
函数创建:int pipe(int fd[2])
;当一个管道建立时它会创建两个文件描述符:fd[0]
用于读管道,fd[1] 用于写管道。
无名管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。
必须在系统调用fork()
前调用pipe()
,否则子进程将不会继承文件描述符。
int main(int argc, char* argv[])
{
int fd,nwrite;
char w_buf[100];
if(argc==1)
{
printf("Please send something\n");
exit(-1);
}
fd= open("myfifo",O_RDWR|O_NONBLOCK);
if(fd==-1)
{
perror("open file error");exit(-1);
}
//用命令行参数作为写入数据
strcpy(w_buf,argv[1]);
nwrite=write(fd,w_buf,sizeof(w_buf));
printf("write %s %d to the FIFO\n",w_buf,nwrite);
close(fd);
}
int main()
{
int fd;
int count = 1;
char cache[80];
fd =open("myfifo",O_RDONLY|O_NONBLOCK);
if(fd == -1)
{
perror("open error"); return -1;
}
while(1){
memset(cache,0, sizeof(cache));
if((read(fd,cache, 100)) == 0 ){
//如果没读到数据
printf("nodata:\n");
}
else
printf("getdata:%s\n", cache);
sleep(1); //休眠1s
}
#include
#include
int mkfifo(const char * pathname, mode_t mode)
若第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了
mkfifo
(管道名, O_CREAT|O_EXCL) O_EXCL
: 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
mode:属性
一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
当打开FIFO时,非阻塞标志(O_NONBLOCK)
将对以后的读写产生如下影响:
1、没有使用O_NONBLOCK
:访问要求无法满足时进程将阻塞。如试图读取空的FIFO,将导致进程阻塞。
2、使用O_NONBLOCK
:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO。
shmXXX
函数族来实现利用共享内存进行存储的
int shmget(key_t key, size_t size, int flag);
获得或得到一个共享存储标识符int shmctl(int shmid, int cmd, struct shmid_ds *buf);
对共享存储段执行多种操作void *shmat(int shm_id, const void *shm_addr, int shmflg);
把共享内存连接到当前进程的地址空间shmdt() : detach
,分离共享内存函数原型
int shmget(key_t key,int size,int shmflg);
参数含义:
系统调用shmget()
中的第一个参数是关键字值。key
标识共享内存的键值: 0/IPC_PRIVATE
。 当key的取值为IPC_PRIVATE
,则函数shmget()
将创建一块新的共享内存;如果key
的取值为0
,而参数shmflg
中又设置IPC_PRIVATE
这个标志,则同样会创建一块新的共享内存。
size
表示待创建的共享内存的大小shmflg
:使用IPC_CREAT
,则新创建一个共享的内存段,可以和IPC_EXCL
一起使用。-1
int shmat(int shmid,char* shmaddr,int shmflg);
int shmdt(char* shmaddr);
int shmctl(int shmqid,int cmd,struct shmid_ds *buf);
int shm_open(const char *name, int oflag, mode_t mode);
创建或打开一个共享内存,成功返回一个整数的文件描述符,错误返回-1。
1.name:共享内存区的名字;
2.oflag标志位;open的标志一样,基本标志
O_RDONLY 1 只读打开
O_WRONLY 2 只写打开
O_RDWR 4 读写打开
附加标志:还可选择以下模式与以上3种基本模式组合,如 O_RDWR|O_CREAT:
O_CREAT 0x0100 创建一个文件并打开 ( 如果指定文件不存在,则创建这个文件)
O_TRUNC 0x0200 若存在文件打开,并将文件长度设置为0,其他属性保持
O_EXCL 0x0400 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
O_APPEND 0x0800 追加打开文件
O_TEXT 0x4000 打开文本文件翻译CR-LF控制字符
O_BINARY 0x8000 打开二进制字符,不作CR-LF翻译
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O 设置为非阻塞模式(nonblocking mode)
3.mode权限位
通常用数字,如#define OPEN_MODE 0777
注意数字前有0
shm_open返回文件描述符
一个整数,标识着一个文件,文件的属性是前面open中标志位所指定的
编译时要加库文件-lrt
shm_open()
:创建共享内存段或连接到现有的已命名内存段。这个系统调用返回一个文件描述符。shm_unlink()
:根据(shm_open()mmap()
:把共享内存段映射到进程的内存。这个系统调用需要shm_open()
mmap()
文档。)munmap()
:作用与 mmap() 相反。msync()
:用来让共享内存段与文件系统同步 — 当把文件映射到内存时,这种技术有用void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
mmap()
系统调用使得进程之间通过映射同一个普通文件实现共享内存open()
返回len
是映射到调用进程地址空间的字节数,它从被映射文件开头offset
个字节开始算起prot
参数指定共享内存的访问权限:PROT_READ
(可读) , PROT_WRITE
(可写), PROT_EXEC
(可执行), PROT_NONE
(不可访问)offset
参数一般设为0
,表示从文件头开始映射flags
通常是:MAP_SHARED
或 MAP_PRIVATE
addr
指定文件应被映射到进程空间的起始地址,一般被指定一个空指针NULL
,此时选择起始地址的任务留给内核来完成mmap()
函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址add_w = mmap(NULL, 1024, PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(add_w, "helloworld", sizeof("helloworld"));
typedef struct{
char name[4];
int age;
}people;
main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
char temp;
fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0 );
close( fd );
temp = 'a';
for(i=0; i<10; i++) {
temp += 1; memcpy( ( *(p_map+i) ).name, &temp,2 ); ( *(p_map+i) ).age = 20+i;
}
printf(" initialize over \n "); sleep(10);
munmap( p_map, sizeof(people)*10 ); printf( "umap ok \n" );
}
typedef struct{
char name[4]; int age;
}people;
main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
fd=open( argv[1],O_CREAT|O_RDWR,00777 );
p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0);
for(i = 0;i<10;i++)
{
printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
}
munmap( p_map,sizeof(people)*10 );
}
当使用完时,原来的进程调用 munmap()
和 shm_unlink()
SIGHUP
:终端上发出的结束信号。
SIGINT
:来自键盘的中断信号(CTRL+C),默认操作是终止当前进程的运行。
SIGQUIT
:来自键盘的退出信号(CTRL +\),默认操作是将当前进程终止。
SIGFPE
:浮点异常信号。
SIGKILL
:该信号结束接收信号的进程。
SIGALRM
:由用户调用alarm( )函数产生,默认操作是将正在执行的进程杀死掉。
SIGTERM
:kill发送出的信号。
SIGCHLD
:标识子进程停止或结束的信号。
SIGSTOP
:键盘(CTRL+Z)或调试程序的停止信号。
kill
函数
进程通过kill函数向包括它本身在内的其他进程发送一个信号(除了root
用户,kill不能向其他用户拥有的进程发送信号)
int kill(pid_t pid, int sig);
把信号sig
发送给进程号为pid
的进程
alarm
函数
unsigned int alarm(unsigned int seconds);
在seconds
秒之后安排发送一个SIGALRM
信号给进程
raise
函数
int raise(int sig);
向进程本身发送一个sig
信号;调用成功返回0
;否则返回-1
。
pause函数
pause 使当前进程暂停,进入睡眠状态。直到当前进程收到“任何信号”,执行相应的响应函数后,pause函数才返回:即继续往下执行
// testPause.c
#include
#include
#include
#include
#include
int main()
{
alarm(1);
pause();
printf("I have been waken up\n");
}
程序本意是想暂停1秒后输出
但执行的结果并不是,原因在于
alarm
的默认处理是结束进程。
修改程序,使得程序能正常运行
tips:使用signal
函数
收到信号后,三种处理方法:
自定义处理函数 如 signal(信号, 函数名)
;
忽略信号
signal(SIGINT, SIG_IGN);
但 SIGKILL
和SIGSTOP
不能忽略
由系统进行默认处理
signal( SIGINT, SIG_DFL);
void ouch(int sig)
{
printf("\nOUCH! - I got signal %d\n", sig);
//恢复终端中断信号SIGINT的默认行为
signal(SIGINT, SIG_DFL);
}
int main()
{
//改变终端中断信号SIGINT的默认行为,使之执行ouch函数
//而不是终止程序的执行
signal(SIGINT, ouch);
while(1)
{
printf("Hello World!\n");
sleep(1);
}
return 0;
第一次按下终止命令(ctrl+c)
时,进程并没有被终止,面是输出OUCH! - I got signal 2
,因为SIGINT
的默认行为被signal
函数改变了,当进程接受到信号SIGINT
时,它就去调用函数ouch
去处理,注意ouch
函数把信号SIGINT
的处理方式改变成默认的方式,所以当再按一次ctrl+c
时,进程就像之前那样被终止了。