Linux编程

一、GCC和库

1.gcc指令

1.在 gcc 命令加上-E 参数,可以得到预处理文件。展开头文件
-o表示新文件名称

gcc -E hello.c -o hello.i

2.编译,变为汇编文件

gcc -S hello.i

3.汇编,得到机器码

gcc -c hello.s

4.链接

gcc hello.o -o hello

2.静态库

1.创建静态库

//先编译成可重定位目标文件
gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
//利用ar工具创建静态库:ar rcs lib库名.a 所有可重定位目标文件
ar rcs libmath.a add.o sub.o

2.构建可执行文件

gcc -c main.c -o main.o
小l写库名,L写所在路径,库名不用写lib
gcc -static main.o -o main -l math -L ./

3.动态库

1.创建动态库

//先编译成可重定位目标文件(生成与位置无关的代码 -fPIC)
gcc -c add.c -o add.o -fPIC
gcc -c sub.c -o sub.o -fPIC
//使用 gcc -shared 制作动态库
gcc -shared -o libmath.so add.o sub.o

2.构造可执行文件

系统默认动态链接

gcc main.c -o main -l math -L ./

3.查看连接了哪些动态库

ldd main

4.配置动态库环境变量

  1. 通过环境变量,终端重启后失效:

export LD_LIBRARY_PATH=/home/lsr/桌面

  1. 写入终端配置文件.bashrc,永久生效

vim ~/.bashrc
写入export LD_LIBRARY_PATH=/home/lsr/桌面

. .bashrc 或 source .bashrc 或 重启终端 --> 让修改后的.bashrc生效。

动态库与静态库区别是重点

4.makefile脚本语言

makefile写在文件夹名为makefile的文件中,也可以指定文件名

make -f lib.mk

用make指令运行,-n查看make将要执行的命令

make
make -n
make clean 执行文件中的伪命令

定义变量,makefile变量为字符替换,$()使用变量

cc=gcc;
$(cc) main.c -o main

常用函数,获取当前目下符合条件的文件(字符串形式)

src=$(wildcard *.c)

替换字符串,将.c文件替换为.o

obj= ( p a t s u b s t (patsubst %.c,%.o, (patsubst(src))

三个系统变量

$@ 目标
$^ 所有依赖条件
$< 第一个依赖条件,在模式规则中依次取出条件

当文件与命令同名造成干扰,声明这是一个伪目标

.PHONY:clean

最终目标默认为第一个命令,也可以指定目标

ALL main

编译文件常规版

  1 src=$(wildcard *.c)
  2 obj=$(patsubst %.c,%.o,$(src))                                              
  3 ALL:main:add.o sub.o main.o
  4     gcc main.o add.o sub.o -o main
  5 main.o:main.c
  6     gcc -c main.c -o main.o
  7 add.o:add.c
  8     gcc -c add.c -o add.o
  9 sub.o:sub.c
 10     gcc -c sub.c -o sub.o
 11 clean:
 12     rm -rf *.o

采用模式替换版

  1 ALL:main                                                                    
  2 src=$(wildcard *.c)
  3 obj=$(patsubst %.c,%.o,$(src))
  4 main:$(obj)
  5     gcc $^ -o $@
  6 %.o:%.c
  7     gcc -c $< -o $@
  8 .PHONY:clean main
  9 clean:
 10     rm -rf *.o

5. io函数

#include<fcntl.h>
#include<sys/stat.h> 储存宏定义
三个文件描述符,0标准输入,1标准输出,2错误输出

1.open

三种打开方式

  1. O_RDONLY 只读方式
  2. O_WRONLY 只写方式
  3. O_RDWR 读写方式

可选模式组合:

1) O_APPEND: 把写入数据追加在文件的末尾。
2) O_TRUNC: 打开文件时把文件长度设置为零,丢弃已有的内容。
3) O_CREAT: 创建文件
4) O_EXCL: 与O_CREAT一起使用,不存在文件才创建

使用实例,返回文件描述符,失败返回-1
可选模式用 | 连接
0644中的0代表8进制

int fd=open("./test.tXt",O_RDWR | O_TRUNC);
int fd=open("./test.tXt",O_RDWR | O_CREAT,0664);

2.read

读取前n个字节文件到buf中
返回读到的字节数,失败返回-1

char buf[1024];
int rr=read(fd,buf,10);

接收系统输入,类似于scanf

int d=read(0,buf,10);
printf("%s\n",buf);

3.write

写入buf的前n个字节,返回写入的字节数

int wr = write(fd,buf,10);

4.close

关闭文件,成功返回0,失败返回-1

int c=close(fd);

5.打印错误信息

#include<errno.h>
返回根据错误号映射的字符串
#include <string.h>

printf(“open error:%s\n”,strerror(errno));

