一, GNU工具链简介:
(1)编译代码步骤:
预处理 -> 编译 -> 汇编 -> 链接;
预处理:去掉注释,进行宏替换,头文件包含等工作;
gcc -E test.c -o test.i
编译: 不同平台使用汇编语言不同,汇编将高级语言编译成汇编语言;
gcc -S test.c -o test.s
汇编: 将汇编语言翻译成二进制代码;
gcc -c test.c -o test.o
链接: 包含各函数库的入口,得到可执行文件;
gcc -o test test.c
(2)gcc编译:
.c文件编译:gcc -o DESFILE SRCFILE ,将源c文件SRCTILE编译为可执行文件DESFILE ;
gcc -o DESFILE SRCFILE ,加上-g选项,生成的可执行文件DESFILE可进行调试;
(2)gdb调试:
调试前提:gcc编译时加入-g参数;
调试选项:gdb DESFILE 进入调试,
l 列出代码信息,后面可带整数参数;
b 设断点,后面跟要设断点的函数名或者行数;
r 运行;
n 下一步;
c 继续;
q 退出,退出调试;
二,Linux下简单编程实例:
(1)进程创建:
函数说明:
#include <sys/types.h>
#include <unistd.h>
pid_t fork();
调用fork()创建进程,若创建成功,父进程返回子进程ID(>0),子进程返回0,出错返回-1;
实例:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 int main(void) 6 { 7 pid_t child_pid; 8 printf("the main program process ID is:%d",(int)getpid); 9 child_pid = fork(); 10 if(child_pid >0) 11 { 12 printf("This is parent process:%d ",(int)getpid); 13 } 14 else if(pid == 0) 15 { 16 printf("This is child process:%d ",(int)getpid); 17 } 18 else 19 { 20 printf("fork() occurr erro! "); 21 } 22 return 0; 23 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 int spawn(char* progam,char** arg_list) 6 { 7 pid_t child_pid; 8 child_pid = fork(); 9 if(child_pid!=0) 10 { 11 return child_pid; 12 } 13 else 14 { 15 execvp(program,arg_list); 16 fprintf(stderr,"Erro occured! "); 17 abort(); 18 } 19 return 0; 20 } 21 22 int main(void) 23 { 24 char* arg_list[] = {"ls","-l","/",NULL}; 25 spawn("ls",arg_list); 26 return 0; 27 }
(2)线程创建:
函数说明:
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,
void *(start_routine)(void *),void *arg);
第一个参数为线程ID;
第二个参数为线程属性,可默认为NULL;
第三个参数为线程执行的函数;
第四个为函数的参数;
实例:
线程创建:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <pthread.h> 4 5 void* print_x(void* unused) 6 { 7 while(1) 8 { 9 printf("x"); 10 } 11 return NULL; 12 } 13 14 int main(void) 15 { 16 pthread_t thread_id; 17 pthread_create(&thread_id,NULL,&print_x,NULL); 18 while(1) 19 { 20 printf("s"); 21 } 22 return 0; 23 }
(3)信号:
signal:软件中断,传递给进程的异步消息;
当进程收到信号后,将立即对信号进行处理;
信号可以在任何代码位置中断;
实例:
1 #include <stdio.h> 2 #include <signal.h> 3 #include <sys/types.h> 4 #include <unistd.h> 5 6 void sig_usr(int signo) 7 { 8 printf("Receive signal ! "); 9 } 10 11 int main(void) 12 { 13 printf("%d ",(int)getpid()); 14 if(signal(SIGUSR1,sig_usr)==SIG_ERR) 15 { 16 printf("Can't catch signal"); 17 } 18 for(;;) 19 { 20 pause(); 21 } 22 return 0; 23 }
(4)线程,信号量
实例:
1 void* thread_fun(void* arg) 2 { 3 while(1) 4 { 5 pthread_mutex_lock(&thread_flag_mutex); 6 while(!thread_flag) 7 { 8 thread_cond_wait(&thread_flag_cv,&thread_flag_mutex); 9 } 10 pthread_mutex_unlock(); 11 do_work(); 12 } 13 return NULL; 14 } 15 16 void set_thread_flag(int flag_vlaue) 17 { 18 pthread_mutex_lock(&thread_flag_mutex); 19 thread_flag_mutex = flag_vlaue; 20 pthread_cond_signal(&thread_flag_cv); 21 pthread_mutex_unlock(&thread_flag_mutex); 22 }
(5)无名管道pipe:
函数原型: int pipe(int fd[2]);
生成两个文件描述符,一个用于读f[0],一个用于写f[1];
无名管道的使用条件:父子进程;
实例:
1 #include <unistd> 2 3 int main(void) 4 { 5 int fd[2]; 6 char buffer[80]; 7 pipe(fd); 8 if(fork()>0) 9 { 10 char s[] = "Hello! "; 11 write(fd[1],s,sizeof(s)); 12 } 13 else 14 { 15 read(fd[1],buffer,80); 16 printf("buffer: %s",buffer); 17 } 18 return 0; 19 }
(6)基于文件描述符的文件简单操作:
文件打开与关闭函数:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
int close(int fd);
flags类型:flags决定文件的打开方式,有以下常用类型:
O_RDONLY:只读; O_RDWR:读写;
O_WRONLY:只写; O_CREAT:若文件不存在则创建;
文件读写函数:
#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count);
ssize_t write(int fdvoid *buf,size_t count);
文件的定位:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
fd:文件注释符;
offset:偏移量;
whence:取值范围:SEEK_SET(将文件位移量设置为据文件开始出offset字节);
SEEK_CUR(将文件位移量设置为当前值+offset字节);
SEEK_END(将文件位移量设置为文件长度+offset字节);
实例:
1 #include <unistd.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(void) 7 { 8 char s[] = "Linux c program! " 9 char buffer[80]; 10 int fd,count; 11 fd = open("./test.txt",O_WRONLY|O_CREAT); 12 write(fd,s,sizeof(s)); 13 close(fd); 14 fd = open("./test.txt",O_RDONLY); 15 count = read(fd,buffer,80); 16 close(fd); 17 printf("buffer:%s",buffer); 18 return 0; 19 }
(7)socket客户端与服务器端:
基于TCP的socket通信流程:
socket套接字基础:
IPV4套接字:
#include <netinet/in.h>
struct sockaddr_in
{
unsigned short sin_len; //IPV4 地址长度
sa_family_t sin_family; //通信地址类型,AF_INET对应IPV4, AF_INET6对应IPV6
unsigned short int sin_port; //16位端口号,以网络字节序存储
struct in_addr sin_addr; //将要访问的IP地址, 其中struct in_addr{uint32_t s_addr;}以网络字节顺序
unsigned char sin_zero[8];//未使用的字段,填充为0
}
struct hostent结构体定义:
struct hostent
{
char *h_name; //主机正式名称
char **h_aliases; //主机别名
int h_addrtype; //主机地址类型,IPV4为AF_INET
int h_length; //地址长度,IPV4为4字节,32位
char **h_addr_list; //主机IP地址列表
};
函数:
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname); //实现域名或主机名到IP地址的转换
struct hostent *gethostbyaddr(const char *addr,size_t len,int family); //实现IP地址到域名或主机名的转换
客户端:
主要函数:
socket():建立套接字,设定远程IP和端口;
connect():连接远程计算机指定端口;
read()或recv(),读取客户端发来的数据;
write()或send()发送数据;
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <stdio.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <fcntl.h> 10 #include <sys/shm.h> 11 12 #define MYPORT 8887 13 #define BUFFER_SIZE 1024 14 15 int main() 16 { 17 ///定义sockfd 18 int sock_cli = socket(AF_INET,SOCK_STREAM, 0); 19 20 ///定义sockaddr_in 21 struct sockaddr_in servaddr; 22 memset(&servaddr, 0, sizeof(servaddr)); 23 servaddr.sin_family = AF_INET; 24 servaddr.sin_port = htons(MYPORT); ///服务器端口 25 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ///服务器ip 26 27 ///连接服务器,成功返回0,错误返回-1 28 if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) 29 { 30 perror("connect"); 31 exit(1); 32 } 33 34 char sendbuf[BUFFER_SIZE]; 35 char recvbuf[BUFFER_SIZE]; 36 while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) 37 { 38 send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送 39 if(strcmp(sendbuf,"exit ")==0) 40 break; 41 recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收 42 fputs(recvbuf, stdout); 43 44 memset(sendbuf, 0, sizeof(sendbuf)); 45 memset(recvbuf, 0, sizeof(recvbuf)); 46 } 47 48 close(sock_cli); 49 return 0; 50 }
服务器端:
主要函数:
socke():建立套接字,用这个套接字完成通讯监听,数据收发;
bind(): 绑定一个端口号和一个IP,使套接字与之关联;
listen():使服务器该端口和IP处于监听状态,等待连接请求;
accept():接收远程计算机的连接请求,建立服务器与客户端之间的连接;
read()或recv(),读取客户端发来的数据;
write()或send()发送数据;
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <stdio.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <fcntl.h> 10 #include <sys/shm.h> 11 12 #define MYPORT 8887 13 #define QUEUE 20 14 #define BUFFER_SIZE 1024 15 16 int main() 17 { 18 ///定义sockfd 19 int server_sockfd = socket(AF_INET,SOCK_STREAM, 0); 20 21 ///定义sockaddr_in 22 struct sockaddr_in server_sockaddr; 23 server_sockaddr.sin_family = AF_INET; 24 server_sockaddr.sin_port = htons(MYPORT); 25 server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 26 27 ///bind,成功返回0,出错返回-1 28 if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) 29 { 30 perror("bind"); 31 exit(1); 32 } 33 34 ///listen,成功返回0,出错返回-1 35 if(listen(server_sockfd,QUEUE) == -1) 36 { 37 perror("listen"); 38 exit(1); 39 } 40 41 ///客户端套接字 42 char buffer[BUFFER_SIZE]; 43 struct sockaddr_in client_addr; 44 socklen_t length = sizeof(client_addr); 45 46 ///成功返回非负描述字,出错返回-1 47 int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length); 48 if(conn<0) 49 { 50 perror("connect"); 51 exit(1); 52 } 53 54 while(1) 55 { 56 memset(buffer,0,sizeof(buffer)); 57 int len = recv(conn, buffer, sizeof(buffer),0); 58 if(strcmp(buffer,"exit ")==0) 59 break; 60 fputs(buffer, stdout); 61 send(conn, buffer, len, 0); 62 } 63 close(conn); 64 close(server_sockfd); 65 return 0; 66 }