[C++系列] UNIX高级编程 day08 文件锁,库函数和系统调用之间的关系,进程基础
in C/C++ with 0 comment

[C++系列] UNIX高级编程 day08 文件锁,库函数和系统调用之间的关系,进程基础

in C/C++ with 0 comment

复习

一,获取文件的元数据

三,文件锁的使用

文件锁

F_GETLK:

举例:

1 #include<stdio.h>
2 #include <fcntl.h>
3 #include<unistd.h>
4 int main(int argc,char *argv[]){
5         int fd;
6         struct flock lock;
7         //打开文件
8         fd= open(argv[1],O_RDONLY);
9         if(fd==-1){
 10                 perror("open");
 11                 return -1;
 12         }
 13         lock.l_type=F_RDLCK;
 14         lock.l_whence=SEEK_SET;
 15         lock.l_start=0;
 16         lock.l_len=6;
 17         //测试是否可以加读锁
 18         int f = fcntl(fd,F_GETLK,&lock);
 19         if(f==-1){
 20                 perror("fcntl");
 21                 return -1;
 22         }
 23         if(lock.l_type==F_UNLCK){
 24                 printf("read lock success...\n");
 25         }
 26         else{
 27                 //将hold这把锁的进程的pid输出。
 28                 printf("hold process pid:%d\n",lock.l_pid);
29         }
 30         getchar();
 31         //关闭的时候文件描述符上的所有的锁释放了
 32         close(fd);
 33         return 0;
 34 }

#二,库函数和系统调用之间的关系

fgetc(3):调用这个函数的时候,首先从文件缓冲区中读取数据,如果成功,立即返回读取到的内容。如果缓冲区取空,
调用read(2),从文件中读取数据到缓冲区,,fgets(3)文件缓冲区获得数据,返回。

fputc(3):调用这个函数的时候,首先检测,写入入缓冲区是否满,不满,直接将数据写入缓冲区。如果满,调用write(2)将写入缓冲区的内容写到文件,清空缓冲区,这个时候,再将fputc(3)的内容写入缓冲区。
fflush();用来清空缓冲取。

fclose(3)调用这个库函数的时候,首先刷新缓冲区。然后调用close(2)关闭文件描述符,关闭缓冲区。

使用库函数对文件操作,这些文件都是缓冲文件。
使用系统调用对文件操作称为非缓冲文件。

库函数能比较好地实现跨平台
系统调用不能实现跨平台。

举例

 1 #include<stdio.h>
  2 int main(int argc,char *argv[]){
  3         FILE *fp=fopen(argv[1],"r");
  4         if(fp==NULL){
  5                 perror("fopen");
  6                 return -1;
  7         }
  8 }

三,文件操作的杂项

Mkdir(2)
Rmdir(2)
Umask(2)
Unlink(2);
Link(2);
chmod(2)
Symlink(2). //软链接
Rename(2)
Chdir(2)
Remove(3)
Symlinkat(2)
Access(2)
Getcwd()

#include <sys/stat.h>
int mkdir(const char *path, mode_t mode);

文件管理 文件系统

四,进程的基础

1,进程基础
程序 与 进程
程序 是计算机指令的集合,静态饿,存放在磁盘之中。
进程 是程序执行的实例,动态的,存放在内存中方

一个程序执行一次创建一个进程,执行多次,创建多个进程。
每个进程都有自己的pid。每个进程在内核中都有自己的进程控制块。(process control bloc)PCB。操作系统内核通过管理PCB来对进程进行管理。

在linux系统中,用户级进程构成了一颗树。树根是1号进程,也是init进程。
如何查看这棵树: pstree
查看进程的详细信息: top ps -aux

2,进程创建

如何创建一个新的进程: 使用fork(2)

#include <unistd.h>

pid_t     fork(void);    

功能:创建一个子进程。

参数:void

返回值:
成功:父进程中 返回子进程的pid

        子进程中        返回0    

错误: 父进程中 返回-1 没有子进程被创建

新进程是子进程
调用fork的进程是父进程

Getpid(2) 获取当前进程id
getppid(2)获取当前父进程的id

创建子进程以后,子进程有自己的pid。但是子进程的PCB从父进程中复制。
PCB在内核中,在内存中
fork的时候,父进程和子进程共用代码段,数据段,堆,栈

写时复制技术

编写代码测试 子进程从父进程的继承。

