文件+通信

IO 文件

IO操作:往硬盘上写内容
当前目录:源文件所在目录、exe所在目录
进程读磁区结束之后是阻塞状态(等待资源),当一个进程等待资源到位之后,修改进程的状态为就绪状态,等待被操作系统调度获取CPU,进行计算.
在默认的情况下,文件描述符都是从3开始的,但是一旦0,1,2其中一个被关闭,那么此时新打开的文件的文件描述符就会是0,1,2中被关闭的哪一个,其中关闭1(stdout)的时候,会发生重定向。

int fd = open ("log.txt",O_CREAT|O_TRUNC| O_WRONLY,0644);//原文件的内容清空,再输出
int fd = open ("log.txt",O_CREAT| O_WRONLY,0644);//原文件的内容隐式清空,再输出
int fd = open ("log.txt" ,0_CREAT|O_APPEND |O_WRONLY,0644);//追加重定向

write():ssize_t write (int fd, const void * buf, size_t count); 参数buf所指的内存写入(期望写入个数)count个字节到参数fd所指的文件内。返回值:如果顺利write()会返回实际写入的字节数(len)。当有错误发生时则返回-1,错误代码存入errno中

软硬链接

如果项目mytest比较深的话,把可执行程序直接的放在bin目录,不用深入目录寻找,直接的./start.link就可以运行。

[s@VM-8-3-centos lesson_17]$ mkdir bin
[s@VM-8-3-centos lesson_17]$ ll
total 28
drwxrwXr-x 2 s s 4096 Apr 2 14:59 bin
- rw-r- - r-- 1 s s 0 Apr 2 14:56 bitel
- rwxrwXrwx 1 s s 7 Apr 2 14:50 link -> log.txt
- rw-r--r-- 1 s s 69 Apr 2 14:49 log.txt
- rw-rw-r-- 1 s s 64 Mar 31 16:56 Makefile
--rwxrwxr-x 1 s s 8712 Apr 2 14:59 mytest
--rw-rw-r-- 1 s s 511 Apr 2 14:52 mytest.c
[s@VM-8-3-centos lesson_17]$ mv mytest bin/
[s@VM-8-3-centos lesson_17]$ ll
total 16
drwxrwxr-x 2 s s 4096 Apr 2 15:00 bin
-rw-r- -r-- 1 s s 0 Apr 2 14:56 bitel
rwxrwxrwx 1 s s 7 Apr 2 14:50 link-> log.txt
- rw-r-- r-- 1 s s 69 Apr 2 14:49 log.txt
- rw-rw-r-- 1 s s 64 Mar 31 16:56 Makefile
- rw-rw-r-- 1 s s 511 Apr 2 14:52 mytest.c
[s@VM-8-3-centos lesson_17]$ ln -s ./bin/mytest start.link #创建软连接
[s@VM-8-3-centos lesson_17]$ ./start.link #直接启动软连接,相当于执行可执行程序
l like

动静态库

动态库:文件中的程序需要所依赖库,需要的库加载到物理内存,然后通过页表和虚拟地址空间建立映射关系,代码执行到需要库来实现的时候,从虚拟地址空间的代码区到共享区去找,进程多的时候,有很多进程都需要用到同一个库,可以通过页表,都映射到虚拟地址空间的共享区去找到这个库,可以只有一份。
静态库:需要依赖库实现的代码直接拷贝放在代码区,代码区会很大。对应的代码拷贝进bin,bin可移植性强,链接的时候纳入进来。本质:.o文件都进行打包,改名为.a
Makefile:gcc -o $@ $^ -static,
静态库生成的可执行程序文件体积大(占用硬盘的资源),程序中有多条printf,会出现多份从C库中拷贝过来的printf的实现方法(占用内存的资源),
链按的本质:链接o文件、lib文件
本质:把你的代码生成的. o进行打包
制作的库目的:直接的使用,包含:一批头文件:方法,接口参数的意思;一个或多个库文件:具体实现,供动静态链接
目录文件 的内容:文件名和inode的映射关系

[s@VM-0-3-centos ]$ ldd mytest
linux-vdso.so.1 =>(Ox00007ffe1cd1d000)
libc.so.6 =>/lib64/libc.so.6 (Ox00007f06ab2dd000)
#libname.so.version

静态库:

