linux C进程常用操作
不登高山,不知天之高也;
不临深溪,不知地之厚也。
荀子《劝学》
linux应用层主要是一个个独立任务的进程在运行,但是很多时候,在工作中我们可能很少去重新写一个进程,
大部分的工作都是分配到了一个进程内的模块或者提供进程内特定功能的接口开发,这篇文章是想简单说明下,
作为一个进程,在实际开发过程中,可能用到的一些编程方法比如:main参数解析,信号注册、回调函数、
线程创建、文件操作(FIFE *fp)、进程间通信(socket);每一种我都会附一个简单的实例。
1、main参数解析,参数较少,使用简单判断argc并取出对应的argv[i]值就就可以处理,代码如下:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = ;
printf("argc is %d\n", argc);
for(i = ; i < argc; i++)
printf("argv[%d] is %s\n", i, argv[i]);
return ;
}
参数较多时就可以调用getopt/getopt_long接口来完成工作。
函数的定义
int getopt(int argc, char * const argv[], const char *optstring);
参数说明:
argc:main()函数传递过来的参数的个数
argv:main()函数传递过来的参数的字符串指针数组
optstring:选项字符串,告知 getopt()可以处理哪个选项以及哪个选项需要参数
代码样列
#include<stdio.h>
#include<unistd.h>
#include<getopt.h>
int main(int argc, char *argv[])
{
int opt;
/*单个字符表示选项没有参数 输入格式:-A即可,不加参数
*单字符加冒号表示选项有且必须加参数 输入格式:-B xiaocang或-Bxiaobo(二选一)
*单字符加两个冒号表示选项可以有也可以无 输入格式:-Cxiaobo(必须挨着)
*/
char *string = "AB:C::";
while ((opt = getopt(argc, argv, string))!= -)
{
/* 下面是常用的两个获取选项及其值得变量optarg无需定义,全局变量
* opt '-' 后面的字符,也就是参数字符
* optarg 指向当前选项参数(如果有)的指针。
*/
printf("opt = %c\t\t", opt);
printf("optarg = %s\t\t\n", optarg);
}
return ;
}
样列输出:
./argc-opt -A -B xiaocang -Cxiaobo
opt = A optarg = (null)
opt = B optarg = xiaocang
opt = C optarg = xiaobo
2、信号注册,作为一个进程,很有必要注册一定的信号,防止进程异常退出时,自己蒙圈。
函数定义:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数说明:
signum:要捕捉的信号(查看信号:kill -l,9号SIGKILL信号不能被捕捉);
handler:我们要对信号进行的处理方式。
示例代码:
#include <stdio.h>
#include <signal.h>
void signal_handler(int signal)
{
printf("Received signal %d\n", signal);
printf("do something...\n");
return;
}
int main(int argc, char *argv[])
{
signal(SIGHUP, signal_handler);
while()
sleep();
return ;
}
示例测试:
一个窗口后台挂进程运行 ./a.out &
开另外的窗口发送信号 kill -1 pid(a.out进程号)
第一个窗口收到将会收到
Received signal 1
do something...
3、回调函数
其实在信号注册中我们就使用了回调函数,但是此回调函数不是我们自己定义的类型,自己用来调用执行,
我们这里做一个回调函数的调用示例,差别就在于回调函数的定义与调用时机,根据自己的实际需要定义就可以。
#include <stdio.h> /* 定义一个函数指针 确定入参与返回值类型 */
typedef int (* MyCallbak)(int PanJinLian, int XiMengQin);
/* 实现一个与上面定义的函数指针入参与返回值类型相同的函数 */
int ThisMyFunc(int PanJinLian, int XiMengQin)
{
printf("PanJinLian is %d\n", PanJinLian);
printf("XiMengQin is %d\n", XiMengQin);
printf("do something...\n");
return ;
}
int main(int argc, char *argv[])
{
int P_adrenaline = ;
int X_adrenaline = ;
MyCallbak CallbakPointer;/* 定义一个函数指针变量 */
CallbakPointer = ThisMyFunc;/* 将函数地址赋予定义的指针 也叫挂钩子*/
int ret = CallbakPointer(P_adrenaline, X_adrenaline);/* 调用函数,执行回调 */
printf("ret is %d\n", ret);
return ;
}
执行返回
PanJinLian is
XiMengQin is
do something...
ret is
4、线程创建
线程主要是用来阻塞接受异步消息,或者完成耗时与周期性的任务,重点需要关注的是线程结束时线程资源的回收问题,
很多人会忽略这部分,会用到 pthread_detach 或者 pthread_join(阻塞等待线程结束并回收资源); 多线程必将引入同步与
互斥问题,则对于全局变量,必须要加锁保护,数据流防止丢失我们会用到队列。
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
//Compile and link with -pthread.
参数说明
thread:指向线程标识符的指针。
attr:用来设置线程属性。
start_routine:线程运行函数的起始地址。
arg:运行函数的参数。
样例代码:
#include<stdio.h>
#include <pthread.h>
static void mythreadfun( void *arg )
{
/*这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源*/
pthread_detach(pthread_self());
printf("arg is %s\n", (char *)arg);
int i = ;
while()
{
printf("do something...\n");
if(i++ == )
break;
sleep();
}
return ;
} int main(int argc, char *argv[])
{
pthread_t pthreadid = ;
int ret = ;
char *param = "good";
/* 创建线程 */
ret = pthread_create(&pthreadid, NULL, (void *)mythreadfun, (void *)param);
if(ret != )
{
printf("create pthread failed.");
return;
}
printf("create pthread success.");
while()
sleep();
return ;
}
执行返回
./a.out
create pthread success.arg is good
do something...
do something...
do something...
do something...
5、文件操作,文件操作很普遍,如记录数据,读入数据等。
文件操作一般要注意,明确操作的fp在文件中的位置,fclose前刷新缓存,对写入或者读出的返回做判断,异常或者结束
操作时关闭fp,同样还有open read write接口。两者区别:
1、缓冲文件系统与非缓冲系统的区别
缓冲文件系统(fopen):在内存为每个文件开辟一个缓存区,当执行读操作,从磁盘文件将数据读入内存缓冲区,装满后从
内存缓冲区依次读取数据。写操作同理。
内存缓冲区的大小影响着实际操作外存的次数,缓冲区越大,操作外存的次数越少,执行速度快,效率高。缓冲区大小由机
器而定。借助文件结构体指针对文件管理,可读写字符串、格式化数据、二进制数据。
非缓冲文件系统(open):依赖操作系统功能对文件读写,不设文件结构体指针,只能读写二进制文件。
2、open属于低级IO,fopen属于高级IO
3、open返回文件描述符,属于用户态,读写需进行用户态与内核态切换。 fopen返回文件指针
4、open是系统函数,不可移植 fopen是标准C函数,可移植
5、一般用fopen打开普通文件,open打开设备文件
6、如果顺序访问文件,fopen比open快、 如果随机访问文件,open比fopen快
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
char *msg = "hello world";
char buffer[];
fp = fopen("test.txt", "w+");/* 打开文件用于读写 */
fwrite(msg, strlen(msg) + , , fp);/* 写入数据到文件 */
fseek(fp, , SEEK_SET);/* 移动到文件的开头 */
fread(buffer, strlen(msg) + , , fp); /* 读取并显示数据 */
printf("%s\n", buffer);
fsync(fileno(fp));/* 刷新缓存 */
fclose(fp);
return();
}
执行结果
hello world
cat test.txt
hello world
6、进程间通信:包括管道(pipe)、有名管道(named pipe)、信号量(semophore)、消息队列(message queue)、
信号(signal)、共享内存(shared memory)、套接字(socket),这里只说下socket,tcp服务端与客户端的简单编
码流程实现。
server端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SERVER_SOCKET_FILE "/tmp/server_socket"
int main()
{
int ret = ;
int listen_fd = -;
int size = ;
int conn_fd = -;
int max_fd = ;
fd_set reads;
struct sockaddr_un clt_addr;
socklen_t len = sizeof(clt_addr);
struct sockaddr_un srv_addr;
unsigned char buf[]; listen_fd = socket(PF_UNIX, SOCK_STREAM, );
if(listen_fd < )
{
printf("can not create listen socket\n");
return -;
} srv_addr.sun_family = AF_UNIX;
strncpy(srv_addr.sun_path, SERVER_SOCKET_FILE, sizeof(srv_addr.sun_path));
unlink(SERVER_SOCKET_FILE);
ret = bind(listen_fd,(struct sockaddr*)&srv_addr, sizeof(srv_addr));
if(ret < )
{
printf("can not bind server socket");
close(listen_fd);
return -;
} ret = listen(listen_fd, );
if(ret < )
{
printf("can not listen the client");
close(listen_fd);
return -;
}
while()
{
FD_ZERO(&reads);
FD_SET(listen_fd, &reads);
max_fd = listen_fd;
if(conn_fd > )
{
FD_SET(conn_fd, &reads);
if(conn_fd > max_fd)
{
max_fd = conn_fd;
}
}
ret = select(max_fd+, &reads, , , NULL);
if(ret <= )
{
perror("select fail\n");
return -;
}
else
{
memset(buf, , sizeof(buf));
if(FD_ISSET(listen_fd, &reads))
{
conn_fd = accept(listen_fd, (struct sockaddr*)&clt_addr, &len);
}
if(FD_ISSET(conn_fd, &reads))
{
printf("recv client msg,conn_fd:%d\n",conn_fd);
read(conn_fd, buf, sizeof(buf));
sleep();
write(conn_fd, "i am server", strlen("i am server"));
}
}
}
}
client端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SERVER_SOCKET_FILE "/tmp/server_socket"
int main()
{
int ret = ;
int retry = ;
int i = ;
int client_fd = ;
struct sockaddr_un server_addr;
char buff[] = {};
int recv_data_len = ;
struct sockaddr_un client_addr;
socklen_t sock_len = sizeof(client_addr);
if ((client_fd = socket(PF_UNIX, SOCK_STREAM, )) < )
{
printf("create sockfd failed\n");
return -;
}
printf("update socket create success.\n");
memset(&server_addr,0x00,sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SERVER_SOCKET_FILE, strlen(SERVER_SOCKET_FILE));
for(i = ; i < retry; i++)
{
ret = connect(client_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
if( != ret)
{
printf("cannot connect to the server, retry %d time.\n", i);
sleep();
continue;
}
else
{
printf("connect server success.\n");
break ;
}
}
write(client_fd, "hello", strlen("hello"));
while()
{
memset(buff, , sizeof(buff));
recv_data_len = recvfrom(client_fd, buff, sizeof(buff), , (struct sockaddr *)&client_addr, &sock_len);
if(recv_data_len <= )
{
sleep(); //sleep 100ms and receive from socket again
printf("recv_data_len is %d.\n", recv_data_len); /* 重新连接 省略*/
close(client_fd);
continue;
}
printf("recv data [%s]\n", buff);
/* do something */
sleep();
write(client_fd, "recv data form server", strlen("recv data form server"));
}
return ;
}
分别编译命名server client,先运行server 后运行client即可如下所示:

以上就是linux c进程,会涉及到的一些基本的操作,还有很多的比较重要且基本的内容没有这里并没有讲到。
接触了不少项目,总结下来大型项目中涉及多进程,多线程,难点在于弄清楚通信消息的流向、数据结构的巧妙定义。剩下
的其实就是特定任务的逻辑部分了,如果作者有很好的注释或者编码规范,那理解起来也是很快的,添加功能也是很容易的事情。
万变不离其宗。搞清楚基本的内容、基本原理,我认为在技术成长中很重要。

关注微信公众号【嵌入式C部落】,获取更多精华文章,海量编程资料,让我们一起进步,一起成长。
linux C进程常用操作的更多相关文章
- Linux Shell数组常用操作详解
Linux Shell数组常用操作详解 1数组定义: declare -a 数组名 数组名=(元素1 元素2 元素3 ) declare -a array array=( ) 数组用小括号括起,数组元 ...
- Linux的date常用操作
Linux的date常用操作 ## 在指定的日期上减1天 $ date -d "2012-04-10 -1 day " +%Y-%m-%d 2012-04-09 ## 在指定的日期 ...
- Linux运维-常用操作-培训用例
一.服务器环境 Centos 7.9 二.常用连接工具(免费) 1.Finalshell 2.MobaXterm 3.Putty + WinSCP 三.Linux 系统目录结构 /bin :是 Bi ...
- Linux下Vim常用操作
linux下Vim的常用操作 linux 首先\(ctrl+Alt+t\)打开小框框 \(./\):相当于手机上的\(home\)键 \(ls\):当前文件夹的东东 \(mkdir\) ...
- 从零开始学Linux[二]:常用操作:用户组、进程、网络、ssh
摘要:Linux基础学习:创建用户组和用户.软件包管理.磁盘管理.进程管理.前后台进程的切换.网络配置.浏览网页.远程登录ssh 第一节,主要介绍一些简单命令,这节介绍一些日常操作. 1.创建用户组和 ...
- linux下进程相关操作
一.定义和理解 狭义定义:进程是正在运行的程序的实例. 广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动. 进程的概念主要有两点: 第一,进程是一个实体.每一个进程都有它自己的 ...
- 【linux】vim常用操作及vim插件的安装使用
vim是linux下一个非常好用的文本编辑器,在linux下开发的人员要熟练掌握vim常用命令. 1. 打开在第n行 vim +143 filename.txt 2. 只读模式打开 vim -R / ...
- LINUX的一些常用操作
CentOs6.7关闭防火墙(SecureCRT连接不上) 解决方法:______________________________________一.开启SSH以root用户登录Linux,打开终端, ...
- linux shell 字符串常用操作
1.shell内置的字符串操作 表达式 含义 ${#string} $string的长度 ${string:position} string中,从位置$position开始提取字符串 ${string ...
随机推荐
- Terminal MultipleXer---终端复用器tmux基本使用
Terminal MultipleXer---终端复用器tmux 使用场景:1.scp大文件 2:编译大文件 3:多窗口对比文件 1.安装tmux [root@localhost ~]# yum in ...
- C# HttpWebRequest 后台调用接口上传大文件以及其他参数
直接上代码,包各位看客能用!!! 1.首先请求参数的封装 /// <summary> /// 上传文件 - 请求参数类 /// </summary> public class ...
- [WP8.1]RSA 使用BouncyCastle 公钥解密
写应用的时候遇到个服务器返回私钥加密过的数据 ,然后要在客户端用公钥解密的需求 ,一直没找到方法,应用搁置了一个学期,多方搜索,结论就是.net没有实现公钥解密的方法,要自己实现,于是硬着头皮开始看 ...
- Java 学习笔记之 JVM初识
JVM初识: java只是启动JVM的命令.JVM真实位置: C:\Program Files\Java\jdk1.8.0_121\jre\bin\server\jvm.dll 1. 第一行JDK版本 ...
- Spring Boot 2.X(三):使用 Spring MVC + MyBatis + Thymeleaf 开发 web 应用
前言 Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中.本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot ...
- Butter Knife
Butter Knife,专门为Android View设计的绑定注解,专业解决各种findViewById. 简介 对一个成员变量使用@BindView注解,并传入一个View ID, Butter ...
- Cannot access a disposed object in ASP.NET Core
Cannot access a disposed object in ASP.NET Core 楠木大叔 导航 常见原因 总结 对于.neter来说,在使用ASP.NET Core的过程中 ...
- TensorFlow2.0(六):Dataset
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- Python多任务之协程
前言 协程的核心点在于协程的使用,即只需要了解怎么使用协程即可:但如果你想了解协程是怎么实现的,就需要了解依次了解可迭代,迭代器,生成器了: 如果你只想看协程的使用,那么只需要看第一部分内容就行了:如 ...
- 初探内核之《Linux内核设计与实现》笔记上
内核简介 本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核 原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...