#include <stdio.h>

perror(“open error”);

6.lseek光标偏移

#include<sys/types.h>
#include <unistd.h>

1)SEEK_SET: 是一个绝对位置(从文件的头位置开始计算)。
2)SEEK_CUR: 是相对于当前位置的一个相对位置(从文件的当前位置开始计算)。
3)SEEK_END: 是相对于文件尾的一个相对位置(从文件的末尾位置开始计算)。

移动光标到相对位置,返回值为到文件开头的距离

int l=lseek(fd,-5,SEEK_CUR);

扩展文件大小
移动光标到文件末尾后500个字节,然后写入“\0“
注意文件要有写权限

lseek(fd, 500, SEEK_END)write(fd, "\0", 1);

直接调用函数扩展,底层还是调用lseek,文件也要有写权限

ftruncate(fd,1000);	扩展文件大小到1000,文件要有写权限

7.dup建立新文件标识符

#include <unistd.h>
给文件增加一个文件标识符,原有标识符并不会消失,也指向当前文件
返回一个系统自动生成的最小可用文件标识符,指向fd文件

int newfd=dup(fd);

指定fd的新标识符为10

dup2(fd,10);

8.main函数参数

argc是命令行参数的数量,argv是具体的参数。
./main,也占用一个参数

./main aaa bbb ccc

argc=4,
argv内含有:./main,aaa,bbb,ccc

9.stat显示文件信息

把文件信息存到结构体中
stat访问软连接是访问它所连接的文件,lstat访问的是软连接本身

struct stat sb;
stat("./main.c",&sb);
prtintf("%d",sb.st_size);

6. 进程

1. fork 创建进程

创建进程,但运行的还是当前程序,从创建行开始运行
返回子进程的进程号
子进程返回0,父进程返回子进程号,失败返回-1

int pid=fork();

获取当前进程号

getpid();

获取当前父进程号

getppid();

使程序休眠(单位秒)

sleep(1);

通过pid是否为0,判断子进程,为0就是子进程

int main(int argc, char* argv[])
 {
     int pid=fork();
      if(pid==0){
          printf("chiled%d,%d",getpid(),getppid());
      }
      else{
          printf("parennt%d,%d",getpid(),getppid());
      }
}

循环创建多个进程,不加break会创建出2^n个进程

	for(int i=0;i<3;i++){
          int pid=fork();
          if(pid==0){
              break;
          }
      }

2. exit 结束进程

结束当前进程,并驯如结束信息

exit(111)

在终端查看结束信息

echo $?

3. execl 执行其他程序

执行mian,进程号不变,如果execl运行成功,当前程序execl行下面的代码都不会被运行
第一个参数为路径,后几个参数为朱函数参数,所以第二个参数要写main(对应的第一个主函数参数)

execl("./main",“main”);

利用写绝对路径的方式执行系统指令,系统指令都在/bin下

execl("/bin/ls",“ls”,"-l","-h");

execlp执行系统命令

execlp(“ls”,“ls”,"-l","-h");

4. 打印进程情况

打印所有进程信息

ps -aux

搜索进程

ps -aux | grep 进程名

5. wait函数

僵尸进程:子进程死后没有被父进程回收,子进程残留资源(PCB)存放于内核中,会占用内存资源
孤儿进程:父进程先于子进程结束,子进程的父进程变为init进程(1)

1.wait函数
返回值为释放的进程号,没有子进程返回-1,一个wait函数只能释放一个进程,如有多个子进程需要循环释放
会阻塞进程,直到子进程结束
status为传出参数,程序正常结束会记录返回值(和exit()括号中的东西),不正常结束时会记录被哪个信号终止
#include <sys/types.h>
#include <sys/wait.h>

int status;
int wr = wait(&status);

2.waitpid
第一个参数

大于0 回收指定 ID 的子进程
0 回收和当前调用 waitpid 一个组的所有子进程
-1 回收任意子进程(相当于 wait)
小于 -1 回收指定进程组内的任意子进程

阻塞回收指定进程

int wr = waitpid(1023,&status,0);

WNOHANG不阻塞回收指定进程,如果子进程还没有结束返回0

int wr = waitpid(1023,&status,WNOHANG);

3.查看进程死亡信息
查看所有信号,linux中所有进程都是被信号终止的

kill -l

查看退出信息
WIFEXITED(status)//为真→ 进程正常结束
WEXITSTATUS(status)//如上宏为真,使用此宏 → 获取进程返回值 (exit的参数)

if(WIFEXITED(status)){		//如果正常退出
	printf("%d",WEXITSTATUS(status));	//打印返回值
}
else if(WIFSIGNALED(status)){	//被信号终止
	printf("%d",WTERMSIG(status));	//打印相应信号
}