1 #include<stdio.h>
 2 #include <unistd.h>
 3 int main(){
 4         pid_t pid;
 5         //创建子进程。
 6         int val = 5;
 7         pid = fork();
 8         if(pid == -1 ){
 9                 perror("fork");
10                 return -1;
11         }
12         if(pid == 0){//子进程执行的代码
13                 val = 8;
14                 printf("cval=%d\n",val);
15                 printf("&cval = %p\n",&val);
16         }else{//父进程执行的代码
17                 printf("pval=%d\n",val);
18                 printf("&pval=%p\n",&val);
19         }
20         return 0;
21 }
~```



## 举例
使用fork创建子进程

1 #include<stdio.h>
2 #include <unistd.h>
3 int main(){
4 pid_t pid;
5 //创建子进程
6 pid = fork();
7 if(pid == -1){
8 perror("fork");
9 return -1;
10 }
11 if(pid == 0){
12 printf("child...n");
13 }else{
14 printf("parent..n");
15 }
16 return 0;
17 }
~

3,进程退出

区分`return` 和`exit(3)`

`return`只是从函数返回,代表的是结束函数的生命周期。
`Exit(3)`代表结束的是进程的生命周期。

include<stdlib.h>

Exit(int status);

功能:使进程结束。
参数:`status & 0377`被返回给父进程
返回值:

有符号的整型    `status`就有可能是负数

`Status & 0377 `    低8位
`0~255`

调用`exit(3)`退出进程的时候,会刷新`stdio(3)`的缓冲区
关闭这些文件流

在进程退出的时候,可以做一些后期处理。
遗言函数。可以为进程注册遗言函数,在进程结束的时候调用这些遗言函数。

如何注册遗言函数

Ataxia();
#include <stdlib.h>

`int     atexit(void (*func)(void));`
功能:给进程注册一个函数,在进程终止的时候被调用。
参数:
`Void (*func)(void):`  指定遗言函数的首地址。
返回值:
0     成功
非0    错误    

不用给遗言函数传递参数
遗言函数的注册顺序和调用顺序相反
一个遗言函数可以被注册多次,每注册一次就被调用一次。
子进程继承父进程的遗言函数
## 举例
遗言函数的使用

1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 void doit(void);
5 void doit1(void);
6 int main(){
7 //给进程注册一个遗言函数
8 pid_t pid;
9 pid = fork();
10 atexit(doit);
11 atexit(doit1);
12 if(pid == 0){
13 exit(1);
14 }
15 //exit(1);
16 getchar();
17 return 0;
18 }
19 void doit1(void){
20 printf("1 is outn");
21 }
22 void doit(void){
23 printf("0 is out!n");
24 }
~`

on_exit();

#include<stdlib.h>
Int on_exit(Void (* function)(int, void *),void *arg);

功能:在进程正常终止的时候被调用。
参数:
Function:指定了注册给进程的遗言函数。
Arg:遗言函数的参数
返回值:
0。成功
非0 错误

Void (* function)(int, void *)
Void *   来自于on_exit函数的地儿个参数。,
int        来自于进程中止的时候的的退出状态值。

举例

使用on_exit函数给进程注册遗言函数,在进程退出时调用遗言函数

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 void doit(int n,void *arg){
  4         printf("exit  code %d\targ %s\n",n,(char *)arg);
  5 }
  6 int main(){
  7         on_exit(doit,"good");
  8         getchar();
  9         return 7;
 10 }
~```

include <unistd.h>

void _exit(int status);

功能:终止当前进程
参数:
 ` status:`退出状态吗
返回值:
这个函数不返回

关闭所有的属于这个进程的文件描述符。
属于这个进程的所有子进程都过继给1号进程    init进程
过继给init进程的这些子进程称为孤儿进程。

## 举例
孤儿进程

2 #include<stdlib.h>
3 #include <sys/types.h>
4 #include<unistd.h>
5 int main(){
6 pid_t pid;
7 //创建一个子进程
8 pid=fork();
9 if(pid==-1){
10 perror("fork");
11 return -1;
12 }
13 if(pid == 0){// 子进程执行
14 printf("parent of child pid %dn",getpid());
15 sleep(4);
16 printf("parent of child pid %dn",getpid());
17 }else{
18 sleep(1);
19 printf("parent pid :%dn",getpid());
20 }
21
22 return 0;
23 }

## 练习

1 #include<stdio.h>
2 #include<stdlib.h>
3 int main(void){
4 printf("%d",-1 & 0377);
5 exit(-1);
6 return 0;
7 }



## 总结:
一,文件锁的使用。加锁测试
二,库函数和系统调用之间的关系
三,文件操作的杂项
四,进程的基础
进程的创建    退出
遗言函数

## 文件的复制

1 #include<stdio.h>
2
3 void file_copy(int s,int d){
4 char buf[128];
5 int r,w;
6 while((r=read(s,buf,128))){
7 char *tmp =buf;
8 while(1){
9 w=write(d,buf,r);
10 tmp += w;
11 r = r-w;
12 if(r == 0) break;
13 }
14 }
15 return;
16 }
17
18 int main(int argc,char *argv){
19 int s_fd,d_fd;
20 int mode_t mode=0664;
21 int flags = O_RDWR|O_CREAT|O_TRUNC
22 //以只读方式打开源文件
23 s_fd = open(argvp[1],O_RDONLY);
24 //以写的方式打开目的文件,如果文件不存在,创建文件,文件存在,清空
25 d_fd = open(argv[2],flags,mode);
26 if(){
27 perror("open");
28 return -1; return -1;
29 }
30 //文件复制
31 file_copy(s_fd,d_fd);
32 //关闭文件
33 close(s_fd);
34 close(d_fd);
35 return 0;
36 }

Responses