linux下的c语言开发流程,Linux下的C语言开发学习笔记

本文详细介绍了GCC编译的四个步骤:预处理、编译、汇编和链接,以及不同阶段生成的文件后缀。同时,讲解了动态和静态链接的区别。接着,文章探讨了Linux进程的创建,特别是fork()函数在创建子进程中的作用,以及exec函数族如何替换当前进程。最后,重点阐述了进程间的通信方式,包括无名管道和命名管道的使用,以及文件描述符重定向的应用。
摘要由CSDN通过智能技术生成

gcc编译四个过程

预处理

头文件包含、宏替换、条件编译、删除注释

gcc -E

该步骤生成的文件后缀 .i

编译

语法检查、将预处理的文件编译成汇编文件

gcc -S

该步骤生成的文件后缀 .s

汇编

将汇编文件处理成二进制文件

gcc -c

该步骤生成的文件后缀 .o

链接

整合二进制文件、相关库函数、启动代码生成可执行文件,main函数是由启动代码调用的,程序是从启动代码开始运行的。

静态链接,指调用ld/collect2链接程序,将所有的.o中的机器指令整合到一起,然后保存到可执行文件中。

动态链接,在编译的时候只留下调用接口(函数第一条指令的地址),当程序真正运行的时候,才去链接执行,动态链接这件事不是在编译时发生的,是在程序动态运行时发生。

比如程序中调用printf函数,这个函数基本都是动态库提供的,程序编译后代码里面是没有printf函数代码的,只有printf这个接口,当程序运行起来后,再去动态链接printf所在的动态库,那么程序就能调用printf函数。

Linux默认的动态库搜索路径/usr/lib

gcc

该步骤生成的文件后缀 .out

参考自

动态空间的申请与释放

//动态从堆区空间申请

int *p = (int *)malloc(n*sizeof(int));

if(p == NULL)

{

perror("malloc");

exit(-1);

}

// 空间的释放

if(p != NULL)

{

// 释放指向的内存空间

free(p);

// 取消对以释放空间的指向

p = NULL;

}

进程相关

进程创建

进程是系统资源分配的最小单位,是正在运行的,且占有内存空间。

Linux环境中创建进程可通过调用fork() / vfork()函数,在一个已经存在的进程中创建一个新的子进程,它拷贝了父进程的地址空间,包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等,子进程仅独有其进程号、计时器等,因此使用fork()创建的进程的代价是较大的。

父子进程的地址空间相同,但相互独立。

子进程从fork()语句后开始执行。

但在进程中使用exec函数族时,exec中调用执行的程序会替换当前进程。

#include

#include

...

pid_t pid = fork();

if(pid < 0) // 创建失败

{

perror("fork");

_exit(-1);

}

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

{

...

}

else if (pid > 0) // 父进程,返回的pid是子进程的id

{

...

}

进程通信——管道

管道是内核提供的一段内存(队列),这段内存抽象成文件,通过访问文件描述符的形式,来读写这块内存中的数据。

单向通信,半双工

面向字节流

内置同步互斥机制。互斥:当多个进程一起读时会,读到完整数据或者读不到数据。同步:管道为空,读阻塞;管道满了,写阻塞。

所有引用这个管道的进程都销毁,管道才释放。真正释放的只是管道在内核中对应的这段内存,这个内存才是管道的本质

无名管道

仅限于具有“血缘关系”的进程间(如父子进程)的通信,因为这类进程在创建时拷贝了父进程的地址空间,其中包含了以打开的文件描述符,故可以打开以在父进程中创建的无名管道,进行相互通信。

// 创建数组存储管道的文件描述符,fd[0]用于读, fd[1]用于写

int fd[2];

// 创建并打开管道

pipe(fd);

pid_t pid = fork();

if(pid == 0)

{

// 可在子进程中读取管道信息

char buf[128] = "";

read(fd[0], buf, sizeof(buf));

}

if(pid > 0)

{

// 可在父进程中向管道写信息

write(fd[1], "hello pipe", strlen("hello pipe"));

}

命名管道

相互通信的进程可通过管道的名字(即文件名),打开管道进行读/写

一般在读端与写端都分别创建同一命名管道(当要创建文件存在时,文件不会重复再创建),因为无法确定实际运行中,哪端的代码先运行。若仅在一端创建管道文件,而是另一端先运行到open管道文件的代码时,会因找不到该文件而报错。通过读写两端分别创建同一命名管道文件,可以确保无论哪端先执行到open代码,都能正常打开管道。

读端进程

// 创建一个有名管道,并赋予相关权限

mkfifo("fifo_demo", 0777);

// 以只读的方式打开创建的有名管道

int fd = open("fifo_demo", O_RDONLY);

char buf[128] = "";

read(fd, buf, sizeof(buf));

printf("读取到的数据为:%s\n", buf);

// 关闭文件描述符fd

close(fd);

写端进程

// 创建一个有名管道

mkfifo("fifo_demo", 0777);

// 以只写的方式打开创建的有名管道

int fd = open("fifo_demo", O_WRONLY);

// 发送消息

write(fd, "hello fifo", strlen("hello fifo"));

// 关闭文件描述符fd

close(fd);

文件描述符符重定向

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

{

// 由于grep仅从输入设备0中读取信息

// 所以需要将文件描述符1重定向到fd[0],使grep从无名管道中读取结果

dup2(fd[0], 0);

// 执行grep命令

execlp("grep", "grep", "ps", NULL);

}

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

{

// 由于ps仅向输出设备1中写结果

// 所以需要将文件描述符1重定向到fd[1],使ps向无名管道中输出结果

dup2(fd[1], 1);

// 执行ps命令

execlp("ps", "ps", "-elf", NULL);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值