Linux系统编程

1.文件编程:

(1)光标lseek():

lseek();的返回值可用来计算文件大小
lseek();光标位置移动

(2)linux系统默认文件标识符

fd = 0:标准输入(从键盘输入)
fd = 1:标准输出(从键盘输出)
fd = 2:标准错误

(3)文件类型:

静态文件:文件存储在磁盘中
动态文件:open静态文件->内核产生linux内核结构体

(4)写一个整数、结构体到文件:

例:int data = 10;//结构体类似
write(fd,&data,sizeof(data));

(5)open和fopen的区别:

fopen fread fwrite fseek fclose fgetc fputc feof
1.来源
open是UNIX系统调用函数(包括linux等)返回值是文件描述符,它是文件在文件描述符表里的索引
fopen是ANSIC标准中的c语言函数库,在不同的系统中调用不同的内核api,返回的是一个指向文件结构的指针
2.移植性
fopen是c标准函数,具有良好的移植性
open是UNIX系统调用,移植性有限
3.适用范围(一整套,不能相互使用)
open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念。UNIX下的一切设备都是以文件的形式操作,如:网络套接字,硬件设备等。包括操作普通正规文件
fopen是用来操纵普通正规文件
4.文件IO层次(低级高级区别标准:谁离系统内核更近)
open属于低级IO函数
fopen属于高级IO函数
5.缓冲
fopen->缓冲文件系统(有缓冲)
open->非缓冲文件系统(无缓冲)

2.Linux进程:

(1)进程的概念:

进程:是程序的一次运行活动
通俗来说:程序跑起来了,系统中就多了一个进程

(2)查看系统中有哪些进程:

1.使用ps指令查看		例:ps -aux|grep xxx(名字)
配合grep来查看程序中是否存在某一个进程
2.使用top指令查看,类似window任务管理器

(3)进程标识符:

每个进程都有一个非负整数表示唯一的ID,叫做pid,类似身份证
pid = 0//称为交换进程  作用:进程调度
pid = 1//init进程  作用系统初始化

(4)fork与vfork的区别

fork();以当前进程作为父进程创建出一个新的子进程,并且将父进程的所有资源拷贝给子进程,这样子进程作为父进程的一个副本存在

fork函数被调用一次,但返回2次,两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的pid
fork与vfork的区别:
1.fork:子进程拷贝父进程的代码段与数据段(不共享)
vfork:子进程与父进程共享代码段和数据段
2.fork:父子进程交替运行
vfork:保证子进程先运行,子进程退出后父进程才能运行
3.vfork:保证子进程先运行,在他调用exec或exit之后父进程才可能被调度运行。如果调用这两个函数之前子进程依赖父进程的进一步动作,则会导致死锁
4.fork实现了写时拷贝,效率仍没有vfork高
但是在一般的平台上都存在问题,不建议使用

(5)wait与waitpid(子进程退出状态不被收集,变成僵死进程)

wait();等待子程序执行完,父进程才可以执行
/*例:
子进程:exit(-1);
父进程:int status;
wait(&status);
i = WEXITSTATUS(status);*/
wait使调用者阻塞,waitpid();有一个选项,可以使调用者不阻塞
//例:waitpid(pid,&status,WNOHANG);
非阻塞等待,子进程会变成僵尸进程

(6)孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”此时子进程叫做孤儿进程
Linux避免系统存在过多的孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程

(7)exec族函数、system函数、popen函数

如果执行了exec族函数 ,那么就会进入到新进程里面 ,不会继续运行之前的进程。

exec族函数:execl,execlp,execle(少),execv,execvp,execvpe(少)
exec族函数:在调用进程内部执行一个可执行文件(可以是二进制文件,也可以是任何linux下可执行的脚本文件)
/*例:int execl(const char *path, const char *arg, …):
函数作用就是在当前目录下执行path程序的arg操作
path就是在当前目录下想要执行的命令,而arg就是命令行参数列表 ,第一个参数是命令行名字,结尾必须为NULL*/
返回值:exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行
													(使用perror("why");输出错误原因)	

system函数执行完还会继续运行原程序

system函数:int system(const char *command);
直接把命令行传参进去给system就可以了,command就是命令行。

比起system函数,popen函数能够保留运行结果。

popen函数:FILE *popen(const char *command, const char *type);
command就是命令行 ,而type只有 “r”,“w” 读写俩种。
popen会把运行结果存入管道中 。

3.进程间通信:

(1)进程间通信的种类:

单机:半双工管道、全双工管道、消息队列、信号量、共享内存
多机:套接字,STREAMS(基于net)

(2)管道:

1.他是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
2.他只能用于具有亲缘关系的进程之间的通信(父子进程,兄弟进程)
3.它可以看成是一种特殊的文件,对于他的读写也可以使用普通的read,write函数,但他不是普通的文件,并不属于其他任何的文件系统,并且只存在于内存中

(3)命名管道(FIFO):

1.FIFO可以在无关的进程之间交换数据,与无名管道不同
2.FIFO有路径名与之相关,他以一种特殊设备文件形式存在于文件系统中
								(可以使用普通文件操作进行操作,一般需要设置阻塞(有输入,有输出))
使用open只读或者只写方式打开一般会阻塞(加上O_NONBLOCK则不会阻塞)
如果以读写方式打开,则一定不阻塞

(4)消息队列:

消息队列:消息队列,是消息的链接表,存在于内核
1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除
3.消息队列可以实现消息的随机查询,消息并不一定要先进先出的次序读取,也可以按照消息的类型读取

(5)ftok函数

功能:系统建立IPC通信(消息队列、信号量和共享内存)时必须指定一个ID值,通常情况下,该ID值通过ftok函数得到
key_t key;
key = ftok(".",1);
printf("key = %x\n",key);//十六进制数

(6)共享内存:
查看系统的共享内存:ipcs -m;
删除:ipcrm -m xxx(shmid)

概念:两个或者多个进程共享一个给定的存储区
1.共享内存是最快的IPC,因为进程是直接对内存进行存取
2.因为多个进程可以同时操作,所以需要同步
3.信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问

(7)信号:
kill -l:可以查看系统中所有的信号

信号的处理:忽略、捕捉、默认动作
两个信号不能忽略:SIGKILL、SIGSTOP
入门:kill、signal;
高级:sigaction、sigqueue
atoi();函数功能:用于把一个字符串转化成一个整形数
sprintf();函数功能:用于格式化的输出
//可以配合system使用 sprintf(cmd,"kill %d %d",signum,pid);
					//system(cmd);

(8)信号量:

1.信号量用于进程间同步,若要在进程之间传递数据需要结合共享内存
2.信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作
3.每次对信号量PV操作不仅限于对信号量值加一或减一,而且可以加减任意整数
4.支持信号量组
P操作:拿锁
V操作:放锁

4.linux线程:

(1)线程与进程:
进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
(2)使用线程的原因:
1.它是一种非常"节俭"的多任务操作方式:一个进程的开销大约是一个线程开销的30倍左右
2.线程间方便的通信机制:同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
注意:数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
(3)线程相关API:
在这里插入图片描述
(4)互斥锁、条件的动态和静态初始化
1.可以用宏PTHREAD_MUTEX_INITIALIZER来初始化静态定义的条件变量,使其具有缺省属性。这和用pthread_mutex_init函数动态分配的效果是一样的。初始化时不进行错误检查。如:

pthread_mutex_t cv = PTHREAD_MUTEX_INITIALIZER;

1.可以用宏PTHREAD_COND_INITIALIZER来初始化静态定义的条件变量,使其具有缺省属性。这和用pthread_cond_init函数动态分配的效果是一样的。初始化时不进行错误检查。如:

pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

5.网络编程(socket):

(1)TCP和UDP对比

1.TCP面向连接;UDP是无连接的,及发送数据之前不需要建立连接
2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3.TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低
4.每一条UDP连接只能是点到点的;UDP支持一对一,一对多,多对一,多对多的交互通信
5.TCP首部开销20字节,UDP首部开销很小,只有8字节
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

(2)套接字的建立模型:
在这里插入图片描述
(3)字节序:

概念:指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序
1.Little endian 小端字节序;//将低字节序存储在起始位置
2.Big endian 大端字节序;//将高字节存储在起始位置
网络字节序:大端字节序
X86系列CPU都是小端的
字节序转换api:
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);//返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue);//返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue);//返回主机字节序的值uint32_t
ntohl(uint32_t net32bitvalue);//返回主机字节序的值
h代表host,n代表net,s代表short(两个字节),l代表long4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转化

(4)一些常用的API:

int inet_aton(const char* straddr,struct in_addr *addrp);
//把字符串形式的"192.168.1.123"转为网络能识别的格式
char* inet_ntoa(struct in_addr inaddr);
//把网络格式的ip地址转为字符串形式
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值