6. 管道(pipe)

1.匿名管道
只能用于有亲缘关系的进程,共用一个管道
注意用写端就关闭读端,用读端就关闭写端
建立一个int长度为2的数组,p[1]为写端,p[0]为读端
从写段写的东西,会从读端读到

① 读管道: 1. 管道中有数据,read 返回实际读到的字节数。
2. 管道中无数据:
(1)管道写端被全部关闭,read 返回 0 (像读到文件结尾)
(2)写端没有全部被关闭,read 阻塞等待
② 写管道: 1. 管道读端全部被关闭, 进程异常终止
2. 管道读端没有全部关闭:
(1)管道已满,write 阻塞。
(2)管道未满,write 将数据写入,并返回实际写入的字节数。

实例代码

	int pipefd[2];
    int pr = pipe(pipefd);
    int pid = fork();
    if(pid==0){
       close(pipefd[0]);
       write(pipefd[1],"Hello World!",12);     
    }
    if(pid > 0){
        close(pipefd[1]);
        read(pipefd[0],buf,12);     
    }

7. 有名管道(fifo)

#include <sys/stat.h>
创建管道

mkfifo fifo

函数创建管道
返回值:成功返回0,出错返回-1

int mr = mkfifo("./fifo",0644);

有名管道创建成功后可以像读写文件一样使用,写入管道中内容

	int fd = open("./myfifo",O_RDWR);
	int i = 1;
	while(1){
		dprintf(fd,"%dth",i++);	//写内容到对应文件
		sleep(1);
	}

读出管道中内容

   int buf[10];
   int fd = open("./myfifo",O_RDONLY);
   while(1){
       int rd = read(fd,buf,3);
       printf("%s\n",buf);
  }

8. mmap

函数原型
<sys/mman.h>
创建映射:void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
释放映射:int munmap(void *addr, size_t length);
参数:
参数addr:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。
参数length映射区的长度。
参数prot:映射区域的保护方式。可以为以下几种方式的组合:
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
MAP_ANON建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。
参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值:
若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。

文件权限要大于mmap权限
偏移量要为0或4096的整数倍
mmap实现在内存进行文件操作

 11 #include<sys/mman.h>
 12 #include<fcntl.h>
 13 int main(int argc, char* argv[])
 14 {
 15     int fd = open("mmap.txt", O_RDWR | O_CREAT, 0644);
 16     ftruncate(fd,1000);	文件要有写权限
 17     void* ptr = mmap(NULL, 200, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 18     if(ptr == MAP_FAILED)
 19     {
 20         perror("mmap error:");
 21         exit(1);    
 22     }
 23     while(1)
 24     {
 25         sprintf(ptr, "-------hahaha--------");
 26         printf("%s\n", ptr);
 27         sleep(1);
 28     }
 29     munmap(ptr, 200);
 30     close(fd);
 31     return 0;
 32 }

进程间通信,父子进程公用一个缓存区,进行读写即可完成通信
无血缘关系的进程,用同一个文件做映射(参数要相同),得到也是一块相同的内存区(但系统检测到文件已经建立映射时,会直接返回地址,并不会创建新的映射)
血缘关系文件可以用匿名映射,无血缘关系的不能

 11 #include<sys/mman.h>                                                        
 12 #include<fcntl.h>
 13 int main(int argc, char* argv[])
 14 {
 15     int fd = open("mmap.txt", O_RDWR);
 16     void* ptr = mmap(NULL, 200, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 17     int pid = fork();
 18     if(fork == 0)
 19     {   
 20         sprintf(ptr, "++++happy++++");
 21     }
 22     else
 23     {   
 24         sleep(1);
 25         printf("%s\n",ptr);
 26     }
 27     close(fd);
 28     munmap(ptr, 200);
 29     return 0;
 30 }

匿名映射
用MAP_ANON,有些系统可能不支持

void* ptr = mmap(NULL, 200, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

用/dev/zero文件

int fd = open("/dev/zero", O_RDWR);
void* ptr = mmap(NULL, 200, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

9. 创建线程

gcc thread1.c -o thread1 -lpthread
编译含有线程的文件时,要在末尾添加-lpthread指令

 #include<pthread.h>
void* fun(void* arg){	//线程执行的函数
    int a = (int)arg;
     while(1)
     {
         printf("child thread\n,%d",a);
         sleep(1);
     }
 }
 int main(int argc, char* argv[])
 {
     int a = 10;
     pthread_t thread;
     int creat = pthread_create(&thread, NULL, fun, (void*)a);
     if(creat < 0)
     {
         printf("error: %s", strerror(creat));
         exit(1);
     }
     while(1)
     {
         printf("main thread\n");
         sleep(1);       
     }
     return 0;
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值