操作文件的系统调用 && 进程替换exec



提示:以下是本篇文章正文内容,下面案例可供参考

一、系统调用

系统调用:用很少的函数对文件和设备进行访问和控制,这些函数被成为系统调用,由UNIX(和Linux)直接提供它们也是通向操作系用本身的接口。

系统调用是在库与操作系统之间的方法,一个程序通过调用库方法来调用系统调用,在操作系统上实现想要实现的内容。程序也可以直接调用系统调用。
在这里插入图片描述

文件操作有关的系统调用

 int open(const char* pathname, int flags);//用于打开一个已存在的文件
 
 int open(const char* pathname, int flags,mode_t mode);//用于新建一个文件,并设置访问权限
 参数介绍:
 pathname:将要打开的文件路径和名称
 flags : 打开标志,
 如 O_WRONLY 只写打开
 O_RDONLY 只读打开
 O_RDWR 读写方式打开
 O_CREAT 文件不存在则创建
 O_APPEND 文件末尾追加
 O_TRUNC 清空文件,重新写入
 mode: 权限 如:“0600”
 返回值:为文件描述符

 ssize_t read(int fd, void* buf, size_t count); //读取文件内容
 参数介绍:
 fd 对应打开的文件描述符
 buf 存放数据的空间
 count 计划一次从文件中读多少字节数据
 返回值:为实际读到的字节数

 ssize_t write(int fd, const void* buf,size_t count); //向文件写入
 参数介绍:
 fd 对应打开的文件描述符
 buf 存放待写入的数据
 count 计划一次向文件中写多少数据

 int close(int fd); //关闭文件
 参数介绍:
 fd 要关闭的文件描述符

文件描述符
文件描述符是一个int 型的一个数据。

普通用户是无法直接访问内核中的文件的,用户只有通过系统调用来访问内核中的数据,要想让内核知道到底要访问哪一个文件,就要告诉内核一个程序启动后生成的ID,内核拿到ID之后,就可以找到对应的文件并对文件进行相应的操作。这个ID,就是文件描述符。

分析:父进程先打开一个文件,fork 后子进程是否可以共享使用?

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <assert.h>
 #include <fcntl.h>
 //分析程序执行的打印结果
 int main()
 {
 char buff[128] = {0};
 //myfile.txt 自己创建,并写入内容为“abcdef”
 int fd = open("myfile.txt",O_RDONLY);

 pid_t pid = fork();
 assert( pid != -1 );

 if ( pid == 0 )
 {
 read(fd,buff,1);
 printf("child buff=%s\n",buff);

 sleep(1);
 read(fd,buff,1);
 printf("child buff=%s\n",buff);
 }
 else
 {
 read(fd,buff,1);
 printf("parent buff=%s\n",buff);

 sleep(1);
 read(fd,buff,1);
 printf("parent buff=%s\n",buff);
 }

 close(fd);
 }

由于 fork 创建的子进程的 PCB 是拷贝父进程的,子进程的 PCB 中的文件表指向打开文件的指针只是拷贝了父进程 PCB 中的值,所以父子进程会共享父进程 fork 之前打开的所有文件描述符。如下图所示:

在这里插入图片描述

练习题:写一个程序完成对一个普通文件的复制,(类似 cp 命令)

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <fcntl.h>
 #include <unistd.h>
 /*
 *将当前目录下名为"passwd"的普通文件复制一份,新文件名叫"newpasswd"
 */
 int main()
 {
 char buff[256] = {0};
 int fdr = open("passwd",O_RDONLY);
 assert( fdr != -1 );

 int fdw = open("newpasswd",O_WRONLY|O_CREAT,0600);
 assert( fdw != -1 );

 int n = 0;

 while( (n = read(fdr,buff,256)) > 0 )
 {
 write(fdw,buff,n);
 }

 close(fdr);
 close(fdw);
 }

二、系统调用与库函数的区别

区别: 系统调用的实现在内核中,属于内核空间,库函数的实现在函数库中,属于用户空间。

系统调用执行过程如下图:

在这里插入图片描述

三、进程替换 exec

(1)exec 系统函数介绍

exec 系列替换过程:pcb 使用以前的,进程实体更换。

 #include <unistd.h>//需要引用的头文件
 
 exec 系类方法介绍以 execl 为例:
 /*
 *path:新替换的程序的路径名称
 *arg :传给新程序主函数的第一个参数,一般为程序的名字
 *arg 后面是剩余参数列表,参数个数可变,必须以空指针作为最后一个参数
 */
 int execl(const char* path, const char * arg,...);
 
 int execlp(const char* file, const char * arg,...);
 
 int execle(const char* path, const char * arg,...,char* const envp[]);
 
 int execv(const char * path, char* const argv[]);
 
 int execvp(const char * file, char* const argv[]);
 
 int execve(const char * path, char* const argv[],char* const envp[]);

(2)exec 系统函数使用

以 ps 替换当前程序为例,介绍 exec 系统函数使用,注意该系列方法功能都一样,没有区别,只是参数不同.

execl 使用代码示例如下:( ps 替换当前程序)

示例一:

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
 #include <string.h>

 int main()
 {
 printf("main pid=%d\n",getpid());
 //execl 执行成功不返回,直接从新程序的主函数开始执行,只有失败才返回错误码
 execl("/bin/ps","ps","-f"(char*)0);
 perror("execl error");
 exit(0);
 }

示例二:

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
 #include <string.h>

 int main()
 {
 //存放传给新程序主函数的参数
 char* myargv[] = {"ps","-f",(char*)0};
 //存放传给新程序主函数的环境变量
 char* myenvp[] = {"MYSTR=hello","VAL=100",(char*)0};

 printf("main pid=%d\n",getpid());

 //execl 执行成功不返回,失败返回错误码
 execve("/bin/ps",myargv,myenvp);
 perror("execve error");

 exit(0);
 }

(3) fork 和 exec 联合使用创建一个全新的进程

如下示例,当前主程序复制产生一个子进程,子进程用新程序“b”替换自身。

当前主程序如下:

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>

 int main(int argc, char* argv[],char* envp[])
 {
 printf("main pid=%d\n",getpid());

 pid_t pid = fork();
 assert( pid != -1 );

 if ( pid == 0 )
 {
 char * myargv[] = {"b","hello","abc","123",(char*)0};
 //char * myenvp[] = {"MYSTR=hello","VAL=100",(char*)0};

 execve("./b",myargv,envp);
 perror("execl error");
 exit(0);
 }

 wait(NULL);
 printf("main over\n");

 exit(0);
 }

新替换的 b 程序如下:

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
 #include <string.h>


 int main(int argc, char* argv[],char* envp[])
 {
 printf("b pid=%d\n",getpid());

 int i = 0;
 printf("argc=%d\n",argc);

 for( ; i < argc; i++ )
 {
 printf("argv[%d]=%s\n",i,argv[i]);
 }

 for( i = 0; envp[i] != NULL; i++ )
 {
 printf("envp[%d]=%s\n",i,envp[i]);
 }

 exit(0);
 }
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__小柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值