[whb@vM-0-3-centos]$ gcc -c myadd .c
[whb@VM-0- 3-centos]$ gcc -c mysub.c #产生myadd.o、mysub.o
[whb@VM-0-3-centos]$ ar -rc libmymath.a myadd.o mysub.o #产生libmymath.a静态库
[whbavM-0-3-centos]$ tree mylib
mylib
    include #放myadd、mysub的头文件
      myadd .h
      mysub.h
    lib #放的库
      libmymath . a
[whb@VM-0-3-centos]$ ll
total 12
- rw- r--r-- 1 whb whb87 Mar 28 15:46 Makefile
drwx rwxr-x 4 whb whb 4096 Mar 28 15:56 lib
-- rw - rw- r-- 1 whb whb113 Mar 28 15:46 mytest.c#在mytest.c中使用自己写的lib库

Makefile:产生可执行程序

mytest : mytest.c
	gcc -o s@ s^ -I ./mylib/include -L ./lib/lib -l mymath -static #告诉编译器这些头文件……的路径
.PHONY: clean
clean :
	rm -f mytest

动态库:
Makefile:

libmymath.so:myadd.o mysub.o
	gcc -shared -o $@ $^
myadd.o : myadd.c
	gcc -fPIC -c $<
mysub.o : mysub.c
	gcc -fPIC -c $<
.PHONY :clean
clean:
	rm -rf output *.o libmymath.so
.PHONY :output #发布
output :
	mkdir -p mylib/include
	mkdir -p mylib/lib
	cp *.h mylib/include
	cp *.so mylib/lib
[whb@VM-0- 3-centos 15_lesson]$ pwd
/home/whb/72_class/15_lesson
[whb@VM-0-3-centos 15_lesson]$ export LD_LIBRARY_PATH=/home/whb/72_class/15_lesson/mylib/lib
path=$(shell pwd)
mytest : mytest.c
	gcc -o $@ $^ -I $(path)/mylib/include -L $(path)/mylib/lib -lmymath #告诉编译器、操作系统这些头文件……的路径
.PHONY :clean
clean :
	rm -f mytest

进程间通信

1.匿名管道

匿名管道在内核创建出来的缓冲区是没有标识符的,导致了其他进程没有办法直接找到这个缓冲区。但是创建的进程可以通过读写两端的文件描述符进行操作.
管道的大小为64k

套接字:使用最频繁的进程间通信方式,就是网络通信,需要跨网络,虽然可以持续的传递,但是在速度上还是没有共享内存快捷
管道:速度慢,容量有限
消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
共享内存:能够很容易控制容量,速度快

两个进程之间通信,就是想尽一切办法,两个进程能够看到同一份资源(内存)。
内存的提供是通过文件的方式: 管道;如果绕过文件,使用操作系统提供的和通信相关的数据结构:system V
情况1:

  #include<stdio.h>
   #include<unistd.h>
   #include<string.h>
   
   int main()
   {     int pipefd[2] = {0};                                                                                                                                                                                  
     pipe(pipefd);  
    pid_t id = fork();
    if(id < 0){
      perror("fork error\n");
    }
    else if(id == 0){
      //child 写
      close(pipefd[0]);
      const char *msg = "I am child ......!\n";
      while(1){
        write(pipefd[1],msg,strlen(msg));
//strlen(msg)不需要+1,管道(文件)不认识\0
        sleep(5);
      }
    }
      else{
      //parent 读
      close(pipefd[1]);
      char buffer[64];
      while(1){
     ssize_t s = read(pipefd[0],buffer,sizeof(buffer)-1);// 是因为文件里面的内容读出来,想要以字符串的形式显示
          if(s > 0){
            //读取成功
            buffer[s] = '\0';//,文件读出来的内容无\0,需要自己加上;以\0结尾
            printf("father get message : %s\n",buffer);
         }//父进程读到子进程发来的信息
      }
    }
    return 0;
  }                                  
[S@VM-8-3-centos pipe]$ ./mypipe
father get message : l am child ....
father get message : l am child .....
father get message : l am child .....
father get message : l am child.....

管道的4种情况:
①子进程的写端每间隔5秒写一次,父进程的读端和子进程保持同步,5秒钟显示在屏幕1次。(管道是空的,读的快)。写、读端不关闭文件描述符,且长时间不写入,读端可能需要长时间阻塞
②子进程的写入端很快不停的写入,一瞬间被塞满3439,需要过一会再写入,父进程的读端每隔1段时间读取1次.(管道是写满的,被阻塞,写的快)。不关闭写、读端文件描述符时,实际进行写入时,如果写入条件不满足,写入端就要被阻塞。
情况2:

const char *msg ="I am child.. .. !\n" ;
int count = 0;
while( 1){
write(pipefd[1],msg, strlen (msg));
printf( " %d\n" , count++);
3435
3436
3437
3438
3439
father get message : l am child ...!
l am child .. ....!
l am child .. ... .!
l am child.. ....!
father get message : ..!
I am child.. ....!
l am child.. ....!
l am child.. ....!

③写端关闭文件描述符,读端在读取完数据后,会读到文件的结尾。
情况3:

write(pipefd[1], msg, strlen(msg));
printf("CHILD: %din", count++);
if (count == 2)
{
	close(pipefd[1]); break;
}
}
exit(2);
else{
	//father , read
	close(pipefd[1]);
	char buffer[64];
	while (1){
		ssize_t s = read(pipefd[0],buffer, sizeof(buffer)-1);
		if (s > 0){
			buffer[s] = 0;
			printf("father get message: %s\n", buffer);
			sleep(1);
		}
		printf("father exit return : %d\n", s);
CHILD:0
CHILD:1
father get message: I am chilt....!
father exit return : 16
father get message: I am child....!
I am child.. ..!
I am child....!
I am child. ...!
father exit return : 63
father get message :
father exit return : 1
father exit return : 0
father exit return : 0//返回值=0,表示到了文件结尾处

④读端关闭文件描述符,写端进程可能会被OS直接杀掉(不推荐这种方式 ,建议先关闭写端)写入的数据已经没有人读了,操作系统认为此时的写入是在浪费资源,用信号SIGPIPE杀死,写端子进程变成僵尸进程。

命名管道

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
int main()
{
if( -1 == mkfifo( " ./fifo" ,0666)){
perror ( "mkfifo error\n" );
return 1;
}}
[s@VM-8-3-centos file]$ ./a.out
[s@VM-8-3-centos file]$ ll
total 16
- rwxrwxr-x 1 s s 8408 Apr 716:25 a.out
prw- rw-r-- 1 s s 0 Apr7 16:25 fifo
-rw-rw-r-- 1 s s 159 Apr7 16:25 test.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#define FIFO_FILE "./fifo"
int main ()
{//标准输入(键盘)--buffer--(write)管道
int fd = open(FIFO_FILE,O_WRONLY);//客户端写然后发信息给服务端
if(fd >= 0)
{
char buffer[64];//自己的用户缓冲区buffer
while(1){
printf( "Please Enter Message # " );
flush (stdout) ;
ssize_t s = read ( 0, buffer,sizeof (buffer)-1);
if(s > 0)
{
buffer[s] = '\0';
write(fd , buffer,s);}
}
}}

#include<unistd.h>
#define FIFO_FILE "./fifo"
int main()
{
//对于server来说应该首先创建管道
umask(0);//权限是666,umask会影响权限,所以使用umask(0)。哪里修改umask影响哪里的进程,不影响其他没修改的进程。
if( -1 == mkfifo(FIFO_FILE,0666))
{
perror( "mkfifo error\n" );return 1;
}
int fd = open (FIFO_FILE,O_RDONLY);
if(fd >= 0)
{
char buffer[64];
while(1)
{
ssize_t s = read (fd, buffer, sizeof(buffer)-1);
if(s >0)
{
buffer[s] = '\0';//字符串以\0结尾
printf( "client# %s" , buffer);}
else if(s ==0){
//为0的时候说明,读取文件结束
printf ( "client quit , me too\n" );
break;
}
else{
//读失败了
perror( " read" );break;
}}}}
.PHONY:all
all:mm mm2
mm : mm.c
	gcc -o $@ $^
mm2:mm2.c
	 gcc -o $@ $^
.PHONY:clean
clean :
	rm mm mm2

[ s@VM-8-3-centos file]$ ll
total 36
- rwxrwxr-x 1 s s 8608 Apr7 17:38 client
-- rw- rw-r-- 1 s s 442Apr7 17:29 client.c
prw-rw-rw- 1 s s 0 Apr 7 17;38 fifo
- rw- nw-r--1 s s 133 Apr 7 16:36 Makefile
- rwXrwxr-x 1 s s 8664 Apr7 17:38 server
- -rw-rw-r-- 1 s s 777 Apr7 17:38 server.c
- [s@vM-8-3-centos file]s ./client
Please Enter Message # nihao
^c
[s@VM-8-3-centos file]$ ./server
client# nihao
client quit , me too

fifo管道文件大小是0,fifo是一种符号性或者标志性的文件,在内存中所创建的,直接进行信息的交互,不需要刷新到硬盘。client server2个无关的进程之间通过通过看到的文件fifo通信,

消息队列

一个进程中创建一个消息队列,往消息队列中写数据,而另一个进程则从那个消息队列中取数据。消息队列:用创建文件的方式建立的,如果一个进程向某个消息队列中写入了数据之后,另一个进程并没有取出数据,即使向消息队列中写数据的进程已经结束,保存在消息队列中的数据并没有消失,也就是说下次再从这个消息队列读数据的时候,就是上次的数据。

2.system V 共享内存

创建共享内存、关联共享内存、取消关联共享内存、删除共享内存
效率最高,写进去,对方立马可以看见,无堵塞
在一个bash当中,只允许一个前台进程
ulimit -a:查看资源的上限,关闭core file size,生成的core.pid文件是占有一定的磁盘大小,不停的出错,会消耗掉大量的磁盘资源。是否要生成core dump文件:在waitpid—statue参数,其中最后一个字节的最有一个bite位为core dump标志位,为1则生成.
存储结构变化:转化,强转:看待方式变化
字符串转为int:atoi

#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void handler(int signo)
{//接收到信号后打印signo
  printf("catch a signal : %d\n",signo);
}
int main(int argc,char *argv[])
{  signal(2,handler); //handler:函数指针,指向函数
   //argv[0] 代表的是可执行程序
  if(argc == 3)
  {
    kill(atoi(argv[1]),atoi(argv[2]));  
  }
  return 0;
}
[root@VM-8-3-centos log]#ls
Makefile myfile myfile.c
[ root@VM-8-3-centos log]#./myfile &
[1]26195
[ root@VM-8-3-centos log]# ./myfile 26195 9
[1]+Done ./myfile

信号量

信号量:描述并且管理一个资源的数量,提供wait/post的接口来保证程序不会产生二义性结果,同时信号量也可以保证线程同步,信号量=资源计数器+PCB等待队列,
1.count>0表示这个信号量是空闲的(或者理解为资源还可以被使用),进程(线程)可以使用这个信号量
2.count值=0,表示信号量被其他进程使用,现在不可以用这个信号量,但PCB等待队列也没有进程在等待
3.count值<0,count的绝对值表示有多少个进程〔线程)在等待

互斥信号量。初始值为1,取值范围为〔-1,0,1) .
当信号量为1时,表示两个进程皆未进入需要互斥的临界区;
当信号量为0时,表示有一个进程进入临界区运行,另一个必须等待;
当信号量为-1时,表示有一个进程正在临界区运行,另一个进程因等待而阻塞在信号量队列中,需要当前已在临界区运行的进程退出时唤醒。

如果系统只有用户态线程,则线程对操作系统是不可见的,处理机调度单位是进程;;
如果系统中有内核态线程,则操作系统可以按线程进行调度;
作业:包括多个进程,完成某一个功能,

不适合使用cache缓存的是:数据库中每条数据被访问的概率近似相等,且独立
缓存:磁盘的数据提前缓存在内存当中,从而减少程序访问获取数据时的IO操作次数,缓存一般会将高频访问的数据,提前读到内存当中缓存起来,
访问的概率相等,且独立,就失去了缓存的意义,因为无法区分到底缓存什么数据,能够提高程序的运行效率。除非全部缓存,对标的概念就是内存数据库
多线程程序为了减少线程IO的时间,可以把线程使用的数据提前缓存起来,提高程序的运行效率
尺寸较小的数据可以放到缓存当中,因为比较小,不会吃掉缓存的空间。同时缓存之后,也能提高程序的运行效率
大量的访问的服务想要访问的数据可以缓存下来,减少IO次数,提高程序的运行效率

3.信号

raise:自己给自己发任意信号,9号信号不允许捕捉(不能自定义)。

int main(int argc,char *argv[])
{  signal(6,handler); //signo=6
  while(1)
  {    abort();
  }
  return 0;
}

所有信号都经过OS向进程发信号,进程是由操作系统管理。信号的保存方法:每个PCB中都有1个信号位图,对应的bite位置1:触发相应的那个信号。
普通信号:被记录1次,实时信号:来了信号被立即处理,不被丢失,在OS中用链表管理,

pending未决

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void show_pending(sigset_t *pending)
{
  int sig = 1;
  for(; sig <=31;sig++)
  {
    //判断一个信号是否在信号集当中,相当于去遍历整个pending位图
    if(sigismember(pending,sig))
    {
      printf("1");
    }
    else 
    {
      printf("0");
    }
  }
  printf("\n");
}
void handler(int sig)
{
  printf("get a sig : %d\n",sig);
}
int main()
{
  //自定义捕捉2号信号
  signal(2,handler);
//不屏蔽信号集,信号一发过来直接递达(pending=1),进程终止,阻塞(pending=0)
  sigset_t pending;//pending在栈上
  sigset_t block,oblock;
   sigemptyset(&block);
  sigemptyset(&oblock);
  sigaddset(&block,2);//把2号信号添加到block信号集当中去,屏蔽2号信号
  sigprocmask(SIG_SETMASK,&block,&oblock);
  int count = 0;
  while(1){
       sigemptyset(&pending);
    //获取未决信号集
    sigpending(&pending);//把底层的pending拷贝获取上来
    show_pending(&pending);
    sleep(1);
    count++;
    if(count == 10)//10秒之后解除信号的阻塞,由未决变为递达
    {
      printf("recover sig mask!\n");
      //信号被递达,然后退出
      sigprocmask(SIG_SETMASK,&oblock,NULL);
    }//信号被恢复
  }
  return 0;
}

[ s@vM-0-3-centos_17_lesson]$./ test
0000000000000000000000000000000#已经屏蔽信号,但未产生
0000000000000000000000000000000
0100000000000000000000000000000#信号阻塞,不会递达
0100000000000000000000000000000
0100000000000000000000000000000
recover sig mask !
get a sig : 2
0000000000000000000000000000000#递达
0000000000000000000000000000000

信号被(处理)递达的时间点:内核态切换到用户态的时候
不同的进程用户区的 数据、代码不一样,操作系统只有1份,内核区一样,
产生 、阻塞、 未决(因为是 阻塞所以是未决) 、递达(处理动作) 、(忽略 默认 捕捉(自定义))
信号不递达,则pending、block;收到信号但不递达:pending;不管是否收到信号都可以被block;收到的信号被block,则一定不会被递达;
屏蔽(阻塞)1个信号是1个状态,这个状态是以时间段为单位的,未决1个信号是1个事件,当前有没有这个信号,进程收到信号不是被立即处理,

sigaction捕捉信号

struct sigaction {
void( *sa_handler) (int) ;
void( *sa_sigaction) (int , siginfo_t *, void * );
sigset_t sa_mask;//处理普通信号
int sa_flags ;
void ( *sa_restorer) (void) ;//处理实时信号
};

捕捉信2号信号:

#include<stdio.h>
#include<signal.h>

void sigcb(int sig)
{
  printf("get a sig , NO. %d\n",sig);
}
int main()
{
  struct sigaction act,oact;
  act.sa_handler = sigcb;
  act.sa_flags = 0;
  sigemptyset(&act.sa_mask);
  sigaction(SIGINT,&act,&oact);
  while(1);
  return 0;
}
[s@VM-8-3-centos s]$ make
gcc -o test test.c
[s@VM-8-3-centos s]$./test
^cget a sig ,NO.2
^cget a sig ,NO.2

逻辑是CPU执行过程中检测的,编译器感受不到2个执行流之间是有关系的,语法是编译器检测,

SIGCHLD 了解

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>

void handler(int sig)
{
  printf("pid : %lu,get a sig , NO. %d\n",getpid(),sig);
//getpid:子进程pid
}
int main()
{ signal(SIGCHLD,handler);
 if(fork() == 0){//子进程开始运行
    printf("child running ....!  pid : %lu, ppid : %lu\n",getpid(),getppid());
    sleep(5);
    printf("child quit!\n");//5秒之后,子进程退出
    exit(1);
  }
  while(1);//父进程:死循环
  return 0;
}
[s@VM-8-3-centos s]$ ./test
child running .... !pid : 24284,ppid : 24283
child quit!
pid : 24283, get a sig , NO. 17#说明子进程退出时给父进程发17号信号SIGCHLD